summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig3
-rw-r--r--drivers/acpi/apei/Kconfig1
-rw-r--r--drivers/acpi/apei/erst.c61
-rw-r--r--drivers/acpi/processor_perflib.c6
-rw-r--r--drivers/acpi/processor_throttling.c32
-rw-r--r--drivers/acpi/scan.c4
-rw-r--r--drivers/amba/bus.c6
-rw-r--r--drivers/ata/ahci.c8
-rw-r--r--drivers/ata/ahci.h4
-rw-r--r--drivers/ata/ata_piix.c8
-rw-r--r--drivers/ata/libahci.c39
-rw-r--r--drivers/ata/libata-core.c3
-rw-r--r--drivers/ata/libata-eh.c6
-rw-r--r--drivers/ata/pata_at91.c22
-rw-r--r--drivers/atm/fore200e.c7
-rw-r--r--drivers/base/Kconfig7
-rw-r--r--drivers/base/base.h2
-rw-r--r--drivers/base/core.c7
-rw-r--r--drivers/base/dd.c18
-rw-r--r--drivers/base/firmware_class.c5
-rw-r--r--drivers/base/memory.c29
-rw-r--r--drivers/base/platform.c179
-rw-r--r--drivers/base/power/Makefile4
-rw-r--r--drivers/base/power/clock_ops.c431
-rw-r--r--drivers/base/power/generic_ops.c39
-rw-r--r--drivers/base/power/main.c91
-rw-r--r--drivers/base/power/runtime.c29
-rw-r--r--drivers/base/power/sysfs.c4
-rw-r--r--drivers/base/power/wakeup.c3
-rw-r--r--drivers/base/sys.c202
-rw-r--r--drivers/base/syscore.c2
-rw-r--r--drivers/block/DAC960.c1
-rw-r--r--drivers/block/amiflop.c1
-rw-r--r--drivers/block/ataflop.c1
-rw-r--r--drivers/block/floppy.c1
-rw-r--r--drivers/block/paride/pcd.c1
-rw-r--r--drivers/block/paride/pd.c1
-rw-r--r--drivers/block/paride/pf.c1
-rw-r--r--drivers/block/rbd.c181
-rw-r--r--drivers/block/swim.c1
-rw-r--r--drivers/block/swim3.c1
-rw-r--r--drivers/block/ub.c1
-rw-r--r--drivers/block/xsysace.c1
-rw-r--r--drivers/cdrom/cdrom.c6
-rw-r--r--drivers/cdrom/gdrom.c1
-rw-r--r--drivers/cdrom/viocd.c1
-rw-r--r--drivers/char/Kconfig2
-rw-r--r--drivers/char/agp/generic.c19
-rw-r--r--drivers/char/bsr.c2
-rw-r--r--drivers/char/hpet.c6
-rw-r--r--drivers/char/hw_random/n2-drv.c7
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c7
-rw-r--r--drivers/char/mem.c42
-rw-r--r--drivers/char/raw.c34
-rw-r--r--drivers/char/virtio_console.c11
-rw-r--r--drivers/char/xilinx_hwicap/xilinx_hwicap.c14
-rw-r--r--drivers/clk/clkdev.c19
-rw-r--r--drivers/clocksource/Kconfig2
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/cyclone.c10
-rw-r--r--drivers/clocksource/i8253.c88
-rw-r--r--drivers/cpufreq/Kconfig23
-rw-r--r--drivers/cpufreq/Kconfig.x86255
-rw-r--r--drivers/cpufreq/Makefile26
-rw-r--r--drivers/cpufreq/acpi-cpufreq.c773
-rw-r--r--drivers/cpufreq/cpufreq-nforce2.c444
-rw-r--r--drivers/cpufreq/cpufreq.c215
-rw-r--r--drivers/cpufreq/cpufreq_performance.c5
-rw-r--r--drivers/cpufreq/cpufreq_powersave.c5
-rw-r--r--drivers/cpufreq/cpufreq_stats.c24
-rw-r--r--drivers/cpufreq/cpufreq_userspace.c13
-rw-r--r--drivers/cpufreq/e_powersaver.c367
-rw-r--r--drivers/cpufreq/elanfreq.c309
-rw-r--r--drivers/cpufreq/freq_table.c19
-rw-r--r--drivers/cpufreq/gx-suspmod.c514
-rw-r--r--drivers/cpufreq/longhaul.c1024
-rw-r--r--drivers/cpufreq/longhaul.h353
-rw-r--r--drivers/cpufreq/longrun.c324
-rw-r--r--drivers/cpufreq/mperf.c51
-rw-r--r--drivers/cpufreq/mperf.h9
-rw-r--r--drivers/cpufreq/p4-clockmod.c329
-rw-r--r--drivers/cpufreq/pcc-cpufreq.c621
-rw-r--r--drivers/cpufreq/powernow-k6.c261
-rw-r--r--drivers/cpufreq/powernow-k7.c747
-rw-r--r--drivers/cpufreq/powernow-k7.h43
-rw-r--r--drivers/cpufreq/powernow-k8.c1607
-rw-r--r--drivers/cpufreq/powernow-k8.h222
-rw-r--r--drivers/cpufreq/sc520_freq.c192
-rw-r--r--drivers/cpufreq/speedstep-centrino.c633
-rw-r--r--drivers/cpufreq/speedstep-ich.c448
-rw-r--r--drivers/cpufreq/speedstep-lib.c478
-rw-r--r--drivers/cpufreq/speedstep-lib.h49
-rw-r--r--drivers/cpufreq/speedstep-smi.c464
-rw-r--r--drivers/dma/fsldma.c2
-rw-r--r--drivers/edac/amd64_edac.c88
-rw-r--r--drivers/edac/amd64_edac.h3
-rw-r--r--drivers/edac/edac_mc_sysfs.c11
-rw-r--r--drivers/edac/ppc4xx_edac.c2
-rw-r--r--drivers/firewire/ohci.c39
-rw-r--r--drivers/firmware/Kconfig2
-rw-r--r--drivers/firmware/Makefile2
-rw-r--r--drivers/firmware/edd.c22
-rw-r--r--drivers/firmware/efivars.c21
-rw-r--r--drivers/firmware/google/Kconfig31
-rw-r--r--drivers/firmware/google/Makefile3
-rw-r--r--drivers/firmware/google/gsmi.c940
-rw-r--r--drivers/firmware/google/memconsole.c166
-rw-r--r--drivers/firmware/iscsi_ibft_find.c51
-rw-r--r--drivers/gpio/ml_ioh_gpio.c1
-rw-r--r--drivers/gpio/pca953x.c5
-rw-r--r--drivers/gpio/pch_gpio.c1
-rw-r--r--drivers/gpu/drm/Kconfig2
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c53
-rw-r--r--drivers/gpu/drm/drm_irq.c23
-rw-r--r--drivers/gpu/drm/drm_mm.c6
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c2
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c2
-rw-r--r--drivers/gpu/drm/i915/intel_display.c81
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c17
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h1
-rw-r--r--drivers/gpu/drm/i915/intel_fb.c10
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c3
-rw-r--r--drivers/gpu/drm/i915/intel_tv.c9
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.c53
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dma.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h5
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fbcon.c4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mem.c80
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_notifier.c11
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_object.c10
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_perf.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_sgdma.c8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_state.c18
-rw-r--r--drivers/gpu/drm/nouveau/nv04_dfp.c13
-rw-r--r--drivers/gpu/drm/nouveau/nv50_crtc.c3
-rw-r--r--drivers/gpu/drm/nouveau/nv50_evo.c1
-rw-r--r--drivers/gpu/drm/nouveau/nv50_graph.c2
-rw-r--r--drivers/gpu/drm/nouveau/nv50_instmem.c10
-rw-r--r--drivers/gpu/drm/nouveau/nv50_vm.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_vm.c25
-rw-r--r--drivers/gpu/drm/radeon/atom.c12
-rw-r--r--drivers/gpu/drm/radeon/atombios_crtc.c4
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c130
-rw-r--r--drivers/gpu/drm/radeon/evergreend.h6
-rw-r--r--drivers/gpu/drm/radeon/ni.c18
-rw-r--r--drivers/gpu/drm/radeon/r600.c8
-rw-r--r--drivers/gpu/drm/radeon/radeon.h12
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_atombios.c53
-rw-r--r--drivers/gpu/drm/radeon/radeon_atpx_handler.c29
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c29
-rw-r--r--drivers/gpu/drm/radeon/radeon_cursor.c6
-rw-r--r--drivers/gpu/drm/radeon/radeon_fence.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_gart.c8
-rw-r--r--drivers/gpu/drm/radeon/radeon_i2c.c10
-rw-r--r--drivers/gpu/drm/radeon/radeon_kms.c16
-rw-r--r--drivers/gpu/drm/radeon/radeon_legacy_encoders.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_pm.c11
-rw-r--r--drivers/gpu/drm/radeon/radeon_ring.c2
-rw-r--r--drivers/gpu/drm/radeon/reg_srcs/cayman1
-rw-r--r--drivers/gpu/drm/radeon/reg_srcs/evergreen1
-rw-r--r--drivers/gpu/drm/radeon/reg_srcs/r6001
-rw-r--r--drivers/gpu/drm/radeon/rs600.c2
-rw-r--r--drivers/gpu/drm/radeon/rv770.c6
-rw-r--r--drivers/gpu/drm/ttm/ttm_page_alloc.c26
-rw-r--r--drivers/gpu/stub/Kconfig1
-rw-r--r--drivers/gpu/vga/vga_switcheroo.c6
-rw-r--r--drivers/hwmon/Kconfig11
-rw-r--r--drivers/hwmon/lm85.c6
-rw-r--r--drivers/hwmon/lm90.c22
-rw-r--r--drivers/hwmon/pmbus_core.c1
-rw-r--r--drivers/hwmon/twl4030-madc-hwmon.c3
-rw-r--r--drivers/i2c/algos/i2c-algo-bit.c22
-rw-r--r--drivers/i2c/busses/i2c-i801.c5
-rw-r--r--drivers/i2c/busses/i2c-mpc.c9
-rw-r--r--drivers/i2c/busses/i2c-parport.c27
-rw-r--r--drivers/i2c/busses/i2c-pnx.c2
-rw-r--r--drivers/i2c/i2c-core.c6
-rw-r--r--drivers/ide/ide-cd.c1
-rw-r--r--drivers/ide/ide-cd_ioctl.c6
-rw-r--r--drivers/ide/ide-gd.c7
-rw-r--r--drivers/infiniband/core/cma.c207
-rw-r--r--drivers/infiniband/core/iwcm.c2
-rw-r--r--drivers/infiniband/core/ucma.c7
-rw-r--r--drivers/infiniband/hw/cxgb4/cm.c46
-rw-r--r--drivers/infiniband/hw/cxgb4/device.c115
-rw-r--r--drivers/infiniband/hw/cxgb4/iw_cxgb4.h36
-rw-r--r--drivers/infiniband/hw/cxgb4/provider.c2
-rw-r--r--drivers/infiniband/hw/cxgb4/qp.c3
-rw-r--r--drivers/infiniband/hw/cxgb4/t4.h5
-rw-r--r--drivers/infiniband/hw/ipath/ipath_driver.c9
-rw-r--r--drivers/infiniband/hw/nes/nes_cm.c16
-rw-r--r--drivers/infiniband/hw/nes/nes_verbs.c2
-rw-r--r--drivers/infiniband/hw/qib/qib_iba6120.c2
-rw-r--r--drivers/infiniband/hw/qib/qib_iba7220.c2
-rw-r--r--drivers/infiniband/hw/qib/qib_iba7322.c5
-rw-r--r--drivers/infiniband/hw/qib/qib_pcie.c5
-rw-r--r--drivers/input/evdev.c33
-rw-r--r--drivers/input/input.c40
-rw-r--r--drivers/input/keyboard/atakbd.c5
-rw-r--r--drivers/input/keyboard/twl4030_keypad.c6
-rw-r--r--drivers/input/misc/xen-kbdfront.c13
-rw-r--r--drivers/input/mouse/atarimouse.c15
-rw-r--r--drivers/input/touchscreen/ads7846.c13
-rw-r--r--drivers/input/touchscreen/h3600_ts_input.c17
-rw-r--r--drivers/input/touchscreen/wm831x-ts.c75
-rw-r--r--drivers/leds/leds-lm3530.c1
-rw-r--r--drivers/leds/leds-regulator.c4
-rw-r--r--drivers/lguest/Kconfig6
-rw-r--r--drivers/lguest/Makefile2
-rw-r--r--drivers/macintosh/via-pmu.c56
-rw-r--r--drivers/md/dm-raid.c8
-rw-r--r--drivers/md/md.c88
-rw-r--r--drivers/md/md.h26
-rw-r--r--drivers/md/raid1.c29
-rw-r--r--drivers/md/raid10.c27
-rw-r--r--drivers/md/raid5.c66
-rw-r--r--drivers/md/raid5.h2
-rw-r--r--drivers/media/common/tuners/tda18271-common.c11
-rw-r--r--drivers/media/common/tuners/tda18271-fe.c21
-rw-r--r--drivers/media/common/tuners/tda18271-maps.c12
-rw-r--r--drivers/media/dvb/b2c2/flexcop-pci.c2
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig6
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_devices.c6
-rw-r--r--drivers/media/dvb/ngene/ngene-core.c1
-rw-r--r--drivers/media/media-entity.c8
-rw-r--r--drivers/media/radio/radio-sf16fmr2.c2
-rw-r--r--drivers/media/radio/saa7706h.c2
-rw-r--r--drivers/media/radio/tef6862.c2
-rw-r--r--drivers/media/rc/imon.c31
-rw-r--r--drivers/media/rc/ite-cir.c1
-rw-r--r--drivers/media/rc/mceusb.c2
-rw-r--r--drivers/media/rc/rc-main.c4
-rw-r--r--drivers/media/video/Kconfig2
-rw-r--r--drivers/media/video/cx18/cx18-streams.c10
-rw-r--r--drivers/media/video/cx23885/Kconfig1
-rw-r--r--drivers/media/video/cx88/cx88-input.c2
-rw-r--r--drivers/media/video/imx074.c2
-rw-r--r--drivers/media/video/m52790.c2
-rw-r--r--drivers/media/video/omap3isp/isp.c34
-rw-r--r--drivers/media/video/omap3isp/isp.h12
-rw-r--r--drivers/media/video/omap3isp/ispccdc.c37
-rw-r--r--drivers/media/video/omap3isp/isppreview.c2
-rw-r--r--drivers/media/video/omap3isp/ispqueue.c6
-rw-r--r--drivers/media/video/omap3isp/ispresizer.c75
-rw-r--r--drivers/media/video/omap3isp/ispstat.h6
-rw-r--r--drivers/media/video/omap3isp/ispvideo.c108
-rw-r--r--drivers/media/video/omap3isp/ispvideo.h3
-rw-r--r--drivers/media/video/s5p-fimc/fimc-capture.c8
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.c74
-rw-r--r--drivers/media/video/sh_mobile_ceu_camera.c10
-rw-r--r--drivers/media/video/sh_mobile_csi2.c11
-rw-r--r--drivers/media/video/soc_camera.c55
-rw-r--r--drivers/media/video/tda9840.c2
-rw-r--r--drivers/media/video/tea6415c.c2
-rw-r--r--drivers/media/video/tea6420.c2
-rw-r--r--drivers/media/video/upd64031a.c2
-rw-r--r--drivers/media/video/upd64083.c2
-rw-r--r--drivers/media/video/v4l2-dev.c15
-rw-r--r--drivers/media/video/v4l2-device.c5
-rw-r--r--drivers/media/video/v4l2-subdev.c14
-rw-r--r--drivers/media/video/videobuf-dma-contig.c2
-rw-r--r--drivers/media/video/videobuf2-core.c17
-rw-r--r--drivers/media/video/videobuf2-dma-contig.c2
-rw-r--r--drivers/message/fusion/mptbase.h4
-rw-r--r--drivers/message/fusion/mptsas.c4
-rw-r--r--drivers/message/fusion/mptscsih.c13
-rw-r--r--drivers/message/fusion/mptspi.c22
-rw-r--r--drivers/message/i2o/i2o_block.c1
-rw-r--r--drivers/message/i2o/i2o_scsi.c4
-rw-r--r--drivers/mfd/asic3.c2
-rw-r--r--drivers/mfd/mfd-core.c16
-rw-r--r--drivers/mfd/omap-usb-host.c17
-rw-r--r--drivers/mfd/twl4030-power.c3
-rw-r--r--drivers/misc/Kconfig1
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/carma/Kconfig17
-rw-r--r--drivers/misc/carma/Makefile2
-rw-r--r--drivers/misc/carma/carma-fpga-program.c1141
-rw-r--r--drivers/misc/carma/carma-fpga.c1433
-rw-r--r--drivers/misc/sgi-gru/grufault.c1
-rw-r--r--drivers/misc/sgi-gru/grufile.c8
-rw-r--r--drivers/misc/sgi-gru/grumain.c1
-rw-r--r--drivers/misc/ti-st/Kconfig2
-rw-r--r--drivers/misc/ti-st/st_core.c23
-rw-r--r--drivers/misc/ti-st/st_kim.c1
-rw-r--r--drivers/mmc/core/bus.c1
-rw-r--r--drivers/mmc/host/omap.c2
-rw-r--r--drivers/mmc/host/sdhci-of-core.c7
-rw-r--r--drivers/mmc/host/sdhci-pci.c1
-rw-r--r--drivers/mmc/host/sdhci.c9
-rw-r--r--drivers/mmc/host/tmio_mmc_pio.c10
-rw-r--r--drivers/mtd/maps/Kconfig7
-rw-r--r--drivers/mtd/maps/Makefile1
-rw-r--r--drivers/mtd/maps/lantiq-flash.c251
-rw-r--r--drivers/mtd/maps/physmap_of.c7
-rw-r--r--drivers/mtd/nand/au1550nd.c3
-rw-r--r--drivers/mtd/nand/diskonchip.c2
-rw-r--r--drivers/net/Kconfig7
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/acenic.c1
-rw-r--r--drivers/net/arm/etherh.c4
-rw-r--r--drivers/net/atarilance.c2
-rw-r--r--drivers/net/can/mscan/mpc5xxx_can.c7
-rw-r--r--drivers/net/ehea/ehea_main.c1
-rw-r--r--drivers/net/fs_enet/fs_enet-main.c9
-rw-r--r--drivers/net/fs_enet/mii-fec.c7
-rw-r--r--drivers/net/ixgbe/ixgbe_main.c7
-rw-r--r--drivers/net/lantiq_etop.c805
-rw-r--r--drivers/net/macvlan.c10
-rw-r--r--drivers/net/sunhme.c7
-rw-r--r--drivers/of/irq.c2
-rw-r--r--drivers/parport/parport_pc.c8
-rw-r--r--drivers/pci/Kconfig4
-rw-r--r--drivers/pci/Makefile4
-rw-r--r--drivers/pci/intel-iommu.c56
-rw-r--r--drivers/pci/iov.c1
-rw-r--r--drivers/pci/pci-driver.c6
-rw-r--r--drivers/pci/pci.h37
-rw-r--r--drivers/pci/setup-bus.c4
-rw-r--r--drivers/pcmcia/pcmcia_resource.c2
-rw-r--r--drivers/pcmcia/pxa2xx_balloon3.c5
-rw-r--r--drivers/pcmcia/pxa2xx_trizeps4.c15
-rw-r--r--drivers/platform/x86/Kconfig3
-rw-r--r--drivers/platform/x86/acer-wmi.c2
-rw-r--r--drivers/platform/x86/asus-wmi.c4
-rw-r--r--drivers/platform/x86/eeepc-laptop.c57
-rw-r--r--drivers/platform/x86/eeepc-wmi.c2
-rw-r--r--drivers/platform/x86/intel_pmic_gpio.c43
-rw-r--r--drivers/platform/x86/samsung-laptop.c17
-rw-r--r--drivers/platform/x86/sony-laptop.c195
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c9
-rw-r--r--drivers/rapidio/rio.c5
-rw-r--r--drivers/rapidio/switches/idt_gen2.c10
-rw-r--r--drivers/rapidio/switches/idtcps.c6
-rw-r--r--drivers/rapidio/switches/tsi57x.c6
-rw-r--r--drivers/rtc/Kconfig7
-rw-r--r--drivers/rtc/class.c25
-rw-r--r--drivers/rtc/interface.c26
-rw-r--r--drivers/rtc/rtc-bfin.c2
-rw-r--r--drivers/rtc/rtc-coh901331.c4
-rw-r--r--drivers/rtc/rtc-davinci.c5
-rw-r--r--drivers/rtc/rtc-ds1286.c2
-rw-r--r--drivers/rtc/rtc-ep93xx.c5
-rw-r--r--drivers/rtc/rtc-m41t80.c5
-rw-r--r--drivers/rtc/rtc-max8925.c8
-rw-r--r--drivers/rtc/rtc-max8998.c5
-rw-r--r--drivers/rtc/rtc-mc13xxx.c9
-rw-r--r--drivers/rtc/rtc-msm6242.c3
-rw-r--r--drivers/rtc/rtc-mxc.c19
-rw-r--r--drivers/rtc/rtc-omap.c2
-rw-r--r--drivers/rtc/rtc-pcap.c4
-rw-r--r--drivers/rtc/rtc-rp5c01.c5
-rw-r--r--drivers/rtc/rtc-s3c.c15
-rw-r--r--drivers/s390/block/dasd.c51
-rw-r--r--drivers/s390/block/dasd_devmap.c30
-rw-r--r--drivers/s390/block/dasd_diag.c2
-rw-r--r--drivers/s390/block/dasd_eckd.c5
-rw-r--r--drivers/s390/block/dasd_genhd.c2
-rw-r--r--drivers/s390/block/dasd_int.h3
-rw-r--r--drivers/s390/block/dasd_ioctl.c128
-rw-r--r--drivers/s390/char/sclp_cmd.c2
-rw-r--r--drivers/s390/char/tape_block.c1
-rw-r--r--drivers/s390/cio/qdio_main.c17
-rw-r--r--drivers/s390/kvm/kvm_virtio.c2
-rw-r--r--drivers/scsi/arcmsr/arcmsr_hba.c138
-rw-r--r--drivers/scsi/be2iscsi/be.h10
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.c11
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.h10
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.c13
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.h13
-rw-r--r--drivers/scsi/be2iscsi/be_main.c51
-rw-r--r--drivers/scsi/be2iscsi/be_main.h15
-rw-r--r--drivers/scsi/be2iscsi/be_mgmt.c17
-rw-r--r--drivers/scsi/be2iscsi/be_mgmt.h13
-rw-r--r--drivers/scsi/bfa/bfad.c61
-rw-r--r--drivers/scsi/bfa/bfad_debugfs.c33
-rw-r--r--drivers/scsi/bfa/bfad_im.h25
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc.h2
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_fcoe.c35
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_hwi.c3
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_io.c6
-rw-r--r--drivers/scsi/constants.c1
-rw-r--r--drivers/scsi/dc395x.c193
-rw-r--r--drivers/scsi/device_handler/scsi_dh.c9
-rw-r--r--drivers/scsi/device_handler/scsi_dh_alua.c2
-rw-r--r--drivers/scsi/device_handler/scsi_dh_rdac.c72
-rw-r--r--drivers/scsi/dpt_i2o.c6
-rw-r--r--drivers/scsi/eata.c66
-rw-r--r--drivers/scsi/eata_pio.c19
-rw-r--r--drivers/scsi/esp_scsi.c6
-rw-r--r--drivers/scsi/fcoe/fcoe.c202
-rw-r--r--drivers/scsi/fcoe/fcoe_ctlr.c6
-rw-r--r--drivers/scsi/fcoe/fcoe_transport.c56
-rw-r--r--drivers/scsi/hpsa.c496
-rw-r--r--drivers/scsi/hpsa.h15
-rw-r--r--drivers/scsi/hpsa_cmd.h11
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.c3
-rw-r--r--drivers/scsi/in2000.c29
-rw-r--r--drivers/scsi/ipr.c158
-rw-r--r--drivers/scsi/ipr.h22
-rw-r--r--drivers/scsi/libfc/fc_fcp.c57
-rw-r--r--drivers/scsi/libfc/fc_lport.c1
-rw-r--r--drivers/scsi/lpfc/lpfc.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_bsg.c59
-rw-r--r--drivers/scsi/lpfc/lpfc_bsg.h130
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.c931
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.h48
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c44
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c6
-rw-r--r--drivers/scsi/lpfc/lpfc_hw4.h18
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c19
-rw-r--r--drivers/scsi/lpfc/lpfc_mbox.c2
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c14
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c32
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h2
-rw-r--r--drivers/scsi/megaraid.c20
-rw-r--r--drivers/scsi/megaraid/megaraid_mbox.c37
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_base.c13
-rw-r--r--drivers/scsi/mesh.c3
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.c42
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.h49
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_ctl.c28
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_ctl.h1
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_scsih.c617
-rw-r--r--drivers/scsi/mvsas/Kconfig1
-rw-r--r--drivers/scsi/mvsas/Makefile1
-rw-r--r--drivers/scsi/mvsas/mv_64xx.c1
-rw-r--r--drivers/scsi/mvsas/mv_64xx.h1
-rw-r--r--drivers/scsi/mvsas/mv_94xx.c1
-rw-r--r--drivers/scsi/mvsas/mv_94xx.h1
-rw-r--r--drivers/scsi/mvsas/mv_chips.h1
-rw-r--r--drivers/scsi/mvsas/mv_defs.h3
-rw-r--r--drivers/scsi/mvsas/mv_init.c67
-rw-r--r--drivers/scsi/mvsas/mv_sas.c383
-rw-r--r--drivers/scsi/mvsas/mv_sas.h8
-rw-r--r--drivers/scsi/ncr53c8xx.c2
-rw-r--r--drivers/scsi/pmcraid.c3
-rw-r--r--drivers/scsi/qla1280.c2
-rw-r--r--drivers/scsi/qla2xxx/qla_attr.c23
-rw-r--r--drivers/scsi/qla2xxx/qla_bsg.c2
-rw-r--r--drivers/scsi/qla2xxx/qla_bsg.h2
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.c2
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.h2
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h10
-rw-r--r--drivers/scsi/qla2xxx/qla_dfs.c2
-rw-r--r--drivers/scsi/qla2xxx/qla_fw.h5
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h18
-rw-r--r--drivers/scsi/qla2xxx/qla_gs.c2
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c54
-rw-r--r--drivers/scsi/qla2xxx/qla_inline.h21
-rw-r--r--drivers/scsi/qla2xxx/qla_iocb.c2
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c43
-rw-r--r--drivers/scsi/qla2xxx/qla_mbx.c151
-rw-r--r--drivers/scsi/qla2xxx/qla_mid.c6
-rw-r--r--drivers/scsi/qla2xxx/qla_nx.c97
-rw-r--r--drivers/scsi/qla2xxx/qla_nx.h2
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c85
-rw-r--r--drivers/scsi/qla2xxx/qla_settings.h2
-rw-r--r--drivers/scsi/qla2xxx/qla_sup.c2
-rw-r--r--drivers/scsi/qla2xxx/qla_version.h6
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c5
-rw-r--r--drivers/scsi/qlogicpti.c7
-rw-r--r--drivers/scsi/scsi_error.c6
-rw-r--r--drivers/scsi/scsi_lib.c34
-rw-r--r--drivers/scsi/scsi_proc.c58
-rw-r--r--drivers/scsi/scsi_scan.c2
-rw-r--r--drivers/scsi/scsi_sysfs.c16
-rw-r--r--drivers/scsi/scsi_tgt_lib.c6
-rw-r--r--drivers/scsi/scsi_transport_fc.c30
-rw-r--r--drivers/scsi/tmscsim.c22
-rw-r--r--drivers/scsi/u14-34f.c61
-rw-r--r--drivers/scsi/wd33c93.c45
-rw-r--r--drivers/spi/amba-pl022.c2
-rw-r--r--drivers/spi/dw_spi.c2
-rw-r--r--drivers/spi/pxa2xx_spi.c2
-rw-r--r--drivers/spi/spi_bfin5xx.c2
-rw-r--r--drivers/ssb/pci.c16
-rw-r--r--drivers/ssb/sprom.c43
-rw-r--r--drivers/ssb/ssb_private.h3
-rw-r--r--drivers/staging/Kconfig2
-rw-r--r--drivers/staging/Makefile1
-rw-r--r--drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c4
-rw-r--r--drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c3
-rw-r--r--drivers/staging/gma500/Kconfig2
-rw-r--r--drivers/staging/intel_sst/intelmid_v1_control.c1
-rw-r--r--drivers/staging/intel_sst/intelmid_v2_control.c1
-rw-r--r--drivers/staging/olpc_dcon/olpc_dcon_xo_1.c1
-rw-r--r--drivers/staging/pohmelfs/inode.c1
-rw-r--r--drivers/staging/rt2860/common/cmm_data_pci.c2
-rw-r--r--drivers/staging/rt2860/common/cmm_data_usb.c2
-rw-r--r--drivers/staging/rts_pstor/debug.h2
-rw-r--r--drivers/staging/rts_pstor/ms.c1
-rw-r--r--drivers/staging/rts_pstor/rtsx_chip.c5
-rw-r--r--drivers/staging/rts_pstor/rtsx_scsi.c1
-rw-r--r--drivers/staging/rts_pstor/sd.c4
-rw-r--r--drivers/staging/rts_pstor/trace.h2
-rw-r--r--drivers/staging/rts_pstor/xd.c1
-rw-r--r--drivers/staging/samsung-laptop/Kconfig10
-rw-r--r--drivers/staging/samsung-laptop/Makefile1
-rw-r--r--drivers/staging/samsung-laptop/TODO5
-rw-r--r--drivers/staging/samsung-laptop/samsung-laptop.c843
-rw-r--r--drivers/staging/solo6x10/Kconfig1
-rw-r--r--drivers/staging/spectra/ffsport.c2
-rw-r--r--drivers/staging/tidspbridge/dynload/cload.c2
-rw-r--r--drivers/staging/tty/specialix.c2
-rw-r--r--drivers/staging/usbip/vhci_hcd.c11
-rw-r--r--drivers/staging/usbip/vhci_sysfs.c7
-rw-r--r--drivers/staging/wlan-ng/cfg80211.c2
-rw-r--r--drivers/target/Kconfig1
-rw-r--r--drivers/target/Makefile2
-rw-r--r--drivers/target/tcm_fc/Kconfig5
-rw-r--r--drivers/target/tcm_fc/Makefile15
-rw-r--r--drivers/target/tcm_fc/tcm_fc.h215
-rw-r--r--drivers/target/tcm_fc/tfc_cmd.c696
-rw-r--r--drivers/target/tcm_fc/tfc_conf.c677
-rw-r--r--drivers/target/tcm_fc/tfc_io.c374
-rw-r--r--drivers/target/tcm_fc/tfc_sess.c541
-rw-r--r--drivers/tty/n_gsm.c8
-rw-r--r--drivers/tty/serial/Kconfig8
-rw-r--r--drivers/tty/serial/Makefile1
-rw-r--r--drivers/tty/serial/imx.c3
-rw-r--r--drivers/tty/serial/lantiq.c756
-rw-r--r--drivers/tty/serial/of_serial.c7
-rw-r--r--drivers/uio/uio.c16
-rw-r--r--drivers/uio/uio_netx.c19
-rw-r--r--drivers/uio/uio_pdrv_genirq.c4
-rw-r--r--drivers/usb/Kconfig1
-rw-r--r--drivers/usb/core/devices.c10
-rw-r--r--drivers/usb/core/hcd.c2
-rw-r--r--drivers/usb/core/hub.c12
-rw-r--r--drivers/usb/gadget/f_audio.c1
-rw-r--r--drivers/usb/gadget/f_eem.c8
-rw-r--r--drivers/usb/gadget/fsl_qe_udc.c27
-rw-r--r--drivers/usb/gadget/goku_udc.c1
-rw-r--r--drivers/usb/gadget/imx_udc.c1
-rw-r--r--drivers/usb/gadget/inode.c4
-rw-r--r--drivers/usb/gadget/omap_udc.c1
-rw-r--r--drivers/usb/gadget/pch_udc.c8
-rw-r--r--drivers/usb/gadget/pxa25x_udc.c1
-rw-r--r--drivers/usb/gadget/pxa27x_udc.c1
-rw-r--r--drivers/usb/gadget/r8a66597-udc.c2
-rw-r--r--drivers/usb/host/ehci-omap.c20
-rw-r--r--drivers/usb/host/ehci-q.c15
-rw-r--r--drivers/usb/host/isp1362-hcd.c1
-rw-r--r--drivers/usb/host/isp1760-hcd.c3
-rw-r--r--drivers/usb/host/ohci-au1xxx.c2
-rw-r--r--drivers/usb/host/pci-quirks.c117
-rw-r--r--drivers/usb/host/sl811-hcd.c1
-rw-r--r--drivers/usb/host/xhci-hub.c19
-rw-r--r--drivers/usb/host/xhci-mem.c106
-rw-r--r--drivers/usb/host/xhci-pci.c4
-rw-r--r--drivers/usb/host/xhci-ring.c219
-rw-r--r--drivers/usb/host/xhci.c23
-rw-r--r--drivers/usb/host/xhci.h11
-rw-r--r--drivers/usb/musb/Kconfig6
-rw-r--r--drivers/usb/musb/blackfin.c24
-rw-r--r--drivers/usb/musb/cppi_dma.c27
-rw-r--r--drivers/usb/musb/musb_core.c2
-rw-r--r--drivers/usb/musb/musb_core.h5
-rw-r--r--drivers/usb/musb/musb_gadget.c10
-rw-r--r--drivers/usb/musb/musbhsdma.c8
-rw-r--r--drivers/usb/musb/omap2430.c5
-rw-r--r--drivers/usb/musb/ux500.c2
-rw-r--r--drivers/usb/serial/ftdi_sio.c5
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h12
-rw-r--r--drivers/usb/serial/option.c5
-rw-r--r--drivers/usb/serial/qcserial.c31
-rw-r--r--drivers/usb/storage/isd200.c1
-rw-r--r--drivers/vhost/vhost.c2
-rw-r--r--drivers/video/acornfb.c26
-rw-r--r--drivers/video/atafb.c2
-rw-r--r--drivers/video/fbmem.c223
-rw-r--r--drivers/video/pxafb.c4
-rw-r--r--drivers/video/udlfb.c1
-rw-r--r--drivers/virtio/virtio_pci.c15
-rw-r--r--drivers/virtio/virtio_ring.c1
-rw-r--r--drivers/watchdog/Kconfig6
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/iTCO_wdt.c97
-rw-r--r--drivers/watchdog/lantiq_wdt.c261
-rw-r--r--drivers/watchdog/mpc8xxx_wdt.c7
-rw-r--r--drivers/watchdog/mtx-1_wdt.c21
-rw-r--r--drivers/xen/Makefile24
-rw-r--r--drivers/xen/balloon.c25
-rw-r--r--drivers/xen/events.c158
-rw-r--r--drivers/xen/gntalloc.c14
-rw-r--r--drivers/xen/gntdev.c16
-rw-r--r--drivers/xen/grant-table.c31
-rw-r--r--drivers/xen/manage.c13
-rw-r--r--drivers/xen/sys-hypervisor.c2
591 files changed, 27483 insertions, 5251 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index aca70675146..61631edfecc 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -121,4 +121,7 @@ source "drivers/platform/Kconfig"
source "drivers/clk/Kconfig"
source "drivers/hwspinlock/Kconfig"
+
+source "drivers/clocksource/Kconfig"
+
endmenu
diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig
index 66a03caa2ad..f739a70b1c7 100644
--- a/drivers/acpi/apei/Kconfig
+++ b/drivers/acpi/apei/Kconfig
@@ -1,5 +1,6 @@
config ACPI_APEI
bool "ACPI Platform Error Interface (APEI)"
+ select MISC_FILESYSTEMS
select PSTORE
depends on X86
help
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
index d6cb0ff6988..e6cef8e1b53 100644
--- a/drivers/acpi/apei/erst.c
+++ b/drivers/acpi/apei/erst.c
@@ -929,13 +929,17 @@ static int erst_check_table(struct acpi_table_erst *erst_tab)
return 0;
}
-static size_t erst_reader(u64 *id, enum pstore_type_id *type,
+static int erst_open_pstore(struct pstore_info *psi);
+static int erst_close_pstore(struct pstore_info *psi);
+static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
struct timespec *time);
static u64 erst_writer(enum pstore_type_id type, size_t size);
static struct pstore_info erst_info = {
.owner = THIS_MODULE,
.name = "erst",
+ .open = erst_open_pstore,
+ .close = erst_close_pstore,
.read = erst_reader,
.write = erst_writer,
.erase = erst_clear
@@ -957,12 +961,32 @@ struct cper_pstore_record {
char data[];
} __packed;
-static size_t erst_reader(u64 *id, enum pstore_type_id *type,
+static int reader_pos;
+
+static int erst_open_pstore(struct pstore_info *psi)
+{
+ int rc;
+
+ if (erst_disable)
+ return -ENODEV;
+
+ rc = erst_get_record_id_begin(&reader_pos);
+
+ return rc;
+}
+
+static int erst_close_pstore(struct pstore_info *psi)
+{
+ erst_get_record_id_end();
+
+ return 0;
+}
+
+static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
struct timespec *time)
{
int rc;
- ssize_t len;
- unsigned long flags;
+ ssize_t len = 0;
u64 record_id;
struct cper_pstore_record *rcd = (struct cper_pstore_record *)
(erst_info.buf - sizeof(*rcd));
@@ -970,24 +994,28 @@ static size_t erst_reader(u64 *id, enum pstore_type_id *type,
if (erst_disable)
return -ENODEV;
- raw_spin_lock_irqsave(&erst_lock, flags);
skip:
- rc = __erst_get_next_record_id(&record_id);
- if (rc) {
- raw_spin_unlock_irqrestore(&erst_lock, flags);
- return rc;
- }
+ rc = erst_get_record_id_next(&reader_pos, &record_id);
+ if (rc)
+ goto out;
+
/* no more record */
if (record_id == APEI_ERST_INVALID_RECORD_ID) {
- raw_spin_unlock_irqrestore(&erst_lock, flags);
- return 0;
+ rc = -1;
+ goto out;
}
- len = __erst_read(record_id, &rcd->hdr, sizeof(*rcd) +
- erst_erange.size);
+ len = erst_read(record_id, &rcd->hdr, sizeof(*rcd) +
+ erst_info.bufsize);
+ /* The record may be cleared by others, try read next record */
+ if (len == -ENOENT)
+ goto skip;
+ else if (len < 0) {
+ rc = -1;
+ goto out;
+ }
if (uuid_le_cmp(rcd->hdr.creator_id, CPER_CREATOR_PSTORE) != 0)
goto skip;
- raw_spin_unlock_irqrestore(&erst_lock, flags);
*id = record_id;
if (uuid_le_cmp(rcd->sec_hdr.section_type,
@@ -1005,7 +1033,8 @@ skip:
time->tv_sec = 0;
time->tv_nsec = 0;
- return len - sizeof(*rcd);
+out:
+ return (rc < 0) ? rc : (len - sizeof(*rcd));
}
static u64 erst_writer(enum pstore_type_id type, size_t size)
diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c
index 3a73a93596e..85b32376dad 100644
--- a/drivers/acpi/processor_perflib.c
+++ b/drivers/acpi/processor_perflib.c
@@ -49,10 +49,6 @@ ACPI_MODULE_NAME("processor_perflib");
static DEFINE_MUTEX(performance_mutex);
-/* Use cpufreq debug layer for _PPC changes. */
-#define cpufreq_printk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, \
- "cpufreq-core", msg)
-
/*
* _PPC support is implemented as a CPUfreq policy notifier:
* This means each time a CPUfreq driver registered also with
@@ -145,7 +141,7 @@ static int acpi_processor_get_platform_limit(struct acpi_processor *pr)
return -ENODEV;
}
- cpufreq_printk("CPU %d: _PPC is %d - frequency %s limited\n", pr->id,
+ pr_debug("CPU %d: _PPC is %d - frequency %s limited\n", pr->id,
(int)ppc, ppc ? "" : "not");
pr->performance_platform_limit = (int)ppc;
diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c
index ad350173956..605a2954ef1 100644
--- a/drivers/acpi/processor_throttling.c
+++ b/drivers/acpi/processor_throttling.c
@@ -710,20 +710,14 @@ static int acpi_processor_get_throttling_fadt(struct acpi_processor *pr)
}
#ifdef CONFIG_X86
-static int acpi_throttling_rdmsr(struct acpi_processor *pr,
- u64 *value)
+static int acpi_throttling_rdmsr(u64 *value)
{
- struct cpuinfo_x86 *c;
u64 msr_high, msr_low;
- unsigned int cpu;
u64 msr = 0;
int ret = -1;
- cpu = pr->id;
- c = &cpu_data(cpu);
-
- if ((c->x86_vendor != X86_VENDOR_INTEL) ||
- !cpu_has(c, X86_FEATURE_ACPI)) {
+ if ((this_cpu_read(cpu_info.x86_vendor) != X86_VENDOR_INTEL) ||
+ !this_cpu_has(X86_FEATURE_ACPI)) {
printk(KERN_ERR PREFIX
"HARDWARE addr space,NOT supported yet\n");
} else {
@@ -738,18 +732,13 @@ static int acpi_throttling_rdmsr(struct acpi_processor *pr,
return ret;
}
-static int acpi_throttling_wrmsr(struct acpi_processor *pr, u64 value)
+static int acpi_throttling_wrmsr(u64 value)
{
- struct cpuinfo_x86 *c;
- unsigned int cpu;
int ret = -1;
u64 msr;
- cpu = pr->id;
- c = &cpu_data(cpu);
-
- if ((c->x86_vendor != X86_VENDOR_INTEL) ||
- !cpu_has(c, X86_FEATURE_ACPI)) {
+ if ((this_cpu_read(cpu_info.x86_vendor) != X86_VENDOR_INTEL) ||
+ !this_cpu_has(X86_FEATURE_ACPI)) {
printk(KERN_ERR PREFIX
"HARDWARE addr space,NOT supported yet\n");
} else {
@@ -761,15 +750,14 @@ static int acpi_throttling_wrmsr(struct acpi_processor *pr, u64 value)
return ret;
}
#else
-static int acpi_throttling_rdmsr(struct acpi_processor *pr,
- u64 *value)
+static int acpi_throttling_rdmsr(u64 *value)
{
printk(KERN_ERR PREFIX
"HARDWARE addr space,NOT supported yet\n");
return -1;
}
-static int acpi_throttling_wrmsr(struct acpi_processor *pr, u64 value)
+static int acpi_throttling_wrmsr(u64 value)
{
printk(KERN_ERR PREFIX
"HARDWARE addr space,NOT supported yet\n");
@@ -801,7 +789,7 @@ static int acpi_read_throttling_status(struct acpi_processor *pr,
ret = 0;
break;
case ACPI_ADR_SPACE_FIXED_HARDWARE:
- ret = acpi_throttling_rdmsr(pr, value);
+ ret = acpi_throttling_rdmsr(value);
break;
default:
printk(KERN_ERR PREFIX "Unknown addr space %d\n",
@@ -834,7 +822,7 @@ static int acpi_write_throttling_state(struct acpi_processor *pr,
ret = 0;
break;
case ACPI_ADR_SPACE_FIXED_HARDWARE:
- ret = acpi_throttling_wrmsr(pr, value);
+ ret = acpi_throttling_wrmsr(value);
break;
default:
printk(KERN_ERR PREFIX "Unknown addr space %d\n",
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index b136c9c1e53..449c556274c 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -943,6 +943,10 @@ static int acpi_bus_get_flags(struct acpi_device *device)
if (ACPI_SUCCESS(status))
device->flags.lockable = 1;
+ /* Power resources cannot be power manageable. */
+ if (device->device_type == ACPI_BUS_TYPE_POWER)
+ return 0;
+
/* Presence of _PS0|_PR0 indicates 'power manageable' */
status = acpi_get_handle(device->handle, "_PS0", &temp);
if (ACPI_FAILURE(status))
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index 82104050315..7025593a58c 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -214,7 +214,7 @@ static int amba_pm_resume_noirq(struct device *dev)
#endif /* !CONFIG_SUSPEND */
-#ifdef CONFIG_HIBERNATION
+#ifdef CONFIG_HIBERNATE_CALLBACKS
static int amba_pm_freeze(struct device *dev)
{
@@ -352,7 +352,7 @@ static int amba_pm_restore_noirq(struct device *dev)
return ret;
}
-#else /* !CONFIG_HIBERNATION */
+#else /* !CONFIG_HIBERNATE_CALLBACKS */
#define amba_pm_freeze NULL
#define amba_pm_thaw NULL
@@ -363,7 +363,7 @@ static int amba_pm_restore_noirq(struct device *dev)
#define amba_pm_poweroff_noirq NULL
#define amba_pm_restore_noirq NULL
-#endif /* !CONFIG_HIBERNATION */
+#endif /* !CONFIG_HIBERNATE_CALLBACKS */
#ifdef CONFIG_PM
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 39d829cd82d..71afe037131 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -150,7 +150,7 @@ static const struct ata_port_info ahci_port_info[] = {
{
AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP |
AHCI_HFLAG_YES_NCQ),
- .flags = AHCI_FLAG_COMMON,
+ .flags = AHCI_FLAG_COMMON | ATA_FLAG_NO_DIPM,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
@@ -261,6 +261,12 @@ static const struct pci_device_id ahci_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, 0x1d06), board_ahci }, /* PBG RAID */
{ PCI_VDEVICE(INTEL, 0x2826), board_ahci }, /* PBG RAID */
{ PCI_VDEVICE(INTEL, 0x2323), board_ahci }, /* DH89xxCC AHCI */
+ { PCI_VDEVICE(INTEL, 0x1e02), board_ahci }, /* Panther Point AHCI */
+ { PCI_VDEVICE(INTEL, 0x1e03), board_ahci }, /* Panther Point AHCI */
+ { PCI_VDEVICE(INTEL, 0x1e04), board_ahci }, /* Panther Point RAID */
+ { PCI_VDEVICE(INTEL, 0x1e05), board_ahci }, /* Panther Point RAID */
+ { PCI_VDEVICE(INTEL, 0x1e06), board_ahci }, /* Panther Point RAID */
+ { PCI_VDEVICE(INTEL, 0x1e07), board_ahci }, /* Panther Point RAID */
/* JMicron 360/1/3/5/6, match class to avoid IDE function */
{ PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index 39865009c25..12c5282e7fc 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -229,6 +229,10 @@ enum {
EM_CTL_ALHD = (1 << 26), /* Activity LED */
EM_CTL_XMT = (1 << 25), /* Transmit Only */
EM_CTL_SMB = (1 << 24), /* Single Message Buffer */
+ EM_CTL_SGPIO = (1 << 19), /* SGPIO messages supported */
+ EM_CTL_SES = (1 << 18), /* SES-2 messages supported */
+ EM_CTL_SAFTE = (1 << 17), /* SAF-TE messages supported */
+ EM_CTL_LED = (1 << 16), /* LED messages supported */
/* em message type */
EM_MSG_TYPE_LED = (1 << 0), /* LED */
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
index 0bc3fd6c3fd..6f6e7718b05 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -309,6 +309,14 @@ static const struct pci_device_id piix_pci_tbl[] = {
{ 0x8086, 0x1d00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
/* SATA Controller IDE (PBG) */
{ 0x8086, 0x1d08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
+ /* SATA Controller IDE (Panther Point) */
+ { 0x8086, 0x1e00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
+ /* SATA Controller IDE (Panther Point) */
+ { 0x8086, 0x1e01, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata },
+ /* SATA Controller IDE (Panther Point) */
+ { 0x8086, 0x1e08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
+ /* SATA Controller IDE (Panther Point) */
+ { 0x8086, 0x1e09, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
{ } /* terminate list */
};
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 26d452339e9..d38c40fe4dd 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -109,6 +109,8 @@ static ssize_t ahci_read_em_buffer(struct device *dev,
static ssize_t ahci_store_em_buffer(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size);
+static ssize_t ahci_show_em_supported(struct device *dev,
+ struct device_attribute *attr, char *buf);
static DEVICE_ATTR(ahci_host_caps, S_IRUGO, ahci_show_host_caps, NULL);
static DEVICE_ATTR(ahci_host_cap2, S_IRUGO, ahci_show_host_cap2, NULL);
@@ -116,6 +118,7 @@ static DEVICE_ATTR(ahci_host_version, S_IRUGO, ahci_show_host_version, NULL);
static DEVICE_ATTR(ahci_port_cmd, S_IRUGO, ahci_show_port_cmd, NULL);
static DEVICE_ATTR(em_buffer, S_IWUSR | S_IRUGO,
ahci_read_em_buffer, ahci_store_em_buffer);
+static DEVICE_ATTR(em_message_supported, S_IRUGO, ahci_show_em_supported, NULL);
struct device_attribute *ahci_shost_attrs[] = {
&dev_attr_link_power_management_policy,
@@ -126,6 +129,7 @@ struct device_attribute *ahci_shost_attrs[] = {
&dev_attr_ahci_host_version,
&dev_attr_ahci_port_cmd,
&dev_attr_em_buffer,
+ &dev_attr_em_message_supported,
NULL
};
EXPORT_SYMBOL_GPL(ahci_shost_attrs);
@@ -343,6 +347,24 @@ static ssize_t ahci_store_em_buffer(struct device *dev,
return size;
}
+static ssize_t ahci_show_em_supported(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct ata_port *ap = ata_shost_to_port(shost);
+ struct ahci_host_priv *hpriv = ap->host->private_data;
+ void __iomem *mmio = hpriv->mmio;
+ u32 em_ctl;
+
+ em_ctl = readl(mmio + HOST_EM_CTL);
+
+ return sprintf(buf, "%s%s%s%s\n",
+ em_ctl & EM_CTL_LED ? "led " : "",
+ em_ctl & EM_CTL_SAFTE ? "saf-te " : "",
+ em_ctl & EM_CTL_SES ? "ses-2 " : "",
+ em_ctl & EM_CTL_SGPIO ? "sgpio " : "");
+}
+
/**
* ahci_save_initial_config - Save and fixup initial config values
* @dev: target AHCI device
@@ -1897,7 +1919,17 @@ static void ahci_pmp_attach(struct ata_port *ap)
ahci_enable_fbs(ap);
pp->intr_mask |= PORT_IRQ_BAD_PMP;
- writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
+
+ /*
+ * We must not change the port interrupt mask register if the
+ * port is marked frozen, the value in pp->intr_mask will be
+ * restored later when the port is thawed.
+ *
+ * Note that during initialization, the port is marked as
+ * frozen since the irq handler is not yet registered.
+ */
+ if (!(ap->pflags & ATA_PFLAG_FROZEN))
+ writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
}
static void ahci_pmp_detach(struct ata_port *ap)
@@ -1913,7 +1945,10 @@ static void ahci_pmp_detach(struct ata_port *ap)
writel(cmd, port_mmio + PORT_CMD);
pp->intr_mask &= ~PORT_IRQ_BAD_PMP;
- writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
+
+ /* see comment above in ahci_pmp_attach() */
+ if (!(ap->pflags & ATA_PFLAG_FROZEN))
+ writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK);
}
int ahci_port_resume(struct ata_port *ap)
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 423c0a6952b..76c3c15cb1e 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -4139,6 +4139,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
*/
{ "PIONEER DVD-RW DVRTD08", "1.00", ATA_HORKAGE_NOSETXFER },
{ "PIONEER DVD-RW DVR-212D", "1.28", ATA_HORKAGE_NOSETXFER },
+ { "PIONEER DVD-RW DVR-216D", "1.08", ATA_HORKAGE_NOSETXFER },
/* End Marker */
{ }
@@ -5480,7 +5481,7 @@ struct ata_port *ata_port_alloc(struct ata_host *host)
if (!ap)
return NULL;
- ap->pflags |= ATA_PFLAG_INITIALIZING;
+ ap->pflags |= ATA_PFLAG_INITIALIZING | ATA_PFLAG_FROZEN;
ap->lock = &host->lock;
ap->print_id = -1;
ap->host = host;
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 88cd22fa65c..dad9fd660f3 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -3316,6 +3316,7 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
struct ata_eh_context *ehc = &link->eh_context;
struct ata_device *dev, *link_dev = NULL, *lpm_dev = NULL;
enum ata_lpm_policy old_policy = link->lpm_policy;
+ bool no_dipm = link->ap->flags & ATA_FLAG_NO_DIPM;
unsigned int hints = ATA_LPM_EMPTY | ATA_LPM_HIPM;
unsigned int err_mask;
int rc;
@@ -3332,7 +3333,7 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
*/
ata_for_each_dev(dev, link, ENABLED) {
bool hipm = ata_id_has_hipm(dev->id);
- bool dipm = ata_id_has_dipm(dev->id);
+ bool dipm = ata_id_has_dipm(dev->id) && !no_dipm;
/* find the first enabled and LPM enabled devices */
if (!link_dev)
@@ -3389,7 +3390,8 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
/* host config updated, enable DIPM if transitioning to MIN_POWER */
ata_for_each_dev(dev, link, ENABLED) {
- if (policy == ATA_LPM_MIN_POWER && ata_id_has_dipm(dev->id)) {
+ if (policy == ATA_LPM_MIN_POWER && !no_dipm &&
+ ata_id_has_dipm(dev->id)) {
err_mask = ata_dev_set_feature(dev,
SETFEATURES_SATA_ENABLE, SATA_DIPM);
if (err_mask && err_mask != AC_ERR_DEV) {
diff --git a/drivers/ata/pata_at91.c b/drivers/ata/pata_at91.c
index 0da0dcc7dd0..a5fdbdcb0fa 100644
--- a/drivers/ata/pata_at91.c
+++ b/drivers/ata/pata_at91.c
@@ -33,11 +33,12 @@
#define DRV_NAME "pata_at91"
-#define DRV_VERSION "0.1"
+#define DRV_VERSION "0.2"
#define CF_IDE_OFFSET 0x00c00000
#define CF_ALT_IDE_OFFSET 0x00e00000
#define CF_IDE_RES_SIZE 0x08
+#define NCS_RD_PULSE_LIMIT 0x3f /* maximal value for pulse bitfields */
struct at91_ide_info {
unsigned long mode;
@@ -49,8 +50,18 @@ struct at91_ide_info {
void __iomem *alt_addr;
};
-static const struct ata_timing initial_timing =
- {XFER_PIO_0, 70, 290, 240, 600, 165, 150, 600, 0};
+static const struct ata_timing initial_timing = {
+ .mode = XFER_PIO_0,
+ .setup = 70,
+ .act8b = 290,
+ .rec8b = 240,
+ .cyc8b = 600,
+ .active = 165,
+ .recover = 150,
+ .dmack_hold = 0,
+ .cycle = 600,
+ .udma = 0
+};
static unsigned long calc_mck_cycles(unsigned long ns, unsigned long mck_hz)
{
@@ -109,6 +120,11 @@ static void set_smc_timing(struct device *dev,
/* (CS0, CS1, DIR, OE) <= (CFCE1, CFCE2, CFRNW, NCSX) timings */
ncs_read_setup = 1;
ncs_read_pulse = read_cycle - 2;
+ if (ncs_read_pulse > NCS_RD_PULSE_LIMIT) {
+ ncs_read_pulse = NCS_RD_PULSE_LIMIT;
+ dev_warn(dev, "ncs_read_pulse limited to maximal value %lu\n",
+ ncs_read_pulse);
+ }
/* Write timings same as read timings */
write_cycle = read_cycle;
diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c
index bdd2719f3f6..bc9e702186d 100644
--- a/drivers/atm/fore200e.c
+++ b/drivers/atm/fore200e.c
@@ -2643,16 +2643,19 @@ fore200e_init(struct fore200e* fore200e, struct device *parent)
}
#ifdef CONFIG_SBUS
+static const struct of_device_id fore200e_sba_match[];
static int __devinit fore200e_sba_probe(struct platform_device *op)
{
+ const struct of_device_id *match;
const struct fore200e_bus *bus;
struct fore200e *fore200e;
static int index = 0;
int err;
- if (!op->dev.of_match)
+ match = of_match_device(fore200e_sba_match, &op->dev);
+ if (!match)
return -EINVAL;
- bus = op->dev.of_match->data;
+ bus = match->data;
fore200e = kzalloc(sizeof(struct fore200e), GFP_KERNEL);
if (!fore200e)
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index e9e5238f310..d57e8d0fb82 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -168,11 +168,4 @@ config SYS_HYPERVISOR
bool
default n
-config ARCH_NO_SYSDEV_OPS
- bool
- ---help---
- To be selected by architectures that don't use sysdev class or
- sysdev driver power management (suspend/resume) and shutdown
- operations.
-
endmenu
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 19f49e41ce5..a34dca0ad04 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -111,8 +111,6 @@ static inline int driver_match_device(struct device_driver *drv,
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
-extern void sysdev_shutdown(void);
-
extern char *make_class_name(const char *name, struct kobject *kobj);
extern int devres_release_all(struct device *dev);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 81b78ede37c..bc8729d603a 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -400,7 +400,7 @@ static void device_remove_groups(struct device *dev,
static int device_add_attrs(struct device *dev)
{
struct class *class = dev->class;
- struct device_type *type = dev->type;
+ const struct device_type *type = dev->type;
int error;
if (class) {
@@ -440,7 +440,7 @@ static int device_add_attrs(struct device *dev)
static void device_remove_attrs(struct device *dev)
{
struct class *class = dev->class;
- struct device_type *type = dev->type;
+ const struct device_type *type = dev->type;
device_remove_groups(dev, dev->groups);
@@ -1314,8 +1314,7 @@ EXPORT_SYMBOL_GPL(put_device);
EXPORT_SYMBOL_GPL(device_create_file);
EXPORT_SYMBOL_GPL(device_remove_file);
-struct root_device
-{
+struct root_device {
struct device dev;
struct module *owner;
};
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index da57ee9d63f..6658da743c3 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -245,6 +245,10 @@ int device_attach(struct device *dev)
device_lock(dev);
if (dev->driver) {
+ if (klist_node_attached(&dev->p->knode_driver)) {
+ ret = 1;
+ goto out_unlock;
+ }
ret = device_bind_driver(dev);
if (ret == 0)
ret = 1;
@@ -257,6 +261,7 @@ int device_attach(struct device *dev)
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
pm_runtime_put_sync(dev);
}
+out_unlock:
device_unlock(dev);
return ret;
}
@@ -316,8 +321,7 @@ static void __device_release_driver(struct device *dev)
drv = dev->driver;
if (drv) {
- pm_runtime_get_noresume(dev);
- pm_runtime_barrier(dev);
+ pm_runtime_get_sync(dev);
driver_sysfs_remove(dev);
@@ -326,6 +330,8 @@ static void __device_release_driver(struct device *dev)
BUS_NOTIFY_UNBIND_DRIVER,
dev);
+ pm_runtime_put_sync(dev);
+
if (dev->bus && dev->bus->remove)
dev->bus->remove(dev);
else if (drv->remove)
@@ -338,7 +344,6 @@ static void __device_release_driver(struct device *dev)
BUS_NOTIFY_UNBOUND_DRIVER,
dev);
- pm_runtime_put_sync(dev);
}
}
@@ -408,17 +413,16 @@ void *dev_get_drvdata(const struct device *dev)
}
EXPORT_SYMBOL(dev_get_drvdata);
-void dev_set_drvdata(struct device *dev, void *data)
+int dev_set_drvdata(struct device *dev, void *data)
{
int error;
- if (!dev)
- return;
if (!dev->p) {
error = device_private_init(dev);
if (error)
- return;
+ return error;
}
dev->p->driver_data = data;
+ return 0;
}
EXPORT_SYMBOL(dev_set_drvdata);
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 8c798ef7f13..bbb03e6f725 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -521,6 +521,11 @@ static int _request_firmware(const struct firmware **firmware_p,
if (!firmware_p)
return -EINVAL;
+ if (WARN_ON(usermodehelper_is_disabled())) {
+ dev_err(device, "firmware: %s will not be loaded\n", name);
+ return -EBUSY;
+ }
+
*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
if (!firmware) {
dev_err(device, "%s: kmalloc(struct firmware) failed\n",
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 3da6a43b775..0a134a424a3 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -48,7 +48,8 @@ static const char *memory_uevent_name(struct kset *kset, struct kobject *kobj)
return MEMORY_CLASS_NAME;
}
-static int memory_uevent(struct kset *kset, struct kobject *obj, struct kobj_uevent_env *env)
+static int memory_uevent(struct kset *kset, struct kobject *obj,
+ struct kobj_uevent_env *env)
{
int retval = 0;
@@ -228,10 +229,11 @@ int memory_isolate_notify(unsigned long val, void *v)
* OK to have direct references to sparsemem variables in here.
*/
static int
-memory_section_action(unsigned long phys_index, unsigned long action)
+memory_block_action(unsigned long phys_index, unsigned long action)
{
int i;
unsigned long start_pfn, start_paddr;
+ unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block;
struct page *first_page;
int ret;
@@ -243,7 +245,7 @@ memory_section_action(unsigned long phys_index, unsigned long action)
* that way.
*/
if (action == MEM_ONLINE) {
- for (i = 0; i < PAGES_PER_SECTION; i++) {
+ for (i = 0; i < nr_pages; i++) {
if (PageReserved(first_page+i))
continue;
@@ -257,12 +259,12 @@ memory_section_action(unsigned long phys_index, unsigned long action)
switch (action) {
case MEM_ONLINE:
start_pfn = page_to_pfn(first_page);
- ret = online_pages(start_pfn, PAGES_PER_SECTION);
+ ret = online_pages(start_pfn, nr_pages);
break;
case MEM_OFFLINE:
start_paddr = page_to_pfn(first_page) << PAGE_SHIFT;
ret = remove_memory(start_paddr,
- PAGES_PER_SECTION << PAGE_SHIFT);
+ nr_pages << PAGE_SHIFT);
break;
default:
WARN(1, KERN_WARNING "%s(%ld, %ld) unknown action: "
@@ -276,7 +278,7 @@ memory_section_action(unsigned long phys_index, unsigned long action)
static int memory_block_change_state(struct memory_block *mem,
unsigned long to_state, unsigned long from_state_req)
{
- int i, ret = 0;
+ int ret = 0;
mutex_lock(&mem->state_mutex);
@@ -288,20 +290,11 @@ static int memory_block_change_state(struct memory_block *mem,
if (to_state == MEM_OFFLINE)
mem->state = MEM_GOING_OFFLINE;
- for (i = 0; i < sections_per_block; i++) {
- ret = memory_section_action(mem->start_section_nr + i,
- to_state);
- if (ret)
- break;
- }
-
- if (ret) {
- for (i = 0; i < sections_per_block; i++)
- memory_section_action(mem->start_section_nr + i,
- from_state_req);
+ ret = memory_block_action(mem->start_section_nr, to_state);
+ if (ret)
mem->state = from_state_req;
- } else
+ else
mem->state = to_state;
out:
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index f051cfff18a..1c291af637b 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -149,6 +149,7 @@ static void platform_device_release(struct device *dev)
of_device_node_put(&pa->pdev.dev);
kfree(pa->pdev.dev.platform_data);
+ kfree(pa->pdev.mfd_cell);
kfree(pa->pdev.resource);
kfree(pa);
}
@@ -191,18 +192,18 @@ EXPORT_SYMBOL_GPL(platform_device_alloc);
int platform_device_add_resources(struct platform_device *pdev,
const struct resource *res, unsigned int num)
{
- struct resource *r;
+ struct resource *r = NULL;
- if (!res)
- return 0;
-
- r = kmemdup(res, sizeof(struct resource) * num, GFP_KERNEL);
- if (r) {
- pdev->resource = r;
- pdev->num_resources = num;
- return 0;
+ if (res) {
+ r = kmemdup(res, sizeof(struct resource) * num, GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
}
- return -ENOMEM;
+
+ kfree(pdev->resource);
+ pdev->resource = r;
+ pdev->num_resources = num;
+ return 0;
}
EXPORT_SYMBOL_GPL(platform_device_add_resources);
@@ -219,17 +220,17 @@ EXPORT_SYMBOL_GPL(platform_device_add_resources);
int platform_device_add_data(struct platform_device *pdev, const void *data,
size_t size)
{
- void *d;
+ void *d = NULL;
- if (!data)
- return 0;
-
- d = kmemdup(data, size, GFP_KERNEL);
- if (d) {
- pdev->dev.platform_data = d;
- return 0;
+ if (data) {
+ d = kmemdup(data, size, GFP_KERNEL);
+ if (!d)
+ return -ENOMEM;
}
- return -ENOMEM;
+
+ kfree(pdev->dev.platform_data);
+ pdev->dev.platform_data = d;
+ return 0;
}
EXPORT_SYMBOL_GPL(platform_device_add_data);
@@ -666,7 +667,7 @@ static int platform_legacy_resume(struct device *dev)
return ret;
}
-static int platform_pm_prepare(struct device *dev)
+int platform_pm_prepare(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -677,7 +678,7 @@ static int platform_pm_prepare(struct device *dev)
return ret;
}
-static void platform_pm_complete(struct device *dev)
+void platform_pm_complete(struct device *dev)
{
struct device_driver *drv = dev->driver;
@@ -685,16 +686,11 @@ static void platform_pm_complete(struct device *dev)
drv->pm->complete(dev);
}
-#else /* !CONFIG_PM_SLEEP */
-
-#define platform_pm_prepare NULL
-#define platform_pm_complete NULL
-
-#endif /* !CONFIG_PM_SLEEP */
+#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_SUSPEND
-int __weak platform_pm_suspend(struct device *dev)
+int platform_pm_suspend(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -712,7 +708,7 @@ int __weak platform_pm_suspend(struct device *dev)
return ret;
}
-int __weak platform_pm_suspend_noirq(struct device *dev)
+int platform_pm_suspend_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -728,7 +724,7 @@ int __weak platform_pm_suspend_noirq(struct device *dev)
return ret;
}
-int __weak platform_pm_resume(struct device *dev)
+int platform_pm_resume(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -746,7 +742,7 @@ int __weak platform_pm_resume(struct device *dev)
return ret;
}
-int __weak platform_pm_resume_noirq(struct device *dev)
+int platform_pm_resume_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -762,18 +758,11 @@ int __weak platform_pm_resume_noirq(struct device *dev)
return ret;
}
-#else /* !CONFIG_SUSPEND */
-
-#define platform_pm_suspend NULL
-#define platform_pm_resume NULL
-#define platform_pm_suspend_noirq NULL
-#define platform_pm_resume_noirq NULL
-
-#endif /* !CONFIG_SUSPEND */
+#endif /* CONFIG_SUSPEND */
-#ifdef CONFIG_HIBERNATION
+#ifdef CONFIG_HIBERNATE_CALLBACKS
-static int platform_pm_freeze(struct device *dev)
+int platform_pm_freeze(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -791,7 +780,7 @@ static int platform_pm_freeze(struct device *dev)
return ret;
}
-static int platform_pm_freeze_noirq(struct device *dev)
+int platform_pm_freeze_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -807,7 +796,7 @@ static int platform_pm_freeze_noirq(struct device *dev)
return ret;
}
-static int platform_pm_thaw(struct device *dev)
+int platform_pm_thaw(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -825,7 +814,7 @@ static int platform_pm_thaw(struct device *dev)
return ret;
}
-static int platform_pm_thaw_noirq(struct device *dev)
+int platform_pm_thaw_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -841,7 +830,7 @@ static int platform_pm_thaw_noirq(struct device *dev)
return ret;
}
-static int platform_pm_poweroff(struct device *dev)
+int platform_pm_poweroff(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -859,7 +848,7 @@ static int platform_pm_poweroff(struct device *dev)
return ret;
}
-static int platform_pm_poweroff_noirq(struct device *dev)
+int platform_pm_poweroff_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -875,7 +864,7 @@ static int platform_pm_poweroff_noirq(struct device *dev)
return ret;
}
-static int platform_pm_restore(struct device *dev)
+int platform_pm_restore(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -893,7 +882,7 @@ static int platform_pm_restore(struct device *dev)
return ret;
}
-static int platform_pm_restore_noirq(struct device *dev)
+int platform_pm_restore_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -909,62 +898,13 @@ static int platform_pm_restore_noirq(struct device *dev)
return ret;
}
-#else /* !CONFIG_HIBERNATION */
-
-#define platform_pm_freeze NULL
-#define platform_pm_thaw NULL
-#define platform_pm_poweroff NULL
-#define platform_pm_restore NULL
-#define platform_pm_freeze_noirq NULL
-#define platform_pm_thaw_noirq NULL
-#define platform_pm_poweroff_noirq NULL
-#define platform_pm_restore_noirq NULL
-
-#endif /* !CONFIG_HIBERNATION */
-
-#ifdef CONFIG_PM_RUNTIME
-
-int __weak platform_pm_runtime_suspend(struct device *dev)
-{
- return pm_generic_runtime_suspend(dev);
-};
-
-int __weak platform_pm_runtime_resume(struct device *dev)
-{
- return pm_generic_runtime_resume(dev);
-};
-
-int __weak platform_pm_runtime_idle(struct device *dev)
-{
- return pm_generic_runtime_idle(dev);
-};
-
-#else /* !CONFIG_PM_RUNTIME */
-
-#define platform_pm_runtime_suspend NULL
-#define platform_pm_runtime_resume NULL
-#define platform_pm_runtime_idle NULL
-
-#endif /* !CONFIG_PM_RUNTIME */
+#endif /* CONFIG_HIBERNATE_CALLBACKS */
static const struct dev_pm_ops platform_dev_pm_ops = {
- .prepare = platform_pm_prepare,
- .complete = platform_pm_complete,
- .suspend = platform_pm_suspend,
- .resume = platform_pm_resume,
- .freeze = platform_pm_freeze,
- .thaw = platform_pm_thaw,
- .poweroff = platform_pm_poweroff,
- .restore = platform_pm_restore,
- .suspend_noirq = platform_pm_suspend_noirq,
- .resume_noirq = platform_pm_resume_noirq,
- .freeze_noirq = platform_pm_freeze_noirq,
- .thaw_noirq = platform_pm_thaw_noirq,
- .poweroff_noirq = platform_pm_poweroff_noirq,
- .restore_noirq = platform_pm_restore_noirq,
- .runtime_suspend = platform_pm_runtime_suspend,
- .runtime_resume = platform_pm_runtime_resume,
- .runtime_idle = platform_pm_runtime_idle,
+ .runtime_suspend = pm_generic_runtime_suspend,
+ .runtime_resume = pm_generic_runtime_resume,
+ .runtime_idle = pm_generic_runtime_idle,
+ USE_PLATFORM_PM_SLEEP_OPS
};
struct bus_type platform_bus_type = {
@@ -976,41 +916,6 @@ struct bus_type platform_bus_type = {
};
EXPORT_SYMBOL_GPL(platform_bus_type);
-/**
- * platform_bus_get_pm_ops() - return pointer to busses dev_pm_ops
- *
- * This function can be used by platform code to get the current
- * set of dev_pm_ops functions used by the platform_bus_type.
- */
-const struct dev_pm_ops * __init platform_bus_get_pm_ops(void)
-{
- return platform_bus_type.pm;
-}
-
-/**
- * platform_bus_set_pm_ops() - update dev_pm_ops for the platform_bus_type
- *
- * @pm: pointer to new dev_pm_ops struct to be used for platform_bus_type
- *
- * Platform code can override the dev_pm_ops methods of
- * platform_bus_type by using this function. It is expected that
- * platform code will first do a platform_bus_get_pm_ops(), then
- * kmemdup it, then customize selected methods and pass a pointer to
- * the new struct dev_pm_ops to this function.
- *
- * Since platform-specific code is customizing methods for *all*
- * devices (not just platform-specific devices) it is expected that
- * any custom overrides of these functions will keep existing behavior
- * and simply extend it. For example, any customization of the
- * runtime PM methods should continue to call the pm_generic_*
- * functions as the default ones do in addition to the
- * platform-specific behavior.
- */
-void __init platform_bus_set_pm_ops(const struct dev_pm_ops *pm)
-{
- platform_bus_type.pm = pm;
-}
-
int __init platform_bus_init(void)
{
int error;
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
index 118c1b92a51..3647e114d0e 100644
--- a/drivers/base/power/Makefile
+++ b/drivers/base/power/Makefile
@@ -3,6 +3,6 @@ obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
obj-$(CONFIG_PM_RUNTIME) += runtime.o
obj-$(CONFIG_PM_TRACE_RTC) += trace.o
obj-$(CONFIG_PM_OPP) += opp.o
+obj-$(CONFIG_HAVE_CLK) += clock_ops.o
-ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
-ccflags-$(CONFIG_PM_VERBOSE) += -DDEBUG
+ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG \ No newline at end of file
diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c
new file mode 100644
index 00000000000..c0dd09df7be
--- /dev/null
+++ b/drivers/base/power/clock_ops.c
@@ -0,0 +1,431 @@
+/*
+ * drivers/base/power/clock_ops.c - Generic clock manipulation PM callbacks
+ *
+ * Copyright (c) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_PM_RUNTIME
+
+struct pm_runtime_clk_data {
+ struct list_head clock_list;
+ struct mutex lock;
+};
+
+enum pce_status {
+ PCE_STATUS_NONE = 0,
+ PCE_STATUS_ACQUIRED,
+ PCE_STATUS_ENABLED,
+ PCE_STATUS_ERROR,
+};
+
+struct pm_clock_entry {
+ struct list_head node;
+ char *con_id;
+ struct clk *clk;
+ enum pce_status status;
+};
+
+static struct pm_runtime_clk_data *__to_prd(struct device *dev)
+{
+ return dev ? dev->power.subsys_data : NULL;
+}
+
+/**
+ * pm_runtime_clk_add - Start using a device clock for runtime PM.
+ * @dev: Device whose clock is going to be used for runtime PM.
+ * @con_id: Connection ID of the clock.
+ *
+ * Add the clock represented by @con_id to the list of clocks used for
+ * the runtime PM of @dev.
+ */
+int pm_runtime_clk_add(struct device *dev, const char *con_id)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ if (!prd)
+ return -EINVAL;
+
+ ce = kzalloc(sizeof(*ce), GFP_KERNEL);
+ if (!ce) {
+ dev_err(dev, "Not enough memory for clock entry.\n");
+ return -ENOMEM;
+ }
+
+ if (con_id) {
+ ce->con_id = kstrdup(con_id, GFP_KERNEL);
+ if (!ce->con_id) {
+ dev_err(dev,
+ "Not enough memory for clock connection ID.\n");
+ kfree(ce);
+ return -ENOMEM;
+ }
+ }
+
+ mutex_lock(&prd->lock);
+ list_add_tail(&ce->node, &prd->clock_list);
+ mutex_unlock(&prd->lock);
+ return 0;
+}
+
+/**
+ * __pm_runtime_clk_remove - Destroy runtime PM clock entry.
+ * @ce: Runtime PM clock entry to destroy.
+ *
+ * This routine must be called under the mutex protecting the runtime PM list
+ * of clocks corresponding the the @ce's device.
+ */
+static void __pm_runtime_clk_remove(struct pm_clock_entry *ce)
+{
+ if (!ce)
+ return;
+
+ list_del(&ce->node);
+
+ if (ce->status < PCE_STATUS_ERROR) {
+ if (ce->status == PCE_STATUS_ENABLED)
+ clk_disable(ce->clk);
+
+ if (ce->status >= PCE_STATUS_ACQUIRED)
+ clk_put(ce->clk);
+ }
+
+ if (ce->con_id)
+ kfree(ce->con_id);
+
+ kfree(ce);
+}
+
+/**
+ * pm_runtime_clk_remove - Stop using a device clock for runtime PM.
+ * @dev: Device whose clock should not be used for runtime PM any more.
+ * @con_id: Connection ID of the clock.
+ *
+ * Remove the clock represented by @con_id from the list of clocks used for
+ * the runtime PM of @dev.
+ */
+void pm_runtime_clk_remove(struct device *dev, const char *con_id)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ if (!prd)
+ return;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry(ce, &prd->clock_list, node) {
+ if (!con_id && !ce->con_id) {
+ __pm_runtime_clk_remove(ce);
+ break;
+ } else if (!con_id || !ce->con_id) {
+ continue;
+ } else if (!strcmp(con_id, ce->con_id)) {
+ __pm_runtime_clk_remove(ce);
+ break;
+ }
+ }
+
+ mutex_unlock(&prd->lock);
+}
+
+/**
+ * pm_runtime_clk_init - Initialize a device's list of runtime PM clocks.
+ * @dev: Device to initialize the list of runtime PM clocks for.
+ *
+ * Allocate a struct pm_runtime_clk_data object, initialize its lock member and
+ * make the @dev's power.subsys_data field point to it.
+ */
+int pm_runtime_clk_init(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd;
+
+ prd = kzalloc(sizeof(*prd), GFP_KERNEL);
+ if (!prd) {
+ dev_err(dev, "Not enough memory fo runtime PM data.\n");
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&prd->clock_list);
+ mutex_init(&prd->lock);
+ dev->power.subsys_data = prd;
+ return 0;
+}
+
+/**
+ * pm_runtime_clk_destroy - Destroy a device's list of runtime PM clocks.
+ * @dev: Device to destroy the list of runtime PM clocks for.
+ *
+ * Clear the @dev's power.subsys_data field, remove the list of clock entries
+ * from the struct pm_runtime_clk_data object pointed to by it before and free
+ * that object.
+ */
+void pm_runtime_clk_destroy(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce, *c;
+
+ if (!prd)
+ return;
+
+ dev->power.subsys_data = NULL;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry_safe_reverse(ce, c, &prd->clock_list, node)
+ __pm_runtime_clk_remove(ce);
+
+ mutex_unlock(&prd->lock);
+
+ kfree(prd);
+}
+
+/**
+ * pm_runtime_clk_acquire - Acquire a device clock.
+ * @dev: Device whose clock is to be acquired.
+ * @con_id: Connection ID of the clock.
+ */
+static void pm_runtime_clk_acquire(struct device *dev,
+ struct pm_clock_entry *ce)
+{
+ ce->clk = clk_get(dev, ce->con_id);
+ if (IS_ERR(ce->clk)) {
+ ce->status = PCE_STATUS_ERROR;
+ } else {
+ ce->status = PCE_STATUS_ACQUIRED;
+ dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id);
+ }
+}
+
+/**
+ * pm_runtime_clk_suspend - Disable clocks in a device's runtime PM clock list.
+ * @dev: Device to disable the clocks for.
+ */
+int pm_runtime_clk_suspend(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ if (!prd)
+ return 0;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry_reverse(ce, &prd->clock_list, node) {
+ if (ce->status == PCE_STATUS_NONE)
+ pm_runtime_clk_acquire(dev, ce);
+
+ if (ce->status < PCE_STATUS_ERROR) {
+ clk_disable(ce->clk);
+ ce->status = PCE_STATUS_ACQUIRED;
+ }
+ }
+
+ mutex_unlock(&prd->lock);
+
+ return 0;
+}
+
+/**
+ * pm_runtime_clk_resume - Enable clocks in a device's runtime PM clock list.
+ * @dev: Device to enable the clocks for.
+ */
+int pm_runtime_clk_resume(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ if (!prd)
+ return 0;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry(ce, &prd->clock_list, node) {
+ if (ce->status == PCE_STATUS_NONE)
+ pm_runtime_clk_acquire(dev, ce);
+
+ if (ce->status < PCE_STATUS_ERROR) {
+ clk_enable(ce->clk);
+ ce->status = PCE_STATUS_ENABLED;
+ }
+ }
+
+ mutex_unlock(&prd->lock);
+
+ return 0;
+}
+
+/**
+ * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * @nb: Notifier block object this function is a member of.
+ * @action: Operation being carried out by the caller.
+ * @data: Device the routine is being run for.
+ *
+ * For this function to work, @nb must be a member of an object of type
+ * struct pm_clk_notifier_block containing all of the requisite data.
+ * Specifically, the pwr_domain member of that object is copied to the device's
+ * pwr_domain field and its con_ids member is used to populate the device's list
+ * of runtime PM clocks, depending on @action.
+ *
+ * If the device's pwr_domain field is already populated with a value different
+ * from the one stored in the struct pm_clk_notifier_block object, the function
+ * does nothing.
+ */
+static int pm_runtime_clk_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct pm_clk_notifier_block *clknb;
+ struct device *dev = data;
+ char *con_id;
+ int error;
+
+ dev_dbg(dev, "%s() %ld\n", __func__, action);
+
+ clknb = container_of(nb, struct pm_clk_notifier_block, nb);
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ if (dev->pwr_domain)
+ break;
+
+ error = pm_runtime_clk_init(dev);
+ if (error)
+ break;
+
+ dev->pwr_domain = clknb->pwr_domain;
+ if (clknb->con_ids[0]) {
+ for (con_id = clknb->con_ids[0]; *con_id; con_id++)
+ pm_runtime_clk_add(dev, con_id);
+ } else {
+ pm_runtime_clk_add(dev, NULL);
+ }
+
+ break;
+ case BUS_NOTIFY_DEL_DEVICE:
+ if (dev->pwr_domain != clknb->pwr_domain)
+ break;
+
+ dev->pwr_domain = NULL;
+ pm_runtime_clk_destroy(dev);
+ break;
+ }
+
+ return 0;
+}
+
+#else /* !CONFIG_PM_RUNTIME */
+
+/**
+ * enable_clock - Enable a device clock.
+ * @dev: Device whose clock is to be enabled.
+ * @con_id: Connection ID of the clock.
+ */
+static void enable_clock(struct device *dev, const char *con_id)
+{
+ struct clk *clk;
+
+ clk = clk_get(dev, con_id);
+ if (!IS_ERR(clk)) {
+ clk_enable(clk);
+ clk_put(clk);
+ dev_info(dev, "Runtime PM disabled, clock forced on.\n");
+ }
+}
+
+/**
+ * disable_clock - Disable a device clock.
+ * @dev: Device whose clock is to be disabled.
+ * @con_id: Connection ID of the clock.
+ */
+static void disable_clock(struct device *dev, const char *con_id)
+{
+ struct clk *clk;
+
+ clk = clk_get(dev, con_id);
+ if (!IS_ERR(clk)) {
+ clk_disable(clk);
+ clk_put(clk);
+ dev_info(dev, "Runtime PM disabled, clock forced off.\n");
+ }
+}
+
+/**
+ * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * @nb: Notifier block object this function is a member of.
+ * @action: Operation being carried out by the caller.
+ * @data: Device the routine is being run for.
+ *
+ * For this function to work, @nb must be a member of an object of type
+ * struct pm_clk_notifier_block containing all of the requisite data.
+ * Specifically, the con_ids member of that object is used to enable or disable
+ * the device's clocks, depending on @action.
+ */
+static int pm_runtime_clk_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct pm_clk_notifier_block *clknb;
+ struct device *dev = data;
+ char *con_id;
+
+ dev_dbg(dev, "%s() %ld\n", __func__, action);
+
+ clknb = container_of(nb, struct pm_clk_notifier_block, nb);
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ if (clknb->con_ids[0]) {
+ for (con_id = clknb->con_ids[0]; *con_id; con_id++)
+ enable_clock(dev, con_id);
+ } else {
+ enable_clock(dev, NULL);
+ }
+ break;
+ case BUS_NOTIFY_DEL_DEVICE:
+ if (clknb->con_ids[0]) {
+ for (con_id = clknb->con_ids[0]; *con_id; con_id++)
+ disable_clock(dev, con_id);
+ } else {
+ disable_clock(dev, NULL);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+#endif /* !CONFIG_PM_RUNTIME */
+
+/**
+ * pm_runtime_clk_add_notifier - Add bus type notifier for runtime PM clocks.
+ * @bus: Bus type to add the notifier to.
+ * @clknb: Notifier to be added to the given bus type.
+ *
+ * The nb member of @clknb is not expected to be initialized and its
+ * notifier_call member will be replaced with pm_runtime_clk_notify(). However,
+ * the remaining members of @clknb should be populated prior to calling this
+ * routine.
+ */
+void pm_runtime_clk_add_notifier(struct bus_type *bus,
+ struct pm_clk_notifier_block *clknb)
+{
+ if (!bus || !clknb)
+ return;
+
+ clknb->nb.notifier_call = pm_runtime_clk_notify;
+ bus_register_notifier(bus, &clknb->nb);
+}
diff --git a/drivers/base/power/generic_ops.c b/drivers/base/power/generic_ops.c
index 42f97f92562..cb3bb368681 100644
--- a/drivers/base/power/generic_ops.c
+++ b/drivers/base/power/generic_ops.c
@@ -74,6 +74,23 @@ EXPORT_SYMBOL_GPL(pm_generic_runtime_resume);
#ifdef CONFIG_PM_SLEEP
/**
+ * pm_generic_prepare - Generic routine preparing a device for power transition.
+ * @dev: Device to prepare.
+ *
+ * Prepare a device for a system-wide power transition.
+ */
+int pm_generic_prepare(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (drv && drv->pm && drv->pm->prepare)
+ ret = drv->pm->prepare(dev);
+
+ return ret;
+}
+
+/**
* __pm_generic_call - Generic suspend/freeze/poweroff/thaw subsystem callback.
* @dev: Device to handle.
* @event: PM transition of the system under way.
@@ -213,16 +230,38 @@ int pm_generic_restore(struct device *dev)
return __pm_generic_resume(dev, PM_EVENT_RESTORE);
}
EXPORT_SYMBOL_GPL(pm_generic_restore);
+
+/**
+ * pm_generic_complete - Generic routine competing a device power transition.
+ * @dev: Device to handle.
+ *
+ * Complete a device power transition during a system-wide power transition.
+ */
+void pm_generic_complete(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+
+ if (drv && drv->pm && drv->pm->complete)
+ drv->pm->complete(dev);
+
+ /*
+ * Let runtime PM try to suspend devices that haven't been in use before
+ * going into the system-wide sleep state we're resuming from.
+ */
+ pm_runtime_idle(dev);
+}
#endif /* CONFIG_PM_SLEEP */
struct dev_pm_ops generic_subsys_pm_ops = {
#ifdef CONFIG_PM_SLEEP
+ .prepare = pm_generic_prepare,
.suspend = pm_generic_suspend,
.resume = pm_generic_resume,
.freeze = pm_generic_freeze,
.thaw = pm_generic_thaw,
.poweroff = pm_generic_poweroff,
.restore = pm_generic_restore,
+ .complete = pm_generic_complete,
#endif
#ifdef CONFIG_PM_RUNTIME
.runtime_suspend = pm_generic_runtime_suspend,
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 052dc53eef3..aa632020774 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -63,6 +63,7 @@ void device_pm_init(struct device *dev)
dev->power.wakeup = NULL;
spin_lock_init(&dev->power.lock);
pm_runtime_init(dev);
+ INIT_LIST_HEAD(&dev->power.entry);
}
/**
@@ -233,7 +234,7 @@ static int pm_op(struct device *dev,
}
break;
#endif /* CONFIG_SUSPEND */
-#ifdef CONFIG_HIBERNATION
+#ifdef CONFIG_HIBERNATE_CALLBACKS
case PM_EVENT_FREEZE:
case PM_EVENT_QUIESCE:
if (ops->freeze) {
@@ -260,7 +261,7 @@ static int pm_op(struct device *dev,
suspend_report_result(ops->restore, error);
}
break;
-#endif /* CONFIG_HIBERNATION */
+#endif /* CONFIG_HIBERNATE_CALLBACKS */
default:
error = -EINVAL;
}
@@ -308,7 +309,7 @@ static int pm_noirq_op(struct device *dev,
}
break;
#endif /* CONFIG_SUSPEND */
-#ifdef CONFIG_HIBERNATION
+#ifdef CONFIG_HIBERNATE_CALLBACKS
case PM_EVENT_FREEZE:
case PM_EVENT_QUIESCE:
if (ops->freeze_noirq) {
@@ -335,7 +336,7 @@ static int pm_noirq_op(struct device *dev,
suspend_report_result(ops->restore_noirq, error);
}
break;
-#endif /* CONFIG_HIBERNATION */
+#endif /* CONFIG_HIBERNATE_CALLBACKS */
default:
error = -EINVAL;
}
@@ -425,10 +426,8 @@ static int device_resume_noirq(struct device *dev, pm_message_t state)
if (dev->pwr_domain) {
pm_dev_dbg(dev, state, "EARLY power domain ");
- pm_noirq_op(dev, &dev->pwr_domain->ops, state);
- }
-
- if (dev->type && dev->type->pm) {
+ error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+ } else if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "EARLY type ");
error = pm_noirq_op(dev, dev->type->pm, state);
} else if (dev->class && dev->class->pm) {
@@ -516,7 +515,8 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
if (dev->pwr_domain) {
pm_dev_dbg(dev, state, "power domain ");
- pm_op(dev, &dev->pwr_domain->ops, state);
+ error = pm_op(dev, &dev->pwr_domain->ops, state);
+ goto End;
}
if (dev->type && dev->type->pm) {
@@ -579,11 +579,13 @@ static bool is_async(struct device *dev)
* Execute the appropriate "resume" callback for all devices whose status
* indicates that they are suspended.
*/
-static void dpm_resume(pm_message_t state)
+void dpm_resume(pm_message_t state)
{
struct device *dev;
ktime_t starttime = ktime_get();
+ might_sleep();
+
mutex_lock(&dpm_list_mtx);
pm_transition = state;
async_error = 0;
@@ -628,12 +630,11 @@ static void device_complete(struct device *dev, pm_message_t state)
{
device_lock(dev);
- if (dev->pwr_domain && dev->pwr_domain->ops.complete) {
+ if (dev->pwr_domain) {
pm_dev_dbg(dev, state, "completing power domain ");
- dev->pwr_domain->ops.complete(dev);
- }
-
- if (dev->type && dev->type->pm) {
+ if (dev->pwr_domain->ops.complete)
+ dev->pwr_domain->ops.complete(dev);
+ } else if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "completing type ");
if (dev->type->pm->complete)
dev->type->pm->complete(dev);
@@ -657,10 +658,12 @@ static void device_complete(struct device *dev, pm_message_t state)
* Execute the ->complete() callbacks for all devices whose PM status is not
* DPM_ON (this allows new devices to be registered).
*/
-static void dpm_complete(pm_message_t state)
+void dpm_complete(pm_message_t state)
{
struct list_head list;
+ might_sleep();
+
INIT_LIST_HEAD(&list);
mutex_lock(&dpm_list_mtx);
while (!list_empty(&dpm_prepared_list)) {
@@ -689,7 +692,6 @@ static void dpm_complete(pm_message_t state)
*/
void dpm_resume_end(pm_message_t state)
{
- might_sleep();
dpm_resume(state);
dpm_complete(state);
}
@@ -731,7 +733,12 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state)
{
int error;
- if (dev->type && dev->type->pm) {
+ if (dev->pwr_domain) {
+ pm_dev_dbg(dev, state, "LATE power domain ");
+ error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+ if (error)
+ return error;
+ } else if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "LATE type ");
error = pm_noirq_op(dev, dev->type->pm, state);
if (error)
@@ -748,11 +755,6 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state)
return error;
}
- if (dev->pwr_domain) {
- pm_dev_dbg(dev, state, "LATE power domain ");
- pm_noirq_op(dev, &dev->pwr_domain->ops, state);
- }
-
return 0;
}
@@ -840,21 +842,27 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
goto End;
}
+ if (dev->pwr_domain) {
+ pm_dev_dbg(dev, state, "power domain ");
+ error = pm_op(dev, &dev->pwr_domain->ops, state);
+ goto End;
+ }
+
if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "type ");
error = pm_op(dev, dev->type->pm, state);
- goto Domain;
+ goto End;
}
if (dev->class) {
if (dev->class->pm) {
pm_dev_dbg(dev, state, "class ");
error = pm_op(dev, dev->class->pm, state);
- goto Domain;
+ goto End;
} else if (dev->class->suspend) {
pm_dev_dbg(dev, state, "legacy class ");
error = legacy_suspend(dev, state, dev->class->suspend);
- goto Domain;
+ goto End;
}
}
@@ -868,12 +876,6 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
}
}
- Domain:
- if (!error && dev->pwr_domain) {
- pm_dev_dbg(dev, state, "power domain ");
- pm_op(dev, &dev->pwr_domain->ops, state);
- }
-
End:
device_unlock(dev);
complete_all(&dev->power.completion);
@@ -913,11 +915,13 @@ static int device_suspend(struct device *dev)
* dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices.
* @state: PM transition of the system being carried out.
*/
-static int dpm_suspend(pm_message_t state)
+int dpm_suspend(pm_message_t state)
{
ktime_t starttime = ktime_get();
int error = 0;
+ might_sleep();
+
mutex_lock(&dpm_list_mtx);
pm_transition = state;
async_error = 0;
@@ -964,7 +968,14 @@ static int device_prepare(struct device *dev, pm_message_t state)
device_lock(dev);
- if (dev->type && dev->type->pm) {
+ if (dev->pwr_domain) {
+ pm_dev_dbg(dev, state, "preparing power domain ");
+ if (dev->pwr_domain->ops.prepare)
+ error = dev->pwr_domain->ops.prepare(dev);
+ suspend_report_result(dev->pwr_domain->ops.prepare, error);
+ if (error)
+ goto End;
+ } else if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "preparing type ");
if (dev->type->pm->prepare)
error = dev->type->pm->prepare(dev);
@@ -983,13 +994,6 @@ static int device_prepare(struct device *dev, pm_message_t state)
if (dev->bus->pm->prepare)
error = dev->bus->pm->prepare(dev);
suspend_report_result(dev->bus->pm->prepare, error);
- if (error)
- goto End;
- }
-
- if (dev->pwr_domain && dev->pwr_domain->ops.prepare) {
- pm_dev_dbg(dev, state, "preparing power domain ");
- dev->pwr_domain->ops.prepare(dev);
}
End:
@@ -1004,10 +1008,12 @@ static int device_prepare(struct device *dev, pm_message_t state)
*
* Execute the ->prepare() callback(s) for all devices.
*/
-static int dpm_prepare(pm_message_t state)
+int dpm_prepare(pm_message_t state)
{
int error = 0;
+ might_sleep();
+
mutex_lock(&dpm_list_mtx);
while (!list_empty(&dpm_list)) {
struct device *dev = to_device(dpm_list.next);
@@ -1056,7 +1062,6 @@ int dpm_suspend_start(pm_message_t state)
{
int error;
- might_sleep();
error = dpm_prepare(state);
if (!error)
error = dpm_suspend(state);
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 3172c60d23a..0d4587b15c5 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -168,7 +168,6 @@ static int rpm_check_suspend_allowed(struct device *dev)
static int rpm_idle(struct device *dev, int rpmflags)
{
int (*callback)(struct device *);
- int (*domain_callback)(struct device *);
int retval;
retval = rpm_check_suspend_allowed(dev);
@@ -214,7 +213,9 @@ static int rpm_idle(struct device *dev, int rpmflags)
dev->power.idle_notification = true;
- if (dev->type && dev->type->pm)
+ if (dev->pwr_domain)
+ callback = dev->pwr_domain->ops.runtime_idle;
+ else if (dev->type && dev->type->pm)
callback = dev->type->pm->runtime_idle;
else if (dev->class && dev->class->pm)
callback = dev->class->pm->runtime_idle;
@@ -223,19 +224,10 @@ static int rpm_idle(struct device *dev, int rpmflags)
else
callback = NULL;
- if (dev->pwr_domain)
- domain_callback = dev->pwr_domain->ops.runtime_idle;
- else
- domain_callback = NULL;
-
- if (callback || domain_callback) {
+ if (callback) {
spin_unlock_irq(&dev->power.lock);
- if (domain_callback)
- retval = domain_callback(dev);
-
- if (!retval && callback)
- callback(dev);
+ callback(dev);
spin_lock_irq(&dev->power.lock);
}
@@ -382,7 +374,9 @@ static int rpm_suspend(struct device *dev, int rpmflags)
__update_runtime_status(dev, RPM_SUSPENDING);
- if (dev->type && dev->type->pm)
+ if (dev->pwr_domain)
+ callback = dev->pwr_domain->ops.runtime_suspend;
+ else if (dev->type && dev->type->pm)
callback = dev->type->pm->runtime_suspend;
else if (dev->class && dev->class->pm)
callback = dev->class->pm->runtime_suspend;
@@ -400,8 +394,6 @@ static int rpm_suspend(struct device *dev, int rpmflags)
else
pm_runtime_cancel_pending(dev);
} else {
- if (dev->pwr_domain)
- rpm_callback(dev->pwr_domain->ops.runtime_suspend, dev);
no_callback:
__update_runtime_status(dev, RPM_SUSPENDED);
pm_runtime_deactivate_timer(dev);
@@ -582,9 +574,8 @@ static int rpm_resume(struct device *dev, int rpmflags)
__update_runtime_status(dev, RPM_RESUMING);
if (dev->pwr_domain)
- rpm_callback(dev->pwr_domain->ops.runtime_resume, dev);
-
- if (dev->type && dev->type->pm)
+ callback = dev->pwr_domain->ops.runtime_resume;
+ else if (dev->type && dev->type->pm)
callback = dev->type->pm->runtime_resume;
else if (dev->class && dev->class->pm)
callback = dev->class->pm->runtime_resume;
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index fff49bee781..a9f5b897961 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -212,8 +212,9 @@ static ssize_t autosuspend_delay_ms_store(struct device *dev,
static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show,
autosuspend_delay_ms_store);
-#endif
+#endif /* CONFIG_PM_RUNTIME */
+#ifdef CONFIG_PM_SLEEP
static ssize_t
wake_show(struct device * dev, struct device_attribute *attr, char * buf)
{
@@ -248,7 +249,6 @@ wake_store(struct device * dev, struct device_attribute *attr,
static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);
-#ifdef CONFIG_PM_SLEEP
static ssize_t wakeup_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 4573c83df6d..84f7c7d5a09 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -110,7 +110,6 @@ void wakeup_source_add(struct wakeup_source *ws)
spin_lock_irq(&events_lock);
list_add_rcu(&ws->entry, &wakeup_sources);
spin_unlock_irq(&events_lock);
- synchronize_rcu();
}
EXPORT_SYMBOL_GPL(wakeup_source_add);
@@ -258,7 +257,7 @@ void device_set_wakeup_capable(struct device *dev, bool capable)
if (!!dev->power.can_wakeup == !!capable)
return;
- if (device_is_registered(dev)) {
+ if (device_is_registered(dev) && !list_empty(&dev->power.entry)) {
if (capable) {
if (wakeup_sysfs_add(dev))
return;
diff --git a/drivers/base/sys.c b/drivers/base/sys.c
index acde9b5ee13..9dff77bfe1e 100644
--- a/drivers/base/sys.c
+++ b/drivers/base/sys.c
@@ -328,203 +328,8 @@ void sysdev_unregister(struct sys_device *sysdev)
kobject_put(&sysdev->kobj);
}
-
-#ifndef CONFIG_ARCH_NO_SYSDEV_OPS
-/**
- * sysdev_shutdown - Shut down all system devices.
- *
- * Loop over each class of system devices, and the devices in each
- * of those classes. For each device, we call the shutdown method for
- * each driver registered for the device - the auxiliaries,
- * and the class driver.
- *
- * Note: The list is iterated in reverse order, so that we shut down
- * child devices before we shut down their parents. The list ordering
- * is guaranteed by virtue of the fact that child devices are registered
- * after their parents.
- */
-void sysdev_shutdown(void)
-{
- struct sysdev_class *cls;
-
- pr_debug("Shutting Down System Devices\n");
-
- mutex_lock(&sysdev_drivers_lock);
- list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) {
- struct sys_device *sysdev;
-
- pr_debug("Shutting down type '%s':\n",
- kobject_name(&cls->kset.kobj));
-
- list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) {
- struct sysdev_driver *drv;
- pr_debug(" %s\n", kobject_name(&sysdev->kobj));
-
- /* Call auxiliary drivers first */
- list_for_each_entry(drv, &cls->drivers, entry) {
- if (drv->shutdown)
- drv->shutdown(sysdev);
- }
-
- /* Now call the generic one */
- if (cls->shutdown)
- cls->shutdown(sysdev);
- }
- }
- mutex_unlock(&sysdev_drivers_lock);
-}
-
-static void __sysdev_resume(struct sys_device *dev)
-{
- struct sysdev_class *cls = dev->cls;
- struct sysdev_driver *drv;
-
- /* First, call the class-specific one */
- if (cls->resume)
- cls->resume(dev);
- WARN_ONCE(!irqs_disabled(),
- "Interrupts enabled after %pF\n", cls->resume);
-
- /* Call auxiliary drivers next. */
- list_for_each_entry(drv, &cls->drivers, entry) {
- if (drv->resume)
- drv->resume(dev);
- WARN_ONCE(!irqs_disabled(),
- "Interrupts enabled after %pF\n", drv->resume);
- }
-}
-
-/**
- * sysdev_suspend - Suspend all system devices.
- * @state: Power state to enter.
- *
- * We perform an almost identical operation as sysdev_shutdown()
- * above, though calling ->suspend() instead. Interrupts are disabled
- * when this called. Devices are responsible for both saving state and
- * quiescing or powering down the device.
- *
- * This is only called by the device PM core, so we let them handle
- * all synchronization.
- */
-int sysdev_suspend(pm_message_t state)
-{
- struct sysdev_class *cls;
- struct sys_device *sysdev, *err_dev;
- struct sysdev_driver *drv, *err_drv;
- int ret;
-
- pr_debug("Checking wake-up interrupts\n");
-
- /* Return error code if there are any wake-up interrupts pending */
- ret = check_wakeup_irqs();
- if (ret)
- return ret;
-
- WARN_ONCE(!irqs_disabled(),
- "Interrupts enabled while suspending system devices\n");
-
- pr_debug("Suspending System Devices\n");
-
- list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) {
- pr_debug("Suspending type '%s':\n",
- kobject_name(&cls->kset.kobj));
-
- list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) {
- pr_debug(" %s\n", kobject_name(&sysdev->kobj));
-
- /* Call auxiliary drivers first */
- list_for_each_entry(drv, &cls->drivers, entry) {
- if (drv->suspend) {
- ret = drv->suspend(sysdev, state);
- if (ret)
- goto aux_driver;
- }
- WARN_ONCE(!irqs_disabled(),
- "Interrupts enabled after %pF\n",
- drv->suspend);
- }
-
- /* Now call the generic one */
- if (cls->suspend) {
- ret = cls->suspend(sysdev, state);
- if (ret)
- goto cls_driver;
- WARN_ONCE(!irqs_disabled(),
- "Interrupts enabled after %pF\n",
- cls->suspend);
- }
- }
- }
- return 0;
- /* resume current sysdev */
-cls_driver:
- drv = NULL;
- printk(KERN_ERR "Class suspend failed for %s: %d\n",
- kobject_name(&sysdev->kobj), ret);
-
-aux_driver:
- if (drv)
- printk(KERN_ERR "Class driver suspend failed for %s: %d\n",
- kobject_name(&sysdev->kobj), ret);
- list_for_each_entry(err_drv, &cls->drivers, entry) {
- if (err_drv == drv)
- break;
- if (err_drv->resume)
- err_drv->resume(sysdev);
- }
-
- /* resume other sysdevs in current class */
- list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) {
- if (err_dev == sysdev)
- break;
- pr_debug(" %s\n", kobject_name(&err_dev->kobj));
- __sysdev_resume(err_dev);
- }
-
- /* resume other classes */
- list_for_each_entry_continue(cls, &system_kset->list, kset.kobj.entry) {
- list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) {
- pr_debug(" %s\n", kobject_name(&err_dev->kobj));
- __sysdev_resume(err_dev);
- }
- }
- return ret;
-}
-EXPORT_SYMBOL_GPL(sysdev_suspend);
-
-/**
- * sysdev_resume - Bring system devices back to life.
- *
- * Similar to sysdev_suspend(), but we iterate the list forwards
- * to guarantee that parent devices are resumed before their children.
- *
- * Note: Interrupts are disabled when called.
- */
-int sysdev_resume(void)
-{
- struct sysdev_class *cls;
-
- WARN_ONCE(!irqs_disabled(),
- "Interrupts enabled while resuming system devices\n");
-
- pr_debug("Resuming System Devices\n");
-
- list_for_each_entry(cls, &system_kset->list, kset.kobj.entry) {
- struct sys_device *sysdev;
-
- pr_debug("Resuming type '%s':\n",
- kobject_name(&cls->kset.kobj));
-
- list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) {
- pr_debug(" %s\n", kobject_name(&sysdev->kobj));
-
- __sysdev_resume(sysdev);
- }
- }
- return 0;
-}
-EXPORT_SYMBOL_GPL(sysdev_resume);
-#endif /* CONFIG_ARCH_NO_SYSDEV_OPS */
+EXPORT_SYMBOL_GPL(sysdev_register);
+EXPORT_SYMBOL_GPL(sysdev_unregister);
int __init system_bus_init(void)
{
@@ -534,9 +339,6 @@ int __init system_bus_init(void)
return 0;
}
-EXPORT_SYMBOL_GPL(sysdev_register);
-EXPORT_SYMBOL_GPL(sysdev_unregister);
-
#define to_ext_attr(x) container_of(x, struct sysdev_ext_attribute, attr)
ssize_t sysdev_store_ulong(struct sys_device *sysdev,
diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c
index 90af2943f9e..c126db3cb7d 100644
--- a/drivers/base/syscore.c
+++ b/drivers/base/syscore.c
@@ -73,6 +73,7 @@ int syscore_suspend(void)
return ret;
}
+EXPORT_SYMBOL_GPL(syscore_suspend);
/**
* syscore_resume - Execute all the registered system core resume callbacks.
@@ -95,6 +96,7 @@ void syscore_resume(void)
"Interrupts enabled after %pF\n", ops->resume);
}
}
+EXPORT_SYMBOL_GPL(syscore_resume);
#endif /* CONFIG_PM_SLEEP */
/**
diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c
index 8066d086578..e086fbbbe85 100644
--- a/drivers/block/DAC960.c
+++ b/drivers/block/DAC960.c
@@ -2547,7 +2547,6 @@ static bool DAC960_RegisterBlockDevice(DAC960_Controller_T *Controller)
disk->major = MajorNumber;
disk->first_minor = n << DAC960_MaxPartitionsBits;
disk->fops = &DAC960_BlockDeviceOperations;
- disk->events = DISK_EVENT_MEDIA_CHANGE;
}
/*
Indicate the Block Device Registration completed successfully,
diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c
index 456c0cc90dc..8eba86bba59 100644
--- a/drivers/block/amiflop.c
+++ b/drivers/block/amiflop.c
@@ -1736,7 +1736,6 @@ static int __init fd_probe_drives(void)
disk->major = FLOPPY_MAJOR;
disk->first_minor = drive;
disk->fops = &floppy_fops;
- disk->events = DISK_EVENT_MEDIA_CHANGE;
sprintf(disk->disk_name, "fd%d", drive);
disk->private_data = &unit[drive];
set_capacity(disk, 880*2);
diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c
index c871eae1412..ede16c64ff0 100644
--- a/drivers/block/ataflop.c
+++ b/drivers/block/ataflop.c
@@ -1964,7 +1964,6 @@ static int __init atari_floppy_init (void)
unit[i].disk->first_minor = i;
sprintf(unit[i].disk->disk_name, "fd%d", i);
unit[i].disk->fops = &floppy_fops;
- unit[i].disk->events = DISK_EVENT_MEDIA_CHANGE;
unit[i].disk->private_data = &unit[i];
unit[i].disk->queue = blk_init_queue(do_fd_request,
&ataflop_lock);
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index 301d7a9a41a..db8f88586c8 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -4205,7 +4205,6 @@ static int __init floppy_init(void)
disks[dr]->major = FLOPPY_MAJOR;
disks[dr]->first_minor = TOMINOR(dr);
disks[dr]->fops = &floppy_fops;
- disks[dr]->events = DISK_EVENT_MEDIA_CHANGE;
sprintf(disks[dr]->disk_name, "fd%d", dr);
init_timer(&motor_off_timer[dr]);
diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c
index 2f2ccf68625..8690e31d993 100644
--- a/drivers/block/paride/pcd.c
+++ b/drivers/block/paride/pcd.c
@@ -320,7 +320,6 @@ static void pcd_init_units(void)
disk->first_minor = unit;
strcpy(disk->disk_name, cd->name); /* umm... */
disk->fops = &pcd_bdops;
- disk->events = DISK_EVENT_MEDIA_CHANGE;
}
}
diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c
index 21dfdb77686..869e7676d46 100644
--- a/drivers/block/paride/pd.c
+++ b/drivers/block/paride/pd.c
@@ -837,7 +837,6 @@ static void pd_probe_drive(struct pd_unit *disk)
p->fops = &pd_fops;
p->major = major;
p->first_minor = (disk - pd) << PD_BITS;
- p->events = DISK_EVENT_MEDIA_CHANGE;
disk->gd = p;
p->private_data = disk;
p->queue = pd_queue;
diff --git a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c
index 7adeb1edbf4..f21b520ef41 100644
--- a/drivers/block/paride/pf.c
+++ b/drivers/block/paride/pf.c
@@ -294,7 +294,6 @@ static void __init pf_init_units(void)
disk->first_minor = unit;
strcpy(disk->disk_name, pf->name);
disk->fops = &pf_fops;
- disk->events = DISK_EVENT_MEDIA_CHANGE;
if (!(*drives[unit])[D_PRT])
pf_drive_count++;
}
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 16dc3645291..9712fad82bc 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -92,6 +92,8 @@ struct rbd_client {
struct list_head node;
};
+struct rbd_req_coll;
+
/*
* a single io request
*/
@@ -100,6 +102,24 @@ struct rbd_request {
struct bio *bio; /* cloned bio */
struct page **pages; /* list of used pages */
u64 len;
+ int coll_index;
+ struct rbd_req_coll *coll;
+};
+
+struct rbd_req_status {
+ int done;
+ int rc;
+ u64 bytes;
+};
+
+/*
+ * a collection of requests
+ */
+struct rbd_req_coll {
+ int total;
+ int num_done;
+ struct kref kref;
+ struct rbd_req_status status[0];
};
struct rbd_snap {
@@ -416,6 +436,17 @@ static void rbd_put_client(struct rbd_device *rbd_dev)
rbd_dev->client = NULL;
}
+/*
+ * Destroy requests collection
+ */
+static void rbd_coll_release(struct kref *kref)
+{
+ struct rbd_req_coll *coll =
+ container_of(kref, struct rbd_req_coll, kref);
+
+ dout("rbd_coll_release %p\n", coll);
+ kfree(coll);
+}
/*
* Create a new header structure, translate header format from the on-disk
@@ -590,6 +621,14 @@ static u64 rbd_get_segment(struct rbd_image_header *header,
return len;
}
+static int rbd_get_num_segments(struct rbd_image_header *header,
+ u64 ofs, u64 len)
+{
+ u64 start_seg = ofs >> header->obj_order;
+ u64 end_seg = (ofs + len - 1) >> header->obj_order;
+ return end_seg - start_seg + 1;
+}
+
/*
* bio helpers
*/
@@ -735,6 +774,50 @@ static void rbd_destroy_ops(struct ceph_osd_req_op *ops)
kfree(ops);
}
+static void rbd_coll_end_req_index(struct request *rq,
+ struct rbd_req_coll *coll,
+ int index,
+ int ret, u64 len)
+{
+ struct request_queue *q;
+ int min, max, i;
+
+ dout("rbd_coll_end_req_index %p index %d ret %d len %lld\n",
+ coll, index, ret, len);
+
+ if (!rq)
+ return;
+
+ if (!coll) {
+ blk_end_request(rq, ret, len);
+ return;
+ }
+
+ q = rq->q;
+
+ spin_lock_irq(q->queue_lock);
+ coll->status[index].done = 1;
+ coll->status[index].rc = ret;
+ coll->status[index].bytes = len;
+ max = min = coll->num_done;
+ while (max < coll->total && coll->status[max].done)
+ max++;
+
+ for (i = min; i<max; i++) {
+ __blk_end_request(rq, coll->status[i].rc,
+ coll->status[i].bytes);
+ coll->num_done++;
+ kref_put(&coll->kref, rbd_coll_release);
+ }
+ spin_unlock_irq(q->queue_lock);
+}
+
+static void rbd_coll_end_req(struct rbd_request *req,
+ int ret, u64 len)
+{
+ rbd_coll_end_req_index(req->rq, req->coll, req->coll_index, ret, len);
+}
+
/*
* Send ceph osd request
*/
@@ -749,6 +832,8 @@ static int rbd_do_request(struct request *rq,
int flags,
struct ceph_osd_req_op *ops,
int num_reply,
+ struct rbd_req_coll *coll,
+ int coll_index,
void (*rbd_cb)(struct ceph_osd_request *req,
struct ceph_msg *msg),
struct ceph_osd_request **linger_req,
@@ -763,12 +848,20 @@ static int rbd_do_request(struct request *rq,
struct ceph_osd_request_head *reqhead;
struct rbd_image_header *header = &dev->header;
- ret = -ENOMEM;
req_data = kzalloc(sizeof(*req_data), GFP_NOIO);
- if (!req_data)
- goto done;
+ if (!req_data) {
+ if (coll)
+ rbd_coll_end_req_index(rq, coll, coll_index,
+ -ENOMEM, len);
+ return -ENOMEM;
+ }
- dout("rbd_do_request len=%lld ofs=%lld\n", len, ofs);
+ if (coll) {
+ req_data->coll = coll;
+ req_data->coll_index = coll_index;
+ }
+
+ dout("rbd_do_request obj=%s ofs=%lld len=%lld\n", obj, len, ofs);
down_read(&header->snap_rwsem);
@@ -777,9 +870,9 @@ static int rbd_do_request(struct request *rq,
ops,
false,
GFP_NOIO, pages, bio);
- if (IS_ERR(req)) {
+ if (!req) {
up_read(&header->snap_rwsem);
- ret = PTR_ERR(req);
+ ret = -ENOMEM;
goto done_pages;
}
@@ -828,7 +921,8 @@ static int rbd_do_request(struct request *rq,
ret = ceph_osdc_wait_request(&dev->client->osdc, req);
if (ver)
*ver = le64_to_cpu(req->r_reassert_version.version);
- dout("reassert_ver=%lld\n", le64_to_cpu(req->r_reassert_version.version));
+ dout("reassert_ver=%lld\n",
+ le64_to_cpu(req->r_reassert_version.version));
ceph_osdc_put_request(req);
}
return ret;
@@ -837,10 +931,8 @@ done_err:
bio_chain_put(req_data->bio);
ceph_osdc_put_request(req);
done_pages:
+ rbd_coll_end_req(req_data, ret, len);
kfree(req_data);
-done:
- if (rq)
- blk_end_request(rq, ret, len);
return ret;
}
@@ -874,7 +966,7 @@ static void rbd_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg)
bytes = req_data->len;
}
- blk_end_request(req_data->rq, rc, bytes);
+ rbd_coll_end_req(req_data, rc, bytes);
if (req_data->bio)
bio_chain_put(req_data->bio);
@@ -934,6 +1026,7 @@ static int rbd_req_sync_op(struct rbd_device *dev,
flags,
ops,
2,
+ NULL, 0,
NULL,
linger_req, ver);
if (ret < 0)
@@ -959,7 +1052,9 @@ static int rbd_do_op(struct request *rq,
u64 snapid,
int opcode, int flags, int num_reply,
u64 ofs, u64 len,
- struct bio *bio)
+ struct bio *bio,
+ struct rbd_req_coll *coll,
+ int coll_index)
{
char *seg_name;
u64 seg_ofs;
@@ -995,7 +1090,10 @@ static int rbd_do_op(struct request *rq,
flags,
ops,
num_reply,
+ coll, coll_index,
rbd_req_cb, 0, NULL);
+
+ rbd_destroy_ops(ops);
done:
kfree(seg_name);
return ret;
@@ -1008,13 +1106,15 @@ static int rbd_req_write(struct request *rq,
struct rbd_device *rbd_dev,
struct ceph_snap_context *snapc,
u64 ofs, u64 len,
- struct bio *bio)
+ struct bio *bio,
+ struct rbd_req_coll *coll,
+ int coll_index)
{
return rbd_do_op(rq, rbd_dev, snapc, CEPH_NOSNAP,
CEPH_OSD_OP_WRITE,
CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
2,
- ofs, len, bio);
+ ofs, len, bio, coll, coll_index);
}
/*
@@ -1024,14 +1124,16 @@ static int rbd_req_read(struct request *rq,
struct rbd_device *rbd_dev,
u64 snapid,
u64 ofs, u64 len,
- struct bio *bio)
+ struct bio *bio,
+ struct rbd_req_coll *coll,
+ int coll_index)
{
return rbd_do_op(rq, rbd_dev, NULL,
(snapid ? snapid : CEPH_NOSNAP),
CEPH_OSD_OP_READ,
CEPH_OSD_FLAG_READ,
2,
- ofs, len, bio);
+ ofs, len, bio, coll, coll_index);
}
/*
@@ -1063,7 +1165,9 @@ static int rbd_req_sync_notify_ack(struct rbd_device *dev,
{
struct ceph_osd_req_op *ops;
struct page **pages = NULL;
- int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_NOTIFY_ACK, 0);
+ int ret;
+
+ ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_NOTIFY_ACK, 0);
if (ret < 0)
return ret;
@@ -1077,6 +1181,7 @@ static int rbd_req_sync_notify_ack(struct rbd_device *dev,
CEPH_OSD_FLAG_READ,
ops,
1,
+ NULL, 0,
rbd_simple_req_cb, 0, NULL);
rbd_destroy_ops(ops);
@@ -1274,6 +1379,20 @@ static int rbd_req_sync_exec(struct rbd_device *dev,
return ret;
}
+static struct rbd_req_coll *rbd_alloc_coll(int num_reqs)
+{
+ struct rbd_req_coll *coll =
+ kzalloc(sizeof(struct rbd_req_coll) +
+ sizeof(struct rbd_req_status) * num_reqs,
+ GFP_ATOMIC);
+
+ if (!coll)
+ return NULL;
+ coll->total = num_reqs;
+ kref_init(&coll->kref);
+ return coll;
+}
+
/*
* block device queue callback
*/
@@ -1291,6 +1410,8 @@ static void rbd_rq_fn(struct request_queue *q)
bool do_write;
int size, op_size = 0;
u64 ofs;
+ int num_segs, cur_seg = 0;
+ struct rbd_req_coll *coll;
/* peek at request from block layer */
if (!rq)
@@ -1321,6 +1442,14 @@ static void rbd_rq_fn(struct request_queue *q)
do_write ? "write" : "read",
size, blk_rq_pos(rq) * 512ULL);
+ num_segs = rbd_get_num_segments(&rbd_dev->header, ofs, size);
+ coll = rbd_alloc_coll(num_segs);
+ if (!coll) {
+ spin_lock_irq(q->queue_lock);
+ __blk_end_request_all(rq, -ENOMEM);
+ goto next;
+ }
+
do {
/* a bio clone to be passed down to OSD req */
dout("rq->bio->bi_vcnt=%d\n", rq->bio->bi_vcnt);
@@ -1328,35 +1457,41 @@ static void rbd_rq_fn(struct request_queue *q)
rbd_dev->header.block_name,
ofs, size,
NULL, NULL);
+ kref_get(&coll->kref);
bio = bio_chain_clone(&rq_bio, &next_bio, &bp,
op_size, GFP_ATOMIC);
if (!bio) {
- spin_lock_irq(q->queue_lock);
- __blk_end_request_all(rq, -ENOMEM);
- goto next;
+ rbd_coll_end_req_index(rq, coll, cur_seg,
+ -ENOMEM, op_size);
+ goto next_seg;
}
+
/* init OSD command: write or read */
if (do_write)
rbd_req_write(rq, rbd_dev,
rbd_dev->header.snapc,
ofs,
- op_size, bio);
+ op_size, bio,
+ coll, cur_seg);
else
rbd_req_read(rq, rbd_dev,
cur_snap_id(rbd_dev),
ofs,
- op_size, bio);
+ op_size, bio,
+ coll, cur_seg);
+next_seg:
size -= op_size;
ofs += op_size;
+ cur_seg++;
rq_bio = next_bio;
} while (size > 0);
+ kref_put(&coll->kref, rbd_coll_release);
if (bp)
bio_pair_release(bp);
-
spin_lock_irq(q->queue_lock);
next:
rq = blk_fetch_request(q);
diff --git a/drivers/block/swim.c b/drivers/block/swim.c
index 24a482f2fbd..fd5adcd5594 100644
--- a/drivers/block/swim.c
+++ b/drivers/block/swim.c
@@ -858,7 +858,6 @@ static int __devinit swim_floppy_init(struct swim_priv *swd)
swd->unit[drive].disk->first_minor = drive;
sprintf(swd->unit[drive].disk->disk_name, "fd%d", drive);
swd->unit[drive].disk->fops = &floppy_fops;
- swd->unit[drive].disk->events = DISK_EVENT_MEDIA_CHANGE;
swd->unit[drive].disk->private_data = &swd->unit[drive];
swd->unit[drive].disk->queue = swd->queue;
set_capacity(swd->unit[drive].disk, 2880);
diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c
index 4c10f56facb..773bfa79277 100644
--- a/drivers/block/swim3.c
+++ b/drivers/block/swim3.c
@@ -1163,7 +1163,6 @@ static int __devinit swim3_attach(struct macio_dev *mdev, const struct of_device
disk->major = FLOPPY_MAJOR;
disk->first_minor = i;
disk->fops = &floppy_fops;
- disk->events = DISK_EVENT_MEDIA_CHANGE;
disk->private_data = &floppy_states[i];
disk->queue = swim3_queue;
disk->flags |= GENHD_FL_REMOVABLE;
diff --git a/drivers/block/ub.c b/drivers/block/ub.c
index 68b9430c7cf..0e376d46bdd 100644
--- a/drivers/block/ub.c
+++ b/drivers/block/ub.c
@@ -2334,7 +2334,6 @@ static int ub_probe_lun(struct ub_dev *sc, int lnum)
disk->major = UB_MAJOR;
disk->first_minor = lun->id * UB_PARTS_PER_LUN;
disk->fops = &ub_bd_fops;
- disk->events = DISK_EVENT_MEDIA_CHANGE;
disk->private_data = lun;
disk->driverfs_dev = &sc->intf->dev;
diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c
index 645ff765cd1..6c7fd7db6df 100644
--- a/drivers/block/xsysace.c
+++ b/drivers/block/xsysace.c
@@ -1005,7 +1005,6 @@ static int __devinit ace_setup(struct ace_device *ace)
ace->gd->major = ace_major;
ace->gd->first_minor = ace->id * ACE_NUM_MINORS;
ace->gd->fops = &ace_fops;
- ace->gd->events = DISK_EVENT_MEDIA_CHANGE;
ace->gd->queue = ace->queue;
ace->gd->private_data = ace;
snprintf(ace->gd->disk_name, 32, "xs%c", ace->id + 'a');
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index 514dd8efaf7..75fb965b8f7 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -986,6 +986,9 @@ int cdrom_open(struct cdrom_device_info *cdi, struct block_device *bdev, fmode_t
cdinfo(CD_OPEN, "entering cdrom_open\n");
+ /* open is event synchronization point, check events first */
+ check_disk_change(bdev);
+
/* if this was a O_NONBLOCK open and we should honor the flags,
* do a quick open without drive/disc integrity checks. */
cdi->use_count++;
@@ -1012,9 +1015,6 @@ int cdrom_open(struct cdrom_device_info *cdi, struct block_device *bdev, fmode_t
cdinfo(CD_OPEN, "Use count for \"/dev/%s\" now %d\n",
cdi->name, cdi->use_count);
- /* Do this on open. Don't wait for mount, because they might
- not be mounting, but opening with O_NONBLOCK */
- check_disk_change(bdev);
return 0;
err_release:
if (CDROM_CAN(CDC_LOCK) && cdi->options & CDO_LOCK) {
diff --git a/drivers/cdrom/gdrom.c b/drivers/cdrom/gdrom.c
index b2b034fea34..3ceaf006e7f 100644
--- a/drivers/cdrom/gdrom.c
+++ b/drivers/cdrom/gdrom.c
@@ -803,7 +803,6 @@ static int __devinit probe_gdrom(struct platform_device *devptr)
goto probe_fail_cdrom_register;
}
gd.disk->fops = &gdrom_bdops;
- gd.disk->events = DISK_EVENT_MEDIA_CHANGE;
/* latch on to the interrupt */
err = gdrom_set_interrupt_handlers();
if (err)
diff --git a/drivers/cdrom/viocd.c b/drivers/cdrom/viocd.c
index 4e874c5fa60..e427fbe4599 100644
--- a/drivers/cdrom/viocd.c
+++ b/drivers/cdrom/viocd.c
@@ -626,7 +626,6 @@ static int viocd_probe(struct vio_dev *vdev, const struct vio_device_id *id)
gendisk->queue = q;
gendisk->fops = &viocd_fops;
gendisk->flags = GENHD_FL_CD|GENHD_FL_REMOVABLE;
- gendisk->events = DISK_EVENT_MEDIA_CHANGE;
set_capacity(gendisk, 0);
gendisk->private_data = d;
d->viocd_disk = gendisk;
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index ad59b4e0a9b..49502bc5360 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -523,7 +523,7 @@ config RAW_DRIVER
with the O_DIRECT flag.
config MAX_RAW_DEVS
- int "Maximum number of RAW devices to support (1-8192)"
+ int "Maximum number of RAW devices to support (1-65536)"
depends on RAW_DRIVER
default "256"
help
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c
index 012cba0d6d9..b072648dc3f 100644
--- a/drivers/char/agp/generic.c
+++ b/drivers/char/agp/generic.c
@@ -115,6 +115,9 @@ static struct agp_memory *agp_create_user_memory(unsigned long num_agp_pages)
struct agp_memory *new;
unsigned long alloc_size = num_agp_pages*sizeof(struct page *);
+ if (INT_MAX/sizeof(struct page *) < num_agp_pages)
+ return NULL;
+
new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL);
if (new == NULL)
return NULL;
@@ -234,11 +237,14 @@ struct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge,
int scratch_pages;
struct agp_memory *new;
size_t i;
+ int cur_memory;
if (!bridge)
return NULL;
- if ((atomic_read(&bridge->current_memory_agp) + page_count) > bridge->max_memory_agp)
+ cur_memory = atomic_read(&bridge->current_memory_agp);
+ if ((cur_memory + page_count > bridge->max_memory_agp) ||
+ (cur_memory + page_count < page_count))
return NULL;
if (type >= AGP_USER_TYPES) {
@@ -1089,8 +1095,8 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type)
return -EINVAL;
}
- /* AK: could wrap */
- if ((pg_start + mem->page_count) > num_entries)
+ if (((pg_start + mem->page_count) > num_entries) ||
+ ((pg_start + mem->page_count) < pg_start))
return -EINVAL;
j = pg_start;
@@ -1124,7 +1130,7 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
{
size_t i;
struct agp_bridge_data *bridge;
- int mask_type;
+ int mask_type, num_entries;
bridge = mem->bridge;
if (!bridge)
@@ -1136,6 +1142,11 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
if (type != mem->type)
return -EINVAL;
+ num_entries = agp_num_entries();
+ if (((pg_start + mem->page_count) > num_entries) ||
+ ((pg_start + mem->page_count) < pg_start))
+ return -EINVAL;
+
mask_type = bridge->driver->agp_type_to_mask_type(bridge, type);
if (mask_type != 0) {
/* The generic routines know nothing of memory types */
diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c
index a4a6c2f044b..cf39bc08ce0 100644
--- a/drivers/char/bsr.c
+++ b/drivers/char/bsr.c
@@ -295,7 +295,7 @@ static int bsr_create_devs(struct device_node *bn)
static int __init bsr_init(void)
{
struct device_node *np;
- dev_t bsr_dev = MKDEV(bsr_major, 0);
+ dev_t bsr_dev;
int ret = -ENODEV;
int result;
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index 7066e801b9d..051474c65b7 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -84,8 +84,6 @@ static struct clocksource clocksource_hpet = {
.rating = 250,
.read = read_hpet,
.mask = CLOCKSOURCE_MASK(64),
- .mult = 0, /* to be calculated */
- .shift = 10,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static struct clocksource *hpet_clocksource;
@@ -934,9 +932,7 @@ int hpet_alloc(struct hpet_data *hdp)
if (!hpet_clocksource) {
hpet_mctr = (void __iomem *)&hpetp->hp_hpet->hpet_mc;
CLKSRC_FSYS_MMIO_SET(clocksource_hpet.fsys_mmio, hpet_mctr);
- clocksource_hpet.mult = clocksource_hz2mult(hpetp->hp_tick_freq,
- clocksource_hpet.shift);
- clocksource_register(&clocksource_hpet);
+ clocksource_register_hz(&clocksource_hpet, hpetp->hp_tick_freq);
hpetp->hp_clocksource = &clocksource_hpet;
hpet_clocksource = &clocksource_hpet;
}
diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c
index 43ac61978d8..ac6739e085e 100644
--- a/drivers/char/hw_random/n2-drv.c
+++ b/drivers/char/hw_random/n2-drv.c
@@ -619,15 +619,18 @@ static void __devinit n2rng_driver_version(void)
pr_info("%s", version);
}
+static const struct of_device_id n2rng_match[];
static int __devinit n2rng_probe(struct platform_device *op)
{
+ const struct of_device_id *match;
int victoria_falls;
int err = -ENOMEM;
struct n2rng *np;
- if (!op->dev.of_match)
+ match = of_match_device(n2rng_match, &op->dev);
+ if (!match)
return -EINVAL;
- victoria_falls = (op->dev.of_match->data != NULL);
+ victoria_falls = (match->data != NULL);
n2rng_driver_version();
np = kzalloc(sizeof(*np), GFP_KERNEL);
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index cc6c9b2546a..64c6b853061 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -2554,9 +2554,11 @@ static struct pci_driver ipmi_pci_driver = {
};
#endif /* CONFIG_PCI */
+static struct of_device_id ipmi_match[];
static int __devinit ipmi_probe(struct platform_device *dev)
{
#ifdef CONFIG_OF
+ const struct of_device_id *match;
struct smi_info *info;
struct resource resource;
const __be32 *regsize, *regspacing, *regshift;
@@ -2566,7 +2568,8 @@ static int __devinit ipmi_probe(struct platform_device *dev)
dev_info(&dev->dev, "probing via device tree\n");
- if (!dev->dev.of_match)
+ match = of_match_device(ipmi_match, &dev->dev);
+ if (!match)
return -EINVAL;
ret = of_address_to_resource(np, 0, &resource);
@@ -2601,7 +2604,7 @@ static int __devinit ipmi_probe(struct platform_device *dev)
return -ENOMEM;
}
- info->si_type = (enum si_type) dev->dev.of_match->data;
+ info->si_type = (enum si_type) match->data;
info->addr_source = SI_DEVICETREE;
info->irq_setup = std_irq_setup;
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 436a9901799..8fc04b4f311 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -806,29 +806,41 @@ static const struct file_operations oldmem_fops = {
};
#endif
-static ssize_t kmsg_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
+static ssize_t kmsg_writev(struct kiocb *iocb, const struct iovec *iv,
+ unsigned long count, loff_t pos)
{
- char *tmp;
- ssize_t ret;
+ char *line, *p;
+ int i;
+ ssize_t ret = -EFAULT;
+ size_t len = iov_length(iv, count);
- tmp = kmalloc(count + 1, GFP_KERNEL);
- if (tmp == NULL)
+ line = kmalloc(len + 1, GFP_KERNEL);
+ if (line == NULL)
return -ENOMEM;
- ret = -EFAULT;
- if (!copy_from_user(tmp, buf, count)) {
- tmp[count] = 0;
- ret = printk("%s", tmp);
- if (ret > count)
- /* printk can add a prefix */
- ret = count;
+
+ /*
+ * copy all vectors into a single string, to ensure we do
+ * not interleave our log line with other printk calls
+ */
+ p = line;
+ for (i = 0; i < count; i++) {
+ if (copy_from_user(p, iv[i].iov_base, iv[i].iov_len))
+ goto out;
+ p += iv[i].iov_len;
}
- kfree(tmp);
+ p[0] = '\0';
+
+ ret = printk("%s", line);
+ /* printk can add a prefix */
+ if (ret > len)
+ ret = len;
+out:
+ kfree(line);
return ret;
}
static const struct file_operations kmsg_fops = {
- .write = kmsg_write,
+ .aio_write = kmsg_writev,
.llseek = noop_llseek,
};
diff --git a/drivers/char/raw.c b/drivers/char/raw.c
index b4b9d5a4788..b33e8ea314e 100644
--- a/drivers/char/raw.c
+++ b/drivers/char/raw.c
@@ -21,6 +21,7 @@
#include <linux/mutex.h>
#include <linux/gfp.h>
#include <linux/compat.h>
+#include <linux/vmalloc.h>
#include <asm/uaccess.h>
@@ -30,10 +31,15 @@ struct raw_device_data {
};
static struct class *raw_class;
-static struct raw_device_data raw_devices[MAX_RAW_MINORS];
+static struct raw_device_data *raw_devices;
static DEFINE_MUTEX(raw_mutex);
static const struct file_operations raw_ctl_fops; /* forward declaration */
+static int max_raw_minors = MAX_RAW_MINORS;
+
+module_param(max_raw_minors, int, 0);
+MODULE_PARM_DESC(max_raw_minors, "Maximum number of raw devices (1-65536)");
+
/*
* Open/close code for raw IO.
*
@@ -125,7 +131,7 @@ static int bind_set(int number, u64 major, u64 minor)
struct raw_device_data *rawdev;
int err = 0;
- if (number <= 0 || number >= MAX_RAW_MINORS)
+ if (number <= 0 || number >= max_raw_minors)
return -EINVAL;
if (MAJOR(dev) != major || MINOR(dev) != minor)
@@ -312,14 +318,27 @@ static int __init raw_init(void)
dev_t dev = MKDEV(RAW_MAJOR, 0);
int ret;
- ret = register_chrdev_region(dev, MAX_RAW_MINORS, "raw");
+ if (max_raw_minors < 1 || max_raw_minors > 65536) {
+ printk(KERN_WARNING "raw: invalid max_raw_minors (must be"
+ " between 1 and 65536), using %d\n", MAX_RAW_MINORS);
+ max_raw_minors = MAX_RAW_MINORS;
+ }
+
+ raw_devices = vmalloc(sizeof(struct raw_device_data) * max_raw_minors);
+ if (!raw_devices) {
+ printk(KERN_ERR "Not enough memory for raw device structures\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+ memset(raw_devices, 0, sizeof(struct raw_device_data) * max_raw_minors);
+
+ ret = register_chrdev_region(dev, max_raw_minors, "raw");
if (ret)
goto error;
cdev_init(&raw_cdev, &raw_fops);
- ret = cdev_add(&raw_cdev, dev, MAX_RAW_MINORS);
+ ret = cdev_add(&raw_cdev, dev, max_raw_minors);
if (ret) {
- kobject_put(&raw_cdev.kobj);
goto error_region;
}
@@ -336,8 +355,9 @@ static int __init raw_init(void)
return 0;
error_region:
- unregister_chrdev_region(dev, MAX_RAW_MINORS);
+ unregister_chrdev_region(dev, max_raw_minors);
error:
+ vfree(raw_devices);
return ret;
}
@@ -346,7 +366,7 @@ static void __exit raw_exit(void)
device_destroy(raw_class, MKDEV(RAW_MAJOR, 0));
class_destroy(raw_class);
cdev_del(&raw_cdev);
- unregister_chrdev_region(MKDEV(RAW_MAJOR, 0), MAX_RAW_MINORS);
+ unregister_chrdev_region(MKDEV(RAW_MAJOR, 0), max_raw_minors);
}
module_init(raw_init);
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 84b164d1eb2..838568a7dbf 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -1280,18 +1280,7 @@ static void unplug_port(struct port *port)
spin_lock_irq(&pdrvdata_lock);
list_del(&port->cons.list);
spin_unlock_irq(&pdrvdata_lock);
-#if 0
- /*
- * hvc_remove() not called as removing one hvc port
- * results in other hvc ports getting frozen.
- *
- * Once this is resolved in hvc, this functionality
- * will be enabled. Till that is done, the -EPIPE
- * return from get_chars() above will help
- * hvc_console.c to clean up on ports we remove here.
- */
hvc_remove(port->cons.hvc);
-#endif
}
/* Remove unused data this port might have received. */
diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
index d6412c16385..39ccdeada79 100644
--- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c
+++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
@@ -715,13 +715,13 @@ static int __devexit hwicap_remove(struct device *dev)
}
#ifdef CONFIG_OF
-static int __devinit hwicap_of_probe(struct platform_device *op)
+static int __devinit hwicap_of_probe(struct platform_device *op,
+ const struct hwicap_driver_config *config)
{
struct resource res;
const unsigned int *id;
const char *family;
int rc;
- const struct hwicap_driver_config *config = op->dev.of_match->data;
const struct config_registers *regs;
@@ -751,20 +751,24 @@ static int __devinit hwicap_of_probe(struct platform_device *op)
regs);
}
#else
-static inline int hwicap_of_probe(struct platform_device *op)
+static inline int hwicap_of_probe(struct platform_device *op,
+ const struct hwicap_driver_config *config)
{
return -EINVAL;
}
#endif /* CONFIG_OF */
+static const struct of_device_id __devinitconst hwicap_of_match[];
static int __devinit hwicap_drv_probe(struct platform_device *pdev)
{
+ const struct of_device_id *match;
struct resource *res;
const struct config_registers *regs;
const char *family;
- if (pdev->dev.of_match)
- return hwicap_of_probe(pdev);
+ match = of_match_device(hwicap_of_match, &pdev->dev);
+ if (match)
+ return hwicap_of_probe(pdev, match->data);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index 0fc0a79852d..6db161f64ae 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -32,10 +32,9 @@ static DEFINE_MUTEX(clocks_mutex);
* Then we take the most specific entry - with the following
* order of precedence: dev+con > dev only > con only.
*/
-static struct clk *clk_find(const char *dev_id, const char *con_id)
+static struct clk_lookup *clk_find(const char *dev_id, const char *con_id)
{
- struct clk_lookup *p;
- struct clk *clk = NULL;
+ struct clk_lookup *p, *cl = NULL;
int match, best = 0;
list_for_each_entry(p, &clocks, node) {
@@ -52,27 +51,27 @@ static struct clk *clk_find(const char *dev_id, const char *con_id)
}
if (match > best) {
- clk = p->clk;
+ cl = p;
if (match != 3)
best = match;
else
break;
}
}
- return clk;
+ return cl;
}
struct clk *clk_get_sys(const char *dev_id, const char *con_id)
{
- struct clk *clk;
+ struct clk_lookup *cl;
mutex_lock(&clocks_mutex);
- clk = clk_find(dev_id, con_id);
- if (clk && !__clk_get(clk))
- clk = NULL;
+ cl = clk_find(dev_id, con_id);
+ if (cl && !__clk_get(cl->clk))
+ cl = NULL;
mutex_unlock(&clocks_mutex);
- return clk ? clk : ERR_PTR(-ENOENT);
+ return cl ? cl->clk : ERR_PTR(-ENOENT);
}
EXPORT_SYMBOL(clk_get_sys);
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
new file mode 100644
index 00000000000..110aeeb52f9
--- /dev/null
+++ b/drivers/clocksource/Kconfig
@@ -0,0 +1,2 @@
+config CLKSRC_I8253
+ bool
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index be61ece6330..cfb6383b543 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_CS5535_CLOCK_EVENT_SRC) += cs5535-clockevt.o
obj-$(CONFIG_SH_TIMER_CMT) += sh_cmt.o
obj-$(CONFIG_SH_TIMER_MTU2) += sh_mtu2.o
obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o
+obj-$(CONFIG_CLKSRC_I8253) += i8253.o
diff --git a/drivers/clocksource/cyclone.c b/drivers/clocksource/cyclone.c
index 64e528e8bfa..72f811f73e9 100644
--- a/drivers/clocksource/cyclone.c
+++ b/drivers/clocksource/cyclone.c
@@ -29,8 +29,6 @@ static struct clocksource clocksource_cyclone = {
.rating = 250,
.read = read_cyclone,
.mask = CYCLONE_TIMER_MASK,
- .mult = 10,
- .shift = 0,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
@@ -108,12 +106,8 @@ static int __init init_cyclone_clocksource(void)
}
cyclone_ptr = cyclone_timer;
- /* sort out mult/shift values: */
- clocksource_cyclone.shift = 22;
- clocksource_cyclone.mult = clocksource_hz2mult(CYCLONE_TIMER_FREQ,
- clocksource_cyclone.shift);
-
- return clocksource_register(&clocksource_cyclone);
+ return clocksource_register_hz(&clocksource_cyclone,
+ CYCLONE_TIMER_FREQ);
}
arch_initcall(init_cyclone_clocksource);
diff --git a/drivers/clocksource/i8253.c b/drivers/clocksource/i8253.c
new file mode 100644
index 00000000000..225c1761b37
--- /dev/null
+++ b/drivers/clocksource/i8253.c
@@ -0,0 +1,88 @@
+/*
+ * i8253 PIT clocksource
+ */
+#include <linux/clocksource.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/timex.h>
+
+#include <asm/i8253.h>
+
+/*
+ * Since the PIT overflows every tick, its not very useful
+ * to just read by itself. So use jiffies to emulate a free
+ * running counter:
+ */
+static cycle_t i8253_read(struct clocksource *cs)
+{
+ static int old_count;
+ static u32 old_jifs;
+ unsigned long flags;
+ int count;
+ u32 jifs;
+
+ raw_spin_lock_irqsave(&i8253_lock, flags);
+ /*
+ * Although our caller may have the read side of xtime_lock,
+ * this is now a seqlock, and we are cheating in this routine
+ * by having side effects on state that we cannot undo if
+ * there is a collision on the seqlock and our caller has to
+ * retry. (Namely, old_jifs and old_count.) So we must treat
+ * jiffies as volatile despite the lock. We read jiffies
+ * before latching the timer count to guarantee that although
+ * the jiffies value might be older than the count (that is,
+ * the counter may underflow between the last point where
+ * jiffies was incremented and the point where we latch the
+ * count), it cannot be newer.
+ */
+ jifs = jiffies;
+ outb_pit(0x00, PIT_MODE); /* latch the count ASAP */
+ count = inb_pit(PIT_CH0); /* read the latched count */
+ count |= inb_pit(PIT_CH0) << 8;
+
+ /* VIA686a test code... reset the latch if count > max + 1 */
+ if (count > LATCH) {
+ outb_pit(0x34, PIT_MODE);
+ outb_pit(PIT_LATCH & 0xff, PIT_CH0);
+ outb_pit(PIT_LATCH >> 8, PIT_CH0);
+ count = PIT_LATCH - 1;
+ }
+
+ /*
+ * It's possible for count to appear to go the wrong way for a
+ * couple of reasons:
+ *
+ * 1. The timer counter underflows, but we haven't handled the
+ * resulting interrupt and incremented jiffies yet.
+ * 2. Hardware problem with the timer, not giving us continuous time,
+ * the counter does small "jumps" upwards on some Pentium systems,
+ * (see c't 95/10 page 335 for Neptun bug.)
+ *
+ * Previous attempts to handle these cases intelligently were
+ * buggy, so we just do the simple thing now.
+ */
+ if (count > old_count && jifs == old_jifs)
+ count = old_count;
+
+ old_count = count;
+ old_jifs = jifs;
+
+ raw_spin_unlock_irqrestore(&i8253_lock, flags);
+
+ count = (PIT_LATCH - 1) - count;
+
+ return (cycle_t)(jifs * PIT_LATCH) + count;
+}
+
+static struct clocksource i8253_cs = {
+ .name = "pit",
+ .rating = 110,
+ .read = i8253_read,
+ .mask = CLOCKSOURCE_MASK(32),
+};
+
+int __init clocksource_i8253_init(void)
+{
+ return clocksource_register_hz(&i8253_cs, PIT_TICK_RATE);
+}
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index ca8ee8093d6..9fb84853d8e 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -1,3 +1,5 @@
+menu "CPU Frequency scaling"
+
config CPU_FREQ
bool "CPU Frequency scaling"
help
@@ -18,19 +20,6 @@ if CPU_FREQ
config CPU_FREQ_TABLE
tristate
-config CPU_FREQ_DEBUG
- bool "Enable CPUfreq debugging"
- help
- Say Y here to enable CPUfreq subsystem (including drivers)
- debugging. You will need to activate it via the kernel
- command line by passing
- cpufreq.debug=<value>
-
- To get <value>, add
- 1 to activate CPUfreq core debugging,
- 2 to activate CPUfreq drivers debugging, and
- 4 to activate CPUfreq governor debugging
-
config CPU_FREQ_STAT
tristate "CPU frequency translation statistics"
select CPU_FREQ_TABLE
@@ -190,4 +179,10 @@ config CPU_FREQ_GOV_CONSERVATIVE
If in doubt, say N.
-endif # CPU_FREQ
+menu "x86 CPU frequency scaling drivers"
+depends on X86
+source "drivers/cpufreq/Kconfig.x86"
+endmenu
+
+endif
+endmenu
diff --git a/drivers/cpufreq/Kconfig.x86 b/drivers/cpufreq/Kconfig.x86
new file mode 100644
index 00000000000..78ff7ee4895
--- /dev/null
+++ b/drivers/cpufreq/Kconfig.x86
@@ -0,0 +1,255 @@
+#
+# x86 CPU Frequency scaling drivers
+#
+
+config X86_PCC_CPUFREQ
+ tristate "Processor Clocking Control interface driver"
+ depends on ACPI && ACPI_PROCESSOR
+ help
+ This driver adds support for the PCC interface.
+
+ For details, take a look at:
+ <file:Documentation/cpu-freq/pcc-cpufreq.txt>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pcc-cpufreq.
+
+ If in doubt, say N.
+
+config X86_ACPI_CPUFREQ
+ tristate "ACPI Processor P-States driver"
+ select CPU_FREQ_TABLE
+ depends on ACPI_PROCESSOR
+ help
+ This driver adds a CPUFreq driver which utilizes the ACPI
+ Processor Performance States.
+ This driver also supports Intel Enhanced Speedstep.
+
+ To compile this driver as a module, choose M here: the
+ module will be called acpi-cpufreq.
+
+ For details, take a look at <file:Documentation/cpu-freq/>.
+
+ If in doubt, say N.
+
+config ELAN_CPUFREQ
+ tristate "AMD Elan SC400 and SC410"
+ select CPU_FREQ_TABLE
+ depends on MELAN
+ ---help---
+ This adds the CPUFreq driver for AMD Elan SC400 and SC410
+ processors.
+
+ You need to specify the processor maximum speed as boot
+ parameter: elanfreq=maxspeed (in kHz) or as module
+ parameter "max_freq".
+
+ For details, take a look at <file:Documentation/cpu-freq/>.
+
+ If in doubt, say N.
+
+config SC520_CPUFREQ
+ tristate "AMD Elan SC520"
+ select CPU_FREQ_TABLE
+ depends on MELAN
+ ---help---
+ This adds the CPUFreq driver for AMD Elan SC520 processor.
+
+ For details, take a look at <file:Documentation/cpu-freq/>.
+
+ If in doubt, say N.
+
+
+config X86_POWERNOW_K6
+ tristate "AMD Mobile K6-2/K6-3 PowerNow!"
+ select CPU_FREQ_TABLE
+ depends on X86_32
+ help
+ This adds the CPUFreq driver for mobile AMD K6-2+ and mobile
+ AMD K6-3+ processors.
+
+ For details, take a look at <file:Documentation/cpu-freq/>.
+
+ If in doubt, say N.
+
+config X86_POWERNOW_K7
+ tristate "AMD Mobile Athlon/Duron PowerNow!"
+ select CPU_FREQ_TABLE
+ depends on X86_32
+ help
+ This adds the CPUFreq driver for mobile AMD K7 mobile processors.
+
+ For details, take a look at <file:Documentation/cpu-freq/>.
+
+ If in doubt, say N.
+
+config X86_POWERNOW_K7_ACPI
+ bool
+ depends on X86_POWERNOW_K7 && ACPI_PROCESSOR
+ depends on !(X86_POWERNOW_K7 = y && ACPI_PROCESSOR = m)
+ depends on X86_32
+ default y
+
+config X86_POWERNOW_K8
+ tristate "AMD Opteron/Athlon64 PowerNow!"
+ select CPU_FREQ_TABLE
+ depends on ACPI && ACPI_PROCESSOR
+ help
+ This adds the CPUFreq driver for K8/K10 Opteron/Athlon64 processors.
+
+ To compile this driver as a module, choose M here: the
+ module will be called powernow-k8.
+
+ For details, take a look at <file:Documentation/cpu-freq/>.
+
+config X86_GX_SUSPMOD
+ tristate "Cyrix MediaGX/NatSemi Geode Suspend Modulation"
+ depends on X86_32 && PCI
+ help
+ This add the CPUFreq driver for NatSemi Geode processors which
+ support suspend modulation.
+
+ For details, take a look at <file:Documentation/cpu-freq/>.
+
+ If in doubt, say N.
+
+config X86_SPEEDSTEP_CENTRINO
+ tristate "Intel Enhanced SpeedStep (deprecated)"
+ select CPU_FREQ_TABLE
+ select X86_SPEEDSTEP_CENTRINO_TABLE if X86_32
+ depends on X86_32 || (X86_64 && ACPI_PROCESSOR)
+ help
+ This is deprecated and this functionality is now merged into
+ acpi_cpufreq (X86_ACPI_CPUFREQ). Use that driver instead of
+ speedstep_centrino.
+ This adds the CPUFreq driver for Enhanced SpeedStep enabled
+ mobile CPUs. This means Intel Pentium M (Centrino) CPUs
+ or 64bit enabled Intel Xeons.
+
+ To compile this driver as a module, choose M here: the
+ module will be called speedstep-centrino.
+
+ For details, take a look at <file:Documentation/cpu-freq/>.
+
+ If in doubt, say N.
+
+config X86_SPEEDSTEP_CENTRINO_TABLE
+ bool "Built-in tables for Banias CPUs"
+ depends on X86_32 && X86_SPEEDSTEP_CENTRINO
+ default y
+ help
+ Use built-in tables for Banias CPUs if ACPI encoding
+ is not available.
+
+ If in doubt, say N.
+
+config X86_SPEEDSTEP_ICH
+ tristate "Intel Speedstep on ICH-M chipsets (ioport interface)"
+ select CPU_FREQ_TABLE
+ depends on X86_32
+ help
+ This adds the CPUFreq driver for certain mobile Intel Pentium III
+ (Coppermine), all mobile Intel Pentium III-M (Tualatin) and all
+ mobile Intel Pentium 4 P4-M on systems which have an Intel ICH2,
+ ICH3 or ICH4 southbridge.
+
+ For details, take a look at <file:Documentation/cpu-freq/>.
+
+ If in doubt, say N.
+
+config X86_SPEEDSTEP_SMI
+ tristate "Intel SpeedStep on 440BX/ZX/MX chipsets (SMI interface)"
+ select CPU_FREQ_TABLE
+ depends on X86_32 && EXPERIMENTAL
+ help
+ This adds the CPUFreq driver for certain mobile Intel Pentium III
+ (Coppermine), all mobile Intel Pentium III-M (Tualatin)
+ on systems which have an Intel 440BX/ZX/MX southbridge.
+
+ For details, take a look at <file:Documentation/cpu-freq/>.
+
+ If in doubt, say N.
+
+config X86_P4_CLOCKMOD
+ tristate "Intel Pentium 4 clock modulation"
+ select CPU_FREQ_TABLE
+ help
+ This adds the CPUFreq driver for Intel Pentium 4 / XEON
+ processors. When enabled it will lower CPU temperature by skipping
+ clocks.
+
+ This driver should be only used in exceptional
+ circumstances when very low power is needed because it causes severe
+ slowdowns and noticeable latencies. Normally Speedstep should be used
+ instead.
+
+ To compile this driver as a module, choose M here: the
+ module will be called p4-clockmod.
+
+ For details, take a look at <file:Documentation/cpu-freq/>.
+
+ Unless you are absolutely sure say N.
+
+config X86_CPUFREQ_NFORCE2
+ tristate "nVidia nForce2 FSB changing"
+ depends on X86_32 && EXPERIMENTAL
+ help
+ This adds the CPUFreq driver for FSB changing on nVidia nForce2
+ platforms.
+
+ For details, take a look at <file:Documentation/cpu-freq/>.
+
+ If in doubt, say N.
+
+config X86_LONGRUN
+ tristate "Transmeta LongRun"
+ depends on X86_32
+ help
+ This adds the CPUFreq driver for Transmeta Crusoe and Efficeon processors
+ which support LongRun.
+
+ For details, take a look at <file:Documentation/cpu-freq/>.
+
+ If in doubt, say N.
+
+config X86_LONGHAUL
+ tristate "VIA Cyrix III Longhaul"
+ select CPU_FREQ_TABLE
+ depends on X86_32 && ACPI_PROCESSOR
+ help
+ This adds the CPUFreq driver for VIA Samuel/CyrixIII,
+ VIA Cyrix Samuel/C3, VIA Cyrix Ezra and VIA Cyrix Ezra-T
+ processors.
+
+ For details, take a look at <file:Documentation/cpu-freq/>.
+
+ If in doubt, say N.
+
+config X86_E_POWERSAVER
+ tristate "VIA C7 Enhanced PowerSaver (DANGEROUS)"
+ select CPU_FREQ_TABLE
+ depends on X86_32 && EXPERIMENTAL
+ help
+ This adds the CPUFreq driver for VIA C7 processors. However, this driver
+ does not have any safeguards to prevent operating the CPU out of spec
+ and is thus considered dangerous. Please use the regular ACPI cpufreq
+ driver, enabled by CONFIG_X86_ACPI_CPUFREQ.
+
+ If in doubt, say N.
+
+comment "shared options"
+
+config X86_SPEEDSTEP_LIB
+ tristate
+ default (X86_SPEEDSTEP_ICH || X86_SPEEDSTEP_SMI || X86_P4_CLOCKMOD)
+
+config X86_SPEEDSTEP_RELAXED_CAP_CHECK
+ bool "Relaxed speedstep capability checks"
+ depends on X86_32 && (X86_SPEEDSTEP_SMI || X86_SPEEDSTEP_ICH)
+ help
+ Don't perform all checks for a speedstep capable system which would
+ normally be done. Some ancient or strange systems, though speedstep
+ capable, don't always indicate that they are speedstep capable. This
+ option lets the probing code bypass some of those checks if the
+ parameter "relaxed_check=1" is passed to the module.
+
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 71fc3b4173f..c7f1a6f16b6 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -13,3 +13,29 @@ obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o
# CPUfreq cross-arch helpers
obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o
+##################################################################################d
+# x86 drivers.
+# Link order matters. K8 is preferred to ACPI because of firmware bugs in early
+# K8 systems. ACPI is preferred to all other hardware-specific drivers.
+# speedstep-* is preferred over p4-clockmod.
+
+obj-$(CONFIG_X86_POWERNOW_K8) += powernow-k8.o mperf.o
+obj-$(CONFIG_X86_ACPI_CPUFREQ) += acpi-cpufreq.o mperf.o
+obj-$(CONFIG_X86_PCC_CPUFREQ) += pcc-cpufreq.o
+obj-$(CONFIG_X86_POWERNOW_K6) += powernow-k6.o
+obj-$(CONFIG_X86_POWERNOW_K7) += powernow-k7.o
+obj-$(CONFIG_X86_LONGHAUL) += longhaul.o
+obj-$(CONFIG_X86_E_POWERSAVER) += e_powersaver.o
+obj-$(CONFIG_ELAN_CPUFREQ) += elanfreq.o
+obj-$(CONFIG_SC520_CPUFREQ) += sc520_freq.o
+obj-$(CONFIG_X86_LONGRUN) += longrun.o
+obj-$(CONFIG_X86_GX_SUSPMOD) += gx-suspmod.o
+obj-$(CONFIG_X86_SPEEDSTEP_ICH) += speedstep-ich.o
+obj-$(CONFIG_X86_SPEEDSTEP_LIB) += speedstep-lib.o
+obj-$(CONFIG_X86_SPEEDSTEP_SMI) += speedstep-smi.o
+obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO) += speedstep-centrino.o
+obj-$(CONFIG_X86_P4_CLOCKMOD) += p4-clockmod.o
+obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o
+
+##################################################################################d
+
diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
new file mode 100644
index 00000000000..4e04e127438
--- /dev/null
+++ b/drivers/cpufreq/acpi-cpufreq.c
@@ -0,0 +1,773 @@
+/*
+ * acpi-cpufreq.c - ACPI Processor P-States Driver
+ *
+ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ * Copyright (C) 2002 - 2004 Dominik Brodowski <linux@brodo.de>
+ * Copyright (C) 2006 Denis Sadykov <denis.m.sadykov@intel.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/module.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/sched.h>
+#include <linux/cpufreq.h>
+#include <linux/compiler.h>
+#include <linux/dmi.h>
+#include <linux/slab.h>
+
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+
+#include <acpi/processor.h>
+
+#include <asm/msr.h>
+#include <asm/processor.h>
+#include <asm/cpufeature.h>
+#include "mperf.h"
+
+MODULE_AUTHOR("Paul Diefenbaugh, Dominik Brodowski");
+MODULE_DESCRIPTION("ACPI Processor P-States Driver");
+MODULE_LICENSE("GPL");
+
+enum {
+ UNDEFINED_CAPABLE = 0,
+ SYSTEM_INTEL_MSR_CAPABLE,
+ SYSTEM_IO_CAPABLE,
+};
+
+#define INTEL_MSR_RANGE (0xffff)
+
+struct acpi_cpufreq_data {
+ struct acpi_processor_performance *acpi_data;
+ struct cpufreq_frequency_table *freq_table;
+ unsigned int resume;
+ unsigned int cpu_feature;
+};
+
+static DEFINE_PER_CPU(struct acpi_cpufreq_data *, acfreq_data);
+
+/* acpi_perf_data is a pointer to percpu data. */
+static struct acpi_processor_performance __percpu *acpi_perf_data;
+
+static struct cpufreq_driver acpi_cpufreq_driver;
+
+static unsigned int acpi_pstate_strict;
+
+static int check_est_cpu(unsigned int cpuid)
+{
+ struct cpuinfo_x86 *cpu = &cpu_data(cpuid);
+
+ return cpu_has(cpu, X86_FEATURE_EST);
+}
+
+static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data)
+{
+ struct acpi_processor_performance *perf;
+ int i;
+
+ perf = data->acpi_data;
+
+ for (i = 0; i < perf->state_count; i++) {
+ if (value == perf->states[i].status)
+ return data->freq_table[i].frequency;
+ }
+ return 0;
+}
+
+static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data)
+{
+ int i;
+ struct acpi_processor_performance *perf;
+
+ msr &= INTEL_MSR_RANGE;
+ perf = data->acpi_data;
+
+ for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
+ if (msr == perf->states[data->freq_table[i].index].status)
+ return data->freq_table[i].frequency;
+ }
+ return data->freq_table[0].frequency;
+}
+
+static unsigned extract_freq(u32 val, struct acpi_cpufreq_data *data)
+{
+ switch (data->cpu_feature) {
+ case SYSTEM_INTEL_MSR_CAPABLE:
+ return extract_msr(val, data);
+ case SYSTEM_IO_CAPABLE:
+ return extract_io(val, data);
+ default:
+ return 0;
+ }
+}
+
+struct msr_addr {
+ u32 reg;
+};
+
+struct io_addr {
+ u16 port;
+ u8 bit_width;
+};
+
+struct drv_cmd {
+ unsigned int type;
+ const struct cpumask *mask;
+ union {
+ struct msr_addr msr;
+ struct io_addr io;
+ } addr;
+ u32 val;
+};
+
+/* Called via smp_call_function_single(), on the target CPU */
+static void do_drv_read(void *_cmd)
+{
+ struct drv_cmd *cmd = _cmd;
+ u32 h;
+
+ switch (cmd->type) {
+ case SYSTEM_INTEL_MSR_CAPABLE:
+ rdmsr(cmd->addr.msr.reg, cmd->val, h);
+ break;
+ case SYSTEM_IO_CAPABLE:
+ acpi_os_read_port((acpi_io_address)cmd->addr.io.port,
+ &cmd->val,
+ (u32)cmd->addr.io.bit_width);
+ break;
+ default:
+ break;
+ }
+}
+
+/* Called via smp_call_function_many(), on the target CPUs */
+static void do_drv_write(void *_cmd)
+{
+ struct drv_cmd *cmd = _cmd;
+ u32 lo, hi;
+
+ switch (cmd->type) {
+ case SYSTEM_INTEL_MSR_CAPABLE:
+ rdmsr(cmd->addr.msr.reg, lo, hi);
+ lo = (lo & ~INTEL_MSR_RANGE) | (cmd->val & INTEL_MSR_RANGE);
+ wrmsr(cmd->addr.msr.reg, lo, hi);
+ break;
+ case SYSTEM_IO_CAPABLE:
+ acpi_os_write_port((acpi_io_address)cmd->addr.io.port,
+ cmd->val,
+ (u32)cmd->addr.io.bit_width);
+ break;
+ default:
+ break;
+ }
+}
+
+static void drv_read(struct drv_cmd *cmd)
+{
+ int err;
+ cmd->val = 0;
+
+ err = smp_call_function_any(cmd->mask, do_drv_read, cmd, 1);
+ WARN_ON_ONCE(err); /* smp_call_function_any() was buggy? */
+}
+
+static void drv_write(struct drv_cmd *cmd)
+{
+ int this_cpu;
+
+ this_cpu = get_cpu();
+ if (cpumask_test_cpu(this_cpu, cmd->mask))
+ do_drv_write(cmd);
+ smp_call_function_many(cmd->mask, do_drv_write, cmd, 1);
+ put_cpu();
+}
+
+static u32 get_cur_val(const struct cpumask *mask)
+{
+ struct acpi_processor_performance *perf;
+ struct drv_cmd cmd;
+
+ if (unlikely(cpumask_empty(mask)))
+ return 0;
+
+ switch (per_cpu(acfreq_data, cpumask_first(mask))->cpu_feature) {
+ case SYSTEM_INTEL_MSR_CAPABLE:
+ cmd.type = SYSTEM_INTEL_MSR_CAPABLE;
+ cmd.addr.msr.reg = MSR_IA32_PERF_STATUS;
+ break;
+ case SYSTEM_IO_CAPABLE:
+ cmd.type = SYSTEM_IO_CAPABLE;
+ perf = per_cpu(acfreq_data, cpumask_first(mask))->acpi_data;
+ cmd.addr.io.port = perf->control_register.address;
+ cmd.addr.io.bit_width = perf->control_register.bit_width;
+ break;
+ default:
+ return 0;
+ }
+
+ cmd.mask = mask;
+ drv_read(&cmd);
+
+ pr_debug("get_cur_val = %u\n", cmd.val);
+
+ return cmd.val;
+}
+
+static unsigned int get_cur_freq_on_cpu(unsigned int cpu)
+{
+ struct acpi_cpufreq_data *data = per_cpu(acfreq_data, cpu);
+ unsigned int freq;
+ unsigned int cached_freq;
+
+ pr_debug("get_cur_freq_on_cpu (%d)\n", cpu);
+
+ if (unlikely(data == NULL ||
+ data->acpi_data == NULL || data->freq_table == NULL)) {
+ return 0;
+ }
+
+ cached_freq = data->freq_table[data->acpi_data->state].frequency;
+ freq = extract_freq(get_cur_val(cpumask_of(cpu)), data);
+ if (freq != cached_freq) {
+ /*
+ * The dreaded BIOS frequency change behind our back.
+ * Force set the frequency on next target call.
+ */
+ data->resume = 1;
+ }
+
+ pr_debug("cur freq = %u\n", freq);
+
+ return freq;
+}
+
+static unsigned int check_freqs(const struct cpumask *mask, unsigned int freq,
+ struct acpi_cpufreq_data *data)
+{
+ unsigned int cur_freq;
+ unsigned int i;
+
+ for (i = 0; i < 100; i++) {
+ cur_freq = extract_freq(get_cur_val(mask), data);
+ if (cur_freq == freq)
+ return 1;
+ udelay(10);
+ }
+ return 0;
+}
+
+static int acpi_cpufreq_target(struct cpufreq_policy *policy,
+ unsigned int target_freq, unsigned int relation)
+{
+ struct acpi_cpufreq_data *data = per_cpu(acfreq_data, policy->cpu);
+ struct acpi_processor_performance *perf;
+ struct cpufreq_freqs freqs;
+ struct drv_cmd cmd;
+ unsigned int next_state = 0; /* Index into freq_table */
+ unsigned int next_perf_state = 0; /* Index into perf table */
+ unsigned int i;
+ int result = 0;
+
+ pr_debug("acpi_cpufreq_target %d (%d)\n", target_freq, policy->cpu);
+
+ if (unlikely(data == NULL ||
+ data->acpi_data == NULL || data->freq_table == NULL)) {
+ return -ENODEV;
+ }
+
+ perf = data->acpi_data;
+ result = cpufreq_frequency_table_target(policy,
+ data->freq_table,
+ target_freq,
+ relation, &next_state);
+ if (unlikely(result)) {
+ result = -ENODEV;
+ goto out;
+ }
+
+ next_perf_state = data->freq_table[next_state].index;
+ if (perf->state == next_perf_state) {
+ if (unlikely(data->resume)) {
+ pr_debug("Called after resume, resetting to P%d\n",
+ next_perf_state);
+ data->resume = 0;
+ } else {
+ pr_debug("Already at target state (P%d)\n",
+ next_perf_state);
+ goto out;
+ }
+ }
+
+ switch (data->cpu_feature) {
+ case SYSTEM_INTEL_MSR_CAPABLE:
+ cmd.type = SYSTEM_INTEL_MSR_CAPABLE;
+ cmd.addr.msr.reg = MSR_IA32_PERF_CTL;
+ cmd.val = (u32) perf->states[next_perf_state].control;
+ break;
+ case SYSTEM_IO_CAPABLE:
+ cmd.type = SYSTEM_IO_CAPABLE;
+ cmd.addr.io.port = perf->control_register.address;
+ cmd.addr.io.bit_width = perf->control_register.bit_width;
+ cmd.val = (u32) perf->states[next_perf_state].control;
+ break;
+ default:
+ result = -ENODEV;
+ goto out;
+ }
+
+ /* cpufreq holds the hotplug lock, so we are safe from here on */
+ if (policy->shared_type != CPUFREQ_SHARED_TYPE_ANY)
+ cmd.mask = policy->cpus;
+ else
+ cmd.mask = cpumask_of(policy->cpu);
+
+ freqs.old = perf->states[perf->state].core_frequency * 1000;
+ freqs.new = data->freq_table[next_state].frequency;
+ for_each_cpu(i, policy->cpus) {
+ freqs.cpu = i;
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ }
+
+ drv_write(&cmd);
+
+ if (acpi_pstate_strict) {
+ if (!check_freqs(cmd.mask, freqs.new, data)) {
+ pr_debug("acpi_cpufreq_target failed (%d)\n",
+ policy->cpu);
+ result = -EAGAIN;
+ goto out;
+ }
+ }
+
+ for_each_cpu(i, policy->cpus) {
+ freqs.cpu = i;
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
+ perf->state = next_perf_state;
+
+out:
+ return result;
+}
+
+static int acpi_cpufreq_verify(struct cpufreq_policy *policy)
+{
+ struct acpi_cpufreq_data *data = per_cpu(acfreq_data, policy->cpu);
+
+ pr_debug("acpi_cpufreq_verify\n");
+
+ return cpufreq_frequency_table_verify(policy, data->freq_table);
+}
+
+static unsigned long
+acpi_cpufreq_guess_freq(struct acpi_cpufreq_data *data, unsigned int cpu)
+{
+ struct acpi_processor_performance *perf = data->acpi_data;
+
+ if (cpu_khz) {
+ /* search the closest match to cpu_khz */
+ unsigned int i;
+ unsigned long freq;
+ unsigned long freqn = perf->states[0].core_frequency * 1000;
+
+ for (i = 0; i < (perf->state_count-1); i++) {
+ freq = freqn;
+ freqn = perf->states[i+1].core_frequency * 1000;
+ if ((2 * cpu_khz) > (freqn + freq)) {
+ perf->state = i;
+ return freq;
+ }
+ }
+ perf->state = perf->state_count-1;
+ return freqn;
+ } else {
+ /* assume CPU is at P0... */
+ perf->state = 0;
+ return perf->states[0].core_frequency * 1000;
+ }
+}
+
+static void free_acpi_perf_data(void)
+{
+ unsigned int i;
+
+ /* Freeing a NULL pointer is OK, and alloc_percpu zeroes. */
+ for_each_possible_cpu(i)
+ free_cpumask_var(per_cpu_ptr(acpi_perf_data, i)
+ ->shared_cpu_map);
+ free_percpu(acpi_perf_data);
+}
+
+/*
+ * acpi_cpufreq_early_init - initialize ACPI P-States library
+ *
+ * Initialize the ACPI P-States library (drivers/acpi/processor_perflib.c)
+ * in order to determine correct frequency and voltage pairings. We can
+ * do _PDC and _PSD and find out the processor dependency for the
+ * actual init that will happen later...
+ */
+static int __init acpi_cpufreq_early_init(void)
+{
+ unsigned int i;
+ pr_debug("acpi_cpufreq_early_init\n");
+
+ acpi_perf_data = alloc_percpu(struct acpi_processor_performance);
+ if (!acpi_perf_data) {
+ pr_debug("Memory allocation error for acpi_perf_data.\n");
+ return -ENOMEM;
+ }
+ for_each_possible_cpu(i) {
+ if (!zalloc_cpumask_var_node(
+ &per_cpu_ptr(acpi_perf_data, i)->shared_cpu_map,
+ GFP_KERNEL, cpu_to_node(i))) {
+
+ /* Freeing a NULL pointer is OK: alloc_percpu zeroes. */
+ free_acpi_perf_data();
+ return -ENOMEM;
+ }
+ }
+
+ /* Do initialization in ACPI core */
+ acpi_processor_preregister_performance(acpi_perf_data);
+ return 0;
+}
+
+#ifdef CONFIG_SMP
+/*
+ * Some BIOSes do SW_ANY coordination internally, either set it up in hw
+ * or do it in BIOS firmware and won't inform about it to OS. If not
+ * detected, this has a side effect of making CPU run at a different speed
+ * than OS intended it to run at. Detect it and handle it cleanly.
+ */
+static int bios_with_sw_any_bug;
+
+static int sw_any_bug_found(const struct dmi_system_id *d)
+{
+ bios_with_sw_any_bug = 1;
+ return 0;
+}
+
+static const struct dmi_system_id sw_any_bug_dmi_table[] = {
+ {
+ .callback = sw_any_bug_found,
+ .ident = "Supermicro Server X6DLP",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Supermicro"),
+ DMI_MATCH(DMI_BIOS_VERSION, "080010"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X6DLP"),
+ },
+ },
+ { }
+};
+
+static int acpi_cpufreq_blacklist(struct cpuinfo_x86 *c)
+{
+ /* Intel Xeon Processor 7100 Series Specification Update
+ * http://www.intel.com/Assets/PDF/specupdate/314554.pdf
+ * AL30: A Machine Check Exception (MCE) Occurring during an
+ * Enhanced Intel SpeedStep Technology Ratio Change May Cause
+ * Both Processor Cores to Lock Up. */
+ if (c->x86_vendor == X86_VENDOR_INTEL) {
+ if ((c->x86 == 15) &&
+ (c->x86_model == 6) &&
+ (c->x86_mask == 8)) {
+ printk(KERN_INFO "acpi-cpufreq: Intel(R) "
+ "Xeon(R) 7100 Errata AL30, processors may "
+ "lock up on frequency changes: disabling "
+ "acpi-cpufreq.\n");
+ return -ENODEV;
+ }
+ }
+ return 0;
+}
+#endif
+
+static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
+{
+ unsigned int i;
+ unsigned int valid_states = 0;
+ unsigned int cpu = policy->cpu;
+ struct acpi_cpufreq_data *data;
+ unsigned int result = 0;
+ struct cpuinfo_x86 *c = &cpu_data(policy->cpu);
+ struct acpi_processor_performance *perf;
+#ifdef CONFIG_SMP
+ static int blacklisted;
+#endif
+
+ pr_debug("acpi_cpufreq_cpu_init\n");
+
+#ifdef CONFIG_SMP
+ if (blacklisted)
+ return blacklisted;
+ blacklisted = acpi_cpufreq_blacklist(c);
+ if (blacklisted)
+ return blacklisted;
+#endif
+
+ data = kzalloc(sizeof(struct acpi_cpufreq_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->acpi_data = per_cpu_ptr(acpi_perf_data, cpu);
+ per_cpu(acfreq_data, cpu) = data;
+
+ if (cpu_has(c, X86_FEATURE_CONSTANT_TSC))
+ acpi_cpufreq_driver.flags |= CPUFREQ_CONST_LOOPS;
+
+ result = acpi_processor_register_performance(data->acpi_data, cpu);
+ if (result)
+ goto err_free;
+
+ perf = data->acpi_data;
+ policy->shared_type = perf->shared_type;
+
+ /*
+ * Will let policy->cpus know about dependency only when software
+ * coordination is required.
+ */
+ if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL ||
+ policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) {
+ cpumask_copy(policy->cpus, perf->shared_cpu_map);
+ }
+ cpumask_copy(policy->related_cpus, perf->shared_cpu_map);
+
+#ifdef CONFIG_SMP
+ dmi_check_system(sw_any_bug_dmi_table);
+ if (bios_with_sw_any_bug && cpumask_weight(policy->cpus) == 1) {
+ policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
+ cpumask_copy(policy->cpus, cpu_core_mask(cpu));
+ }
+#endif
+
+ /* capability check */
+ if (perf->state_count <= 1) {
+ pr_debug("No P-States\n");
+ result = -ENODEV;
+ goto err_unreg;
+ }
+
+ if (perf->control_register.space_id != perf->status_register.space_id) {
+ result = -ENODEV;
+ goto err_unreg;
+ }
+
+ switch (perf->control_register.space_id) {
+ case ACPI_ADR_SPACE_SYSTEM_IO:
+ pr_debug("SYSTEM IO addr space\n");
+ data->cpu_feature = SYSTEM_IO_CAPABLE;
+ break;
+ case ACPI_ADR_SPACE_FIXED_HARDWARE:
+ pr_debug("HARDWARE addr space\n");
+ if (!check_est_cpu(cpu)) {
+ result = -ENODEV;
+ goto err_unreg;
+ }
+ data->cpu_feature = SYSTEM_INTEL_MSR_CAPABLE;
+ break;
+ default:
+ pr_debug("Unknown addr space %d\n",
+ (u32) (perf->control_register.space_id));
+ result = -ENODEV;
+ goto err_unreg;
+ }
+
+ data->freq_table = kmalloc(sizeof(struct cpufreq_frequency_table) *
+ (perf->state_count+1), GFP_KERNEL);
+ if (!data->freq_table) {
+ result = -ENOMEM;
+ goto err_unreg;
+ }
+
+ /* detect transition latency */
+ policy->cpuinfo.transition_latency = 0;
+ for (i = 0; i < perf->state_count; i++) {
+ if ((perf->states[i].transition_latency * 1000) >
+ policy->cpuinfo.transition_latency)
+ policy->cpuinfo.transition_latency =
+ perf->states[i].transition_latency * 1000;
+ }
+
+ /* Check for high latency (>20uS) from buggy BIOSes, like on T42 */
+ if (perf->control_register.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE &&
+ policy->cpuinfo.transition_latency > 20 * 1000) {
+ policy->cpuinfo.transition_latency = 20 * 1000;
+ printk_once(KERN_INFO
+ "P-state transition latency capped at 20 uS\n");
+ }
+
+ /* table init */
+ for (i = 0; i < perf->state_count; i++) {
+ if (i > 0 && perf->states[i].core_frequency >=
+ data->freq_table[valid_states-1].frequency / 1000)
+ continue;
+
+ data->freq_table[valid_states].index = i;
+ data->freq_table[valid_states].frequency =
+ perf->states[i].core_frequency * 1000;
+ valid_states++;
+ }
+ data->freq_table[valid_states].frequency = CPUFREQ_TABLE_END;
+ perf->state = 0;
+
+ result = cpufreq_frequency_table_cpuinfo(policy, data->freq_table);
+ if (result)
+ goto err_freqfree;
+
+ if (perf->states[0].core_frequency * 1000 != policy->cpuinfo.max_freq)
+ printk(KERN_WARNING FW_WARN "P-state 0 is not max freq\n");
+
+ switch (perf->control_register.space_id) {
+ case ACPI_ADR_SPACE_SYSTEM_IO:
+ /* Current speed is unknown and not detectable by IO port */
+ policy->cur = acpi_cpufreq_guess_freq(data, policy->cpu);
+ break;
+ case ACPI_ADR_SPACE_FIXED_HARDWARE:
+ acpi_cpufreq_driver.get = get_cur_freq_on_cpu;
+ policy->cur = get_cur_freq_on_cpu(cpu);
+ break;
+ default:
+ break;
+ }
+
+ /* notify BIOS that we exist */
+ acpi_processor_notify_smm(THIS_MODULE);
+
+ /* Check for APERF/MPERF support in hardware */
+ if (cpu_has(c, X86_FEATURE_APERFMPERF))
+ acpi_cpufreq_driver.getavg = cpufreq_get_measured_perf;
+
+ pr_debug("CPU%u - ACPI performance management activated.\n", cpu);
+ for (i = 0; i < perf->state_count; i++)
+ pr_debug(" %cP%d: %d MHz, %d mW, %d uS\n",
+ (i == perf->state ? '*' : ' '), i,
+ (u32) perf->states[i].core_frequency,
+ (u32) perf->states[i].power,
+ (u32) perf->states[i].transition_latency);
+
+ cpufreq_frequency_table_get_attr(data->freq_table, policy->cpu);
+
+ /*
+ * the first call to ->target() should result in us actually
+ * writing something to the appropriate registers.
+ */
+ data->resume = 1;
+
+ return result;
+
+err_freqfree:
+ kfree(data->freq_table);
+err_unreg:
+ acpi_processor_unregister_performance(perf, cpu);
+err_free:
+ kfree(data);
+ per_cpu(acfreq_data, cpu) = NULL;
+
+ return result;
+}
+
+static int acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy)
+{
+ struct acpi_cpufreq_data *data = per_cpu(acfreq_data, policy->cpu);
+
+ pr_debug("acpi_cpufreq_cpu_exit\n");
+
+ if (data) {
+ cpufreq_frequency_table_put_attr(policy->cpu);
+ per_cpu(acfreq_data, policy->cpu) = NULL;
+ acpi_processor_unregister_performance(data->acpi_data,
+ policy->cpu);
+ kfree(data->freq_table);
+ kfree(data);
+ }
+
+ return 0;
+}
+
+static int acpi_cpufreq_resume(struct cpufreq_policy *policy)
+{
+ struct acpi_cpufreq_data *data = per_cpu(acfreq_data, policy->cpu);
+
+ pr_debug("acpi_cpufreq_resume\n");
+
+ data->resume = 1;
+
+ return 0;
+}
+
+static struct freq_attr *acpi_cpufreq_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+static struct cpufreq_driver acpi_cpufreq_driver = {
+ .verify = acpi_cpufreq_verify,
+ .target = acpi_cpufreq_target,
+ .bios_limit = acpi_processor_get_bios_limit,
+ .init = acpi_cpufreq_cpu_init,
+ .exit = acpi_cpufreq_cpu_exit,
+ .resume = acpi_cpufreq_resume,
+ .name = "acpi-cpufreq",
+ .owner = THIS_MODULE,
+ .attr = acpi_cpufreq_attr,
+};
+
+static int __init acpi_cpufreq_init(void)
+{
+ int ret;
+
+ if (acpi_disabled)
+ return 0;
+
+ pr_debug("acpi_cpufreq_init\n");
+
+ ret = acpi_cpufreq_early_init();
+ if (ret)
+ return ret;
+
+ ret = cpufreq_register_driver(&acpi_cpufreq_driver);
+ if (ret)
+ free_acpi_perf_data();
+
+ return ret;
+}
+
+static void __exit acpi_cpufreq_exit(void)
+{
+ pr_debug("acpi_cpufreq_exit\n");
+
+ cpufreq_unregister_driver(&acpi_cpufreq_driver);
+
+ free_percpu(acpi_perf_data);
+}
+
+module_param(acpi_pstate_strict, uint, 0644);
+MODULE_PARM_DESC(acpi_pstate_strict,
+ "value 0 or non-zero. non-zero -> strict ACPI checks are "
+ "performed during frequency changes.");
+
+late_initcall(acpi_cpufreq_init);
+module_exit(acpi_cpufreq_exit);
+
+MODULE_ALIAS("acpi");
diff --git a/drivers/cpufreq/cpufreq-nforce2.c b/drivers/cpufreq/cpufreq-nforce2.c
new file mode 100644
index 00000000000..7bac808804f
--- /dev/null
+++ b/drivers/cpufreq/cpufreq-nforce2.c
@@ -0,0 +1,444 @@
+/*
+ * (C) 2004-2006 Sebastian Witt <se.witt@gmx.net>
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ * Based upon reverse engineered information
+ *
+ * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#define NFORCE2_XTAL 25
+#define NFORCE2_BOOTFSB 0x48
+#define NFORCE2_PLLENABLE 0xa8
+#define NFORCE2_PLLREG 0xa4
+#define NFORCE2_PLLADR 0xa0
+#define NFORCE2_PLL(mul, div) (0x100000 | (mul << 8) | div)
+
+#define NFORCE2_MIN_FSB 50
+#define NFORCE2_SAFE_DISTANCE 50
+
+/* Delay in ms between FSB changes */
+/* #define NFORCE2_DELAY 10 */
+
+/*
+ * nforce2_chipset:
+ * FSB is changed using the chipset
+ */
+static struct pci_dev *nforce2_dev;
+
+/* fid:
+ * multiplier * 10
+ */
+static int fid;
+
+/* min_fsb, max_fsb:
+ * minimum and maximum FSB (= FSB at boot time)
+ */
+static int min_fsb;
+static int max_fsb;
+
+MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
+MODULE_DESCRIPTION("nForce2 FSB changing cpufreq driver");
+MODULE_LICENSE("GPL");
+
+module_param(fid, int, 0444);
+module_param(min_fsb, int, 0444);
+
+MODULE_PARM_DESC(fid, "CPU multiplier to use (11.5 = 115)");
+MODULE_PARM_DESC(min_fsb,
+ "Minimum FSB to use, if not defined: current FSB - 50");
+
+#define PFX "cpufreq-nforce2: "
+
+/**
+ * nforce2_calc_fsb - calculate FSB
+ * @pll: PLL value
+ *
+ * Calculates FSB from PLL value
+ */
+static int nforce2_calc_fsb(int pll)
+{
+ unsigned char mul, div;
+
+ mul = (pll >> 8) & 0xff;
+ div = pll & 0xff;
+
+ if (div > 0)
+ return NFORCE2_XTAL * mul / div;
+
+ return 0;
+}
+
+/**
+ * nforce2_calc_pll - calculate PLL value
+ * @fsb: FSB
+ *
+ * Calculate PLL value for given FSB
+ */
+static int nforce2_calc_pll(unsigned int fsb)
+{
+ unsigned char xmul, xdiv;
+ unsigned char mul = 0, div = 0;
+ int tried = 0;
+
+ /* Try to calculate multiplier and divider up to 4 times */
+ while (((mul == 0) || (div == 0)) && (tried <= 3)) {
+ for (xdiv = 2; xdiv <= 0x80; xdiv++)
+ for (xmul = 1; xmul <= 0xfe; xmul++)
+ if (nforce2_calc_fsb(NFORCE2_PLL(xmul, xdiv)) ==
+ fsb + tried) {
+ mul = xmul;
+ div = xdiv;
+ }
+ tried++;
+ }
+
+ if ((mul == 0) || (div == 0))
+ return -1;
+
+ return NFORCE2_PLL(mul, div);
+}
+
+/**
+ * nforce2_write_pll - write PLL value to chipset
+ * @pll: PLL value
+ *
+ * Writes new FSB PLL value to chipset
+ */
+static void nforce2_write_pll(int pll)
+{
+ int temp;
+
+ /* Set the pll addr. to 0x00 */
+ pci_write_config_dword(nforce2_dev, NFORCE2_PLLADR, 0);
+
+ /* Now write the value in all 64 registers */
+ for (temp = 0; temp <= 0x3f; temp++)
+ pci_write_config_dword(nforce2_dev, NFORCE2_PLLREG, pll);
+
+ return;
+}
+
+/**
+ * nforce2_fsb_read - Read FSB
+ *
+ * Read FSB from chipset
+ * If bootfsb != 0, return FSB at boot-time
+ */
+static unsigned int nforce2_fsb_read(int bootfsb)
+{
+ struct pci_dev *nforce2_sub5;
+ u32 fsb, temp = 0;
+
+ /* Get chipset boot FSB from subdevice 5 (FSB at boot-time) */
+ nforce2_sub5 = pci_get_subsys(PCI_VENDOR_ID_NVIDIA, 0x01EF,
+ PCI_ANY_ID, PCI_ANY_ID, NULL);
+ if (!nforce2_sub5)
+ return 0;
+
+ pci_read_config_dword(nforce2_sub5, NFORCE2_BOOTFSB, &fsb);
+ fsb /= 1000000;
+
+ /* Check if PLL register is already set */
+ pci_read_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8 *)&temp);
+
+ if (bootfsb || !temp)
+ return fsb;
+
+ /* Use PLL register FSB value */
+ pci_read_config_dword(nforce2_dev, NFORCE2_PLLREG, &temp);
+ fsb = nforce2_calc_fsb(temp);
+
+ return fsb;
+}
+
+/**
+ * nforce2_set_fsb - set new FSB
+ * @fsb: New FSB
+ *
+ * Sets new FSB
+ */
+static int nforce2_set_fsb(unsigned int fsb)
+{
+ u32 temp = 0;
+ unsigned int tfsb;
+ int diff;
+ int pll = 0;
+
+ if ((fsb > max_fsb) || (fsb < NFORCE2_MIN_FSB)) {
+ printk(KERN_ERR PFX "FSB %d is out of range!\n", fsb);
+ return -EINVAL;
+ }
+
+ tfsb = nforce2_fsb_read(0);
+ if (!tfsb) {
+ printk(KERN_ERR PFX "Error while reading the FSB\n");
+ return -EINVAL;
+ }
+
+ /* First write? Then set actual value */
+ pci_read_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8 *)&temp);
+ if (!temp) {
+ pll = nforce2_calc_pll(tfsb);
+
+ if (pll < 0)
+ return -EINVAL;
+
+ nforce2_write_pll(pll);
+ }
+
+ /* Enable write access */
+ temp = 0x01;
+ pci_write_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8)temp);
+
+ diff = tfsb - fsb;
+
+ if (!diff)
+ return 0;
+
+ while ((tfsb != fsb) && (tfsb <= max_fsb) && (tfsb >= min_fsb)) {
+ if (diff < 0)
+ tfsb++;
+ else
+ tfsb--;
+
+ /* Calculate the PLL reg. value */
+ pll = nforce2_calc_pll(tfsb);
+ if (pll == -1)
+ return -EINVAL;
+
+ nforce2_write_pll(pll);
+#ifdef NFORCE2_DELAY
+ mdelay(NFORCE2_DELAY);
+#endif
+ }
+
+ temp = 0x40;
+ pci_write_config_byte(nforce2_dev, NFORCE2_PLLADR, (u8)temp);
+
+ return 0;
+}
+
+/**
+ * nforce2_get - get the CPU frequency
+ * @cpu: CPU number
+ *
+ * Returns the CPU frequency
+ */
+static unsigned int nforce2_get(unsigned int cpu)
+{
+ if (cpu)
+ return 0;
+ return nforce2_fsb_read(0) * fid * 100;
+}
+
+/**
+ * nforce2_target - set a new CPUFreq policy
+ * @policy: new policy
+ * @target_freq: the target frequency
+ * @relation: how that frequency relates to achieved frequency
+ * (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
+ *
+ * Sets a new CPUFreq policy.
+ */
+static int nforce2_target(struct cpufreq_policy *policy,
+ unsigned int target_freq, unsigned int relation)
+{
+/* unsigned long flags; */
+ struct cpufreq_freqs freqs;
+ unsigned int target_fsb;
+
+ if ((target_freq > policy->max) || (target_freq < policy->min))
+ return -EINVAL;
+
+ target_fsb = target_freq / (fid * 100);
+
+ freqs.old = nforce2_get(policy->cpu);
+ freqs.new = target_fsb * fid * 100;
+ freqs.cpu = 0; /* Only one CPU on nForce2 platforms */
+
+ if (freqs.old == freqs.new)
+ return 0;
+
+ pr_debug("Old CPU frequency %d kHz, new %d kHz\n",
+ freqs.old, freqs.new);
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ /* Disable IRQs */
+ /* local_irq_save(flags); */
+
+ if (nforce2_set_fsb(target_fsb) < 0)
+ printk(KERN_ERR PFX "Changing FSB to %d failed\n",
+ target_fsb);
+ else
+ pr_debug("Changed FSB successfully to %d\n",
+ target_fsb);
+
+ /* Enable IRQs */
+ /* local_irq_restore(flags); */
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+ return 0;
+}
+
+/**
+ * nforce2_verify - verifies a new CPUFreq policy
+ * @policy: new policy
+ */
+static int nforce2_verify(struct cpufreq_policy *policy)
+{
+ unsigned int fsb_pol_max;
+
+ fsb_pol_max = policy->max / (fid * 100);
+
+ if (policy->min < (fsb_pol_max * fid * 100))
+ policy->max = (fsb_pol_max + 1) * fid * 100;
+
+ cpufreq_verify_within_limits(policy,
+ policy->cpuinfo.min_freq,
+ policy->cpuinfo.max_freq);
+ return 0;
+}
+
+static int nforce2_cpu_init(struct cpufreq_policy *policy)
+{
+ unsigned int fsb;
+ unsigned int rfid;
+
+ /* capability check */
+ if (policy->cpu != 0)
+ return -ENODEV;
+
+ /* Get current FSB */
+ fsb = nforce2_fsb_read(0);
+
+ if (!fsb)
+ return -EIO;
+
+ /* FIX: Get FID from CPU */
+ if (!fid) {
+ if (!cpu_khz) {
+ printk(KERN_WARNING PFX
+ "cpu_khz not set, can't calculate multiplier!\n");
+ return -ENODEV;
+ }
+
+ fid = cpu_khz / (fsb * 100);
+ rfid = fid % 5;
+
+ if (rfid) {
+ if (rfid > 2)
+ fid += 5 - rfid;
+ else
+ fid -= rfid;
+ }
+ }
+
+ printk(KERN_INFO PFX "FSB currently at %i MHz, FID %d.%d\n", fsb,
+ fid / 10, fid % 10);
+
+ /* Set maximum FSB to FSB at boot time */
+ max_fsb = nforce2_fsb_read(1);
+
+ if (!max_fsb)
+ return -EIO;
+
+ if (!min_fsb)
+ min_fsb = max_fsb - NFORCE2_SAFE_DISTANCE;
+
+ if (min_fsb < NFORCE2_MIN_FSB)
+ min_fsb = NFORCE2_MIN_FSB;
+
+ /* cpuinfo and default policy values */
+ policy->cpuinfo.min_freq = min_fsb * fid * 100;
+ policy->cpuinfo.max_freq = max_fsb * fid * 100;
+ policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+ policy->cur = nforce2_get(policy->cpu);
+ policy->min = policy->cpuinfo.min_freq;
+ policy->max = policy->cpuinfo.max_freq;
+
+ return 0;
+}
+
+static int nforce2_cpu_exit(struct cpufreq_policy *policy)
+{
+ return 0;
+}
+
+static struct cpufreq_driver nforce2_driver = {
+ .name = "nforce2",
+ .verify = nforce2_verify,
+ .target = nforce2_target,
+ .get = nforce2_get,
+ .init = nforce2_cpu_init,
+ .exit = nforce2_cpu_exit,
+ .owner = THIS_MODULE,
+};
+
+/**
+ * nforce2_detect_chipset - detect the Southbridge which contains FSB PLL logic
+ *
+ * Detects nForce2 A2 and C1 stepping
+ *
+ */
+static int nforce2_detect_chipset(void)
+{
+ nforce2_dev = pci_get_subsys(PCI_VENDOR_ID_NVIDIA,
+ PCI_DEVICE_ID_NVIDIA_NFORCE2,
+ PCI_ANY_ID, PCI_ANY_ID, NULL);
+
+ if (nforce2_dev == NULL)
+ return -ENODEV;
+
+ printk(KERN_INFO PFX "Detected nForce2 chipset revision %X\n",
+ nforce2_dev->revision);
+ printk(KERN_INFO PFX
+ "FSB changing is maybe unstable and can lead to "
+ "crashes and data loss.\n");
+
+ return 0;
+}
+
+/**
+ * nforce2_init - initializes the nForce2 CPUFreq driver
+ *
+ * Initializes the nForce2 FSB support. Returns -ENODEV on unsupported
+ * devices, -EINVAL on problems during initiatization, and zero on
+ * success.
+ */
+static int __init nforce2_init(void)
+{
+ /* TODO: do we need to detect the processor? */
+
+ /* detect chipset */
+ if (nforce2_detect_chipset()) {
+ printk(KERN_INFO PFX "No nForce2 chipset.\n");
+ return -ENODEV;
+ }
+
+ return cpufreq_register_driver(&nforce2_driver);
+}
+
+/**
+ * nforce2_exit - unregisters cpufreq module
+ *
+ * Unregisters nForce2 FSB change support.
+ */
+static void __exit nforce2_exit(void)
+{
+ cpufreq_unregister_driver(&nforce2_driver);
+}
+
+module_init(nforce2_init);
+module_exit(nforce2_exit);
+
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 2dafc5c38ae..0a5bea9e358 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -32,9 +32,6 @@
#include <trace/events/power.h>
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, \
- "cpufreq-core", msg)
-
/**
* The "cpufreq driver" - the arch- or hardware-dependent low
* level driver of CPUFreq support, and its spinlock. This lock
@@ -181,93 +178,6 @@ EXPORT_SYMBOL_GPL(cpufreq_cpu_put);
/*********************************************************************
- * UNIFIED DEBUG HELPERS *
- *********************************************************************/
-#ifdef CONFIG_CPU_FREQ_DEBUG
-
-/* what part(s) of the CPUfreq subsystem are debugged? */
-static unsigned int debug;
-
-/* is the debug output ratelimit'ed using printk_ratelimit? User can
- * set or modify this value.
- */
-static unsigned int debug_ratelimit = 1;
-
-/* is the printk_ratelimit'ing enabled? It's enabled after a successful
- * loading of a cpufreq driver, temporarily disabled when a new policy
- * is set, and disabled upon cpufreq driver removal
- */
-static unsigned int disable_ratelimit = 1;
-static DEFINE_SPINLOCK(disable_ratelimit_lock);
-
-static void cpufreq_debug_enable_ratelimit(void)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&disable_ratelimit_lock, flags);
- if (disable_ratelimit)
- disable_ratelimit--;
- spin_unlock_irqrestore(&disable_ratelimit_lock, flags);
-}
-
-static void cpufreq_debug_disable_ratelimit(void)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&disable_ratelimit_lock, flags);
- disable_ratelimit++;
- spin_unlock_irqrestore(&disable_ratelimit_lock, flags);
-}
-
-void cpufreq_debug_printk(unsigned int type, const char *prefix,
- const char *fmt, ...)
-{
- char s[256];
- va_list args;
- unsigned int len;
- unsigned long flags;
-
- WARN_ON(!prefix);
- if (type & debug) {
- spin_lock_irqsave(&disable_ratelimit_lock, flags);
- if (!disable_ratelimit && debug_ratelimit
- && !printk_ratelimit()) {
- spin_unlock_irqrestore(&disable_ratelimit_lock, flags);
- return;
- }
- spin_unlock_irqrestore(&disable_ratelimit_lock, flags);
-
- len = snprintf(s, 256, KERN_DEBUG "%s: ", prefix);
-
- va_start(args, fmt);
- len += vsnprintf(&s[len], (256 - len), fmt, args);
- va_end(args);
-
- printk(s);
-
- WARN_ON(len < 5);
- }
-}
-EXPORT_SYMBOL(cpufreq_debug_printk);
-
-
-module_param(debug, uint, 0644);
-MODULE_PARM_DESC(debug, "CPUfreq debugging: add 1 to debug core,"
- " 2 to debug drivers, and 4 to debug governors.");
-
-module_param(debug_ratelimit, uint, 0644);
-MODULE_PARM_DESC(debug_ratelimit, "CPUfreq debugging:"
- " set to 0 to disable ratelimiting.");
-
-#else /* !CONFIG_CPU_FREQ_DEBUG */
-
-static inline void cpufreq_debug_enable_ratelimit(void) { return; }
-static inline void cpufreq_debug_disable_ratelimit(void) { return; }
-
-#endif /* CONFIG_CPU_FREQ_DEBUG */
-
-
-/*********************************************************************
* EXTERNALLY AFFECTING FREQUENCY CHANGES *
*********************************************************************/
@@ -291,7 +201,7 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
if (!l_p_j_ref_freq) {
l_p_j_ref = loops_per_jiffy;
l_p_j_ref_freq = ci->old;
- dprintk("saving %lu as reference value for loops_per_jiffy; "
+ pr_debug("saving %lu as reference value for loops_per_jiffy; "
"freq is %u kHz\n", l_p_j_ref, l_p_j_ref_freq);
}
if ((val == CPUFREQ_PRECHANGE && ci->old < ci->new) ||
@@ -299,7 +209,7 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
(val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) {
loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq,
ci->new);
- dprintk("scaling loops_per_jiffy to %lu "
+ pr_debug("scaling loops_per_jiffy to %lu "
"for frequency %u kHz\n", loops_per_jiffy, ci->new);
}
}
@@ -326,7 +236,7 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state)
BUG_ON(irqs_disabled());
freqs->flags = cpufreq_driver->flags;
- dprintk("notification %u of frequency transition to %u kHz\n",
+ pr_debug("notification %u of frequency transition to %u kHz\n",
state, freqs->new);
policy = per_cpu(cpufreq_cpu_data, freqs->cpu);
@@ -340,7 +250,7 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state)
if (!(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) {
if ((policy) && (policy->cpu == freqs->cpu) &&
(policy->cur) && (policy->cur != freqs->old)) {
- dprintk("Warning: CPU frequency is"
+ pr_debug("Warning: CPU frequency is"
" %u, cpufreq assumed %u kHz.\n",
freqs->old, policy->cur);
freqs->old = policy->cur;
@@ -353,7 +263,7 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state)
case CPUFREQ_POSTCHANGE:
adjust_jiffies(CPUFREQ_POSTCHANGE, freqs);
- dprintk("FREQ: %lu - CPU: %lu", (unsigned long)freqs->new,
+ pr_debug("FREQ: %lu - CPU: %lu", (unsigned long)freqs->new,
(unsigned long)freqs->cpu);
trace_power_frequency(POWER_PSTATE, freqs->new, freqs->cpu);
trace_cpu_frequency(freqs->new, freqs->cpu);
@@ -411,21 +321,14 @@ static int cpufreq_parse_governor(char *str_governor, unsigned int *policy,
t = __find_governor(str_governor);
if (t == NULL) {
- char *name = kasprintf(GFP_KERNEL, "cpufreq_%s",
- str_governor);
-
- if (name) {
- int ret;
+ int ret;
- mutex_unlock(&cpufreq_governor_mutex);
- ret = request_module("%s", name);
- mutex_lock(&cpufreq_governor_mutex);
+ mutex_unlock(&cpufreq_governor_mutex);
+ ret = request_module("cpufreq_%s", str_governor);
+ mutex_lock(&cpufreq_governor_mutex);
- if (ret == 0)
- t = __find_governor(str_governor);
- }
-
- kfree(name);
+ if (ret == 0)
+ t = __find_governor(str_governor);
}
if (t != NULL) {
@@ -753,7 +656,7 @@ no_policy:
static void cpufreq_sysfs_release(struct kobject *kobj)
{
struct cpufreq_policy *policy = to_policy(kobj);
- dprintk("last reference is dropped\n");
+ pr_debug("last reference is dropped\n");
complete(&policy->kobj_unregister);
}
@@ -788,7 +691,7 @@ static int cpufreq_add_dev_policy(unsigned int cpu,
gov = __find_governor(per_cpu(cpufreq_cpu_governor, cpu));
if (gov) {
policy->governor = gov;
- dprintk("Restoring governor %s for cpu %d\n",
+ pr_debug("Restoring governor %s for cpu %d\n",
policy->governor->name, cpu);
}
#endif
@@ -824,7 +727,7 @@ static int cpufreq_add_dev_policy(unsigned int cpu,
per_cpu(cpufreq_cpu_data, cpu) = managed_policy;
spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
- dprintk("CPU already managed, adding link\n");
+ pr_debug("CPU already managed, adding link\n");
ret = sysfs_create_link(&sys_dev->kobj,
&managed_policy->kobj,
"cpufreq");
@@ -865,7 +768,7 @@ static int cpufreq_add_dev_symlink(unsigned int cpu,
if (!cpu_online(j))
continue;
- dprintk("CPU %u already managed, adding link\n", j);
+ pr_debug("CPU %u already managed, adding link\n", j);
managed_policy = cpufreq_cpu_get(cpu);
cpu_sys_dev = get_cpu_sysdev(j);
ret = sysfs_create_link(&cpu_sys_dev->kobj, &policy->kobj,
@@ -941,7 +844,7 @@ static int cpufreq_add_dev_interface(unsigned int cpu,
policy->user_policy.governor = policy->governor;
if (ret) {
- dprintk("setting policy failed\n");
+ pr_debug("setting policy failed\n");
if (cpufreq_driver->exit)
cpufreq_driver->exit(policy);
}
@@ -977,8 +880,7 @@ static int cpufreq_add_dev(struct sys_device *sys_dev)
if (cpu_is_offline(cpu))
return 0;
- cpufreq_debug_disable_ratelimit();
- dprintk("adding CPU %u\n", cpu);
+ pr_debug("adding CPU %u\n", cpu);
#ifdef CONFIG_SMP
/* check whether a different CPU already registered this
@@ -986,7 +888,6 @@ static int cpufreq_add_dev(struct sys_device *sys_dev)
policy = cpufreq_cpu_get(cpu);
if (unlikely(policy)) {
cpufreq_cpu_put(policy);
- cpufreq_debug_enable_ratelimit();
return 0;
}
#endif
@@ -1037,7 +938,7 @@ static int cpufreq_add_dev(struct sys_device *sys_dev)
*/
ret = cpufreq_driver->init(policy);
if (ret) {
- dprintk("initialization failed\n");
+ pr_debug("initialization failed\n");
goto err_unlock_policy;
}
policy->user_policy.min = policy->min;
@@ -1063,8 +964,7 @@ static int cpufreq_add_dev(struct sys_device *sys_dev)
kobject_uevent(&policy->kobj, KOBJ_ADD);
module_put(cpufreq_driver->owner);
- dprintk("initialization complete\n");
- cpufreq_debug_enable_ratelimit();
+ pr_debug("initialization complete\n");
return 0;
@@ -1088,7 +988,6 @@ err_free_policy:
nomem_out:
module_put(cpufreq_driver->owner);
module_out:
- cpufreq_debug_enable_ratelimit();
return ret;
}
@@ -1112,15 +1011,13 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev)
unsigned int j;
#endif
- cpufreq_debug_disable_ratelimit();
- dprintk("unregistering CPU %u\n", cpu);
+ pr_debug("unregistering CPU %u\n", cpu);
spin_lock_irqsave(&cpufreq_driver_lock, flags);
data = per_cpu(cpufreq_cpu_data, cpu);
if (!data) {
spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
- cpufreq_debug_enable_ratelimit();
unlock_policy_rwsem_write(cpu);
return -EINVAL;
}
@@ -1132,12 +1029,11 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev)
* only need to unlink, put and exit
*/
if (unlikely(cpu != data->cpu)) {
- dprintk("removing link\n");
+ pr_debug("removing link\n");
cpumask_clear_cpu(cpu, data->cpus);
spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
kobj = &sys_dev->kobj;
cpufreq_cpu_put(data);
- cpufreq_debug_enable_ratelimit();
unlock_policy_rwsem_write(cpu);
sysfs_remove_link(kobj, "cpufreq");
return 0;
@@ -1170,7 +1066,7 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev)
for_each_cpu(j, data->cpus) {
if (j == cpu)
continue;
- dprintk("removing link for cpu %u\n", j);
+ pr_debug("removing link for cpu %u\n", j);
#ifdef CONFIG_HOTPLUG_CPU
strncpy(per_cpu(cpufreq_cpu_governor, j),
data->governor->name, CPUFREQ_NAME_LEN);
@@ -1199,21 +1095,35 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev)
* not referenced anymore by anybody before we proceed with
* unloading.
*/
- dprintk("waiting for dropping of refcount\n");
+ pr_debug("waiting for dropping of refcount\n");
wait_for_completion(cmp);
- dprintk("wait complete\n");
+ pr_debug("wait complete\n");
lock_policy_rwsem_write(cpu);
if (cpufreq_driver->exit)
cpufreq_driver->exit(data);
unlock_policy_rwsem_write(cpu);
+#ifdef CONFIG_HOTPLUG_CPU
+ /* when the CPU which is the parent of the kobj is hotplugged
+ * offline, check for siblings, and create cpufreq sysfs interface
+ * and symlinks
+ */
+ if (unlikely(cpumask_weight(data->cpus) > 1)) {
+ /* first sibling now owns the new sysfs dir */
+ cpumask_clear_cpu(cpu, data->cpus);
+ cpufreq_add_dev(get_cpu_sysdev(cpumask_first(data->cpus)));
+
+ /* finally remove our own symlink */
+ lock_policy_rwsem_write(cpu);
+ __cpufreq_remove_dev(sys_dev);
+ }
+#endif
+
free_cpumask_var(data->related_cpus);
free_cpumask_var(data->cpus);
kfree(data);
- per_cpu(cpufreq_cpu_data, cpu) = NULL;
- cpufreq_debug_enable_ratelimit();
return 0;
}
@@ -1239,7 +1149,7 @@ static void handle_update(struct work_struct *work)
struct cpufreq_policy *policy =
container_of(work, struct cpufreq_policy, update);
unsigned int cpu = policy->cpu;
- dprintk("handle_update for cpu %u called\n", cpu);
+ pr_debug("handle_update for cpu %u called\n", cpu);
cpufreq_update_policy(cpu);
}
@@ -1257,7 +1167,7 @@ static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq,
{
struct cpufreq_freqs freqs;
- dprintk("Warning: CPU frequency out of sync: cpufreq and timing "
+ pr_debug("Warning: CPU frequency out of sync: cpufreq and timing "
"core thinks of %u, is %u kHz.\n", old_freq, new_freq);
freqs.cpu = cpu;
@@ -1360,7 +1270,7 @@ static int cpufreq_bp_suspend(void)
int cpu = smp_processor_id();
struct cpufreq_policy *cpu_policy;
- dprintk("suspending cpu %u\n", cpu);
+ pr_debug("suspending cpu %u\n", cpu);
/* If there's no policy for the boot CPU, we have nothing to do. */
cpu_policy = cpufreq_cpu_get(cpu);
@@ -1398,7 +1308,7 @@ static void cpufreq_bp_resume(void)
int cpu = smp_processor_id();
struct cpufreq_policy *cpu_policy;
- dprintk("resuming cpu %u\n", cpu);
+ pr_debug("resuming cpu %u\n", cpu);
/* If there's no policy for the boot CPU, we have nothing to do. */
cpu_policy = cpufreq_cpu_get(cpu);
@@ -1510,7 +1420,7 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
{
int retval = -EINVAL;
- dprintk("target for CPU %u: %u kHz, relation %u\n", policy->cpu,
+ pr_debug("target for CPU %u: %u kHz, relation %u\n", policy->cpu,
target_freq, relation);
if (cpu_online(policy->cpu) && cpufreq_driver->target)
retval = cpufreq_driver->target(policy, target_freq, relation);
@@ -1596,7 +1506,7 @@ static int __cpufreq_governor(struct cpufreq_policy *policy,
if (!try_module_get(policy->governor->owner))
return -EINVAL;
- dprintk("__cpufreq_governor for CPU %u, event %u\n",
+ pr_debug("__cpufreq_governor for CPU %u, event %u\n",
policy->cpu, event);
ret = policy->governor->governor(policy, event);
@@ -1697,8 +1607,7 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data,
{
int ret = 0;
- cpufreq_debug_disable_ratelimit();
- dprintk("setting new policy for CPU %u: %u - %u kHz\n", policy->cpu,
+ pr_debug("setting new policy for CPU %u: %u - %u kHz\n", policy->cpu,
policy->min, policy->max);
memcpy(&policy->cpuinfo, &data->cpuinfo,
@@ -1735,19 +1644,19 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data,
data->min = policy->min;
data->max = policy->max;
- dprintk("new min and max freqs are %u - %u kHz\n",
+ pr_debug("new min and max freqs are %u - %u kHz\n",
data->min, data->max);
if (cpufreq_driver->setpolicy) {
data->policy = policy->policy;
- dprintk("setting range\n");
+ pr_debug("setting range\n");
ret = cpufreq_driver->setpolicy(policy);
} else {
if (policy->governor != data->governor) {
/* save old, working values */
struct cpufreq_governor *old_gov = data->governor;
- dprintk("governor switch\n");
+ pr_debug("governor switch\n");
/* end old governor */
if (data->governor)
@@ -1757,7 +1666,7 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data,
data->governor = policy->governor;
if (__cpufreq_governor(data, CPUFREQ_GOV_START)) {
/* new governor failed, so re-start old one */
- dprintk("starting governor %s failed\n",
+ pr_debug("starting governor %s failed\n",
data->governor->name);
if (old_gov) {
data->governor = old_gov;
@@ -1769,12 +1678,11 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data,
}
/* might be a policy change, too, so fall through */
}
- dprintk("governor: change or update limits\n");
+ pr_debug("governor: change or update limits\n");
__cpufreq_governor(data, CPUFREQ_GOV_LIMITS);
}
error_out:
- cpufreq_debug_enable_ratelimit();
return ret;
}
@@ -1801,7 +1709,7 @@ int cpufreq_update_policy(unsigned int cpu)
goto fail;
}
- dprintk("updating policy for CPU %u\n", cpu);
+ pr_debug("updating policy for CPU %u\n", cpu);
memcpy(&policy, data, sizeof(struct cpufreq_policy));
policy.min = data->user_policy.min;
policy.max = data->user_policy.max;
@@ -1813,7 +1721,7 @@ int cpufreq_update_policy(unsigned int cpu)
if (cpufreq_driver->get) {
policy.cur = cpufreq_driver->get(cpu);
if (!data->cur) {
- dprintk("Driver did not initialize current freq");
+ pr_debug("Driver did not initialize current freq");
data->cur = policy.cur;
} else {
if (data->cur != policy.cur)
@@ -1889,7 +1797,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
((!driver_data->setpolicy) && (!driver_data->target)))
return -EINVAL;
- dprintk("trying to register driver %s\n", driver_data->name);
+ pr_debug("trying to register driver %s\n", driver_data->name);
if (driver_data->setpolicy)
driver_data->flags |= CPUFREQ_CONST_LOOPS;
@@ -1920,15 +1828,14 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
/* if all ->init() calls failed, unregister */
if (ret) {
- dprintk("no CPU initialized for driver %s\n",
+ pr_debug("no CPU initialized for driver %s\n",
driver_data->name);
goto err_sysdev_unreg;
}
}
register_hotcpu_notifier(&cpufreq_cpu_notifier);
- dprintk("driver %s up and running\n", driver_data->name);
- cpufreq_debug_enable_ratelimit();
+ pr_debug("driver %s up and running\n", driver_data->name);
return 0;
err_sysdev_unreg:
@@ -1955,14 +1862,10 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver)
{
unsigned long flags;
- cpufreq_debug_disable_ratelimit();
-
- if (!cpufreq_driver || (driver != cpufreq_driver)) {
- cpufreq_debug_enable_ratelimit();
+ if (!cpufreq_driver || (driver != cpufreq_driver))
return -EINVAL;
- }
- dprintk("unregistering driver %s\n", driver->name);
+ pr_debug("unregistering driver %s\n", driver->name);
sysdev_driver_unregister(&cpu_sysdev_class, &cpufreq_sysdev_driver);
unregister_hotcpu_notifier(&cpufreq_cpu_notifier);
diff --git a/drivers/cpufreq/cpufreq_performance.c b/drivers/cpufreq/cpufreq_performance.c
index 7e2e515087f..f13a8a9af6a 100644
--- a/drivers/cpufreq/cpufreq_performance.c
+++ b/drivers/cpufreq/cpufreq_performance.c
@@ -15,9 +15,6 @@
#include <linux/cpufreq.h>
#include <linux/init.h>
-#define dprintk(msg...) \
- cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "performance", msg)
-
static int cpufreq_governor_performance(struct cpufreq_policy *policy,
unsigned int event)
@@ -25,7 +22,7 @@ static int cpufreq_governor_performance(struct cpufreq_policy *policy,
switch (event) {
case CPUFREQ_GOV_START:
case CPUFREQ_GOV_LIMITS:
- dprintk("setting to %u kHz because of event %u\n",
+ pr_debug("setting to %u kHz because of event %u\n",
policy->max, event);
__cpufreq_driver_target(policy, policy->max,
CPUFREQ_RELATION_H);
diff --git a/drivers/cpufreq/cpufreq_powersave.c b/drivers/cpufreq/cpufreq_powersave.c
index e6db5faf3eb..4c2eb512f2b 100644
--- a/drivers/cpufreq/cpufreq_powersave.c
+++ b/drivers/cpufreq/cpufreq_powersave.c
@@ -15,16 +15,13 @@
#include <linux/cpufreq.h>
#include <linux/init.h>
-#define dprintk(msg...) \
- cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "powersave", msg)
-
static int cpufreq_governor_powersave(struct cpufreq_policy *policy,
unsigned int event)
{
switch (event) {
case CPUFREQ_GOV_START:
case CPUFREQ_GOV_LIMITS:
- dprintk("setting to %u kHz because of event %u\n",
+ pr_debug("setting to %u kHz because of event %u\n",
policy->min, event);
__cpufreq_driver_target(policy, policy->min,
CPUFREQ_RELATION_L);
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c
index 00d73fc8e4e..b60a4c26368 100644
--- a/drivers/cpufreq/cpufreq_stats.c
+++ b/drivers/cpufreq/cpufreq_stats.c
@@ -165,17 +165,27 @@ static int freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq)
return -1;
}
+/* should be called late in the CPU removal sequence so that the stats
+ * memory is still available in case someone tries to use it.
+ */
static void cpufreq_stats_free_table(unsigned int cpu)
{
struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, cpu);
- struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
- if (policy && policy->cpu == cpu)
- sysfs_remove_group(&policy->kobj, &stats_attr_group);
if (stat) {
kfree(stat->time_in_state);
kfree(stat);
}
per_cpu(cpufreq_stats_table, cpu) = NULL;
+}
+
+/* must be called early in the CPU removal sequence (before
+ * cpufreq_remove_dev) so that policy is still valid.
+ */
+static void cpufreq_stats_free_sysfs(unsigned int cpu)
+{
+ struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
+ if (policy && policy->cpu == cpu)
+ sysfs_remove_group(&policy->kobj, &stats_attr_group);
if (policy)
cpufreq_cpu_put(policy);
}
@@ -316,6 +326,9 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb,
case CPU_ONLINE_FROZEN:
cpufreq_update_policy(cpu);
break;
+ case CPU_DOWN_PREPARE:
+ cpufreq_stats_free_sysfs(cpu);
+ break;
case CPU_DEAD:
case CPU_DEAD_FROZEN:
cpufreq_stats_free_table(cpu);
@@ -324,9 +337,10 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb,
return NOTIFY_OK;
}
-static struct notifier_block cpufreq_stat_cpu_notifier __refdata =
-{
+/* priority=1 so this will get called before cpufreq_remove_dev */
+static struct notifier_block cpufreq_stat_cpu_notifier __refdata = {
.notifier_call = cpufreq_stat_cpu_callback,
+ .priority = 1,
};
static struct notifier_block notifier_policy_block = {
diff --git a/drivers/cpufreq/cpufreq_userspace.c b/drivers/cpufreq/cpufreq_userspace.c
index 66d2d1d6c80..f231015904c 100644
--- a/drivers/cpufreq/cpufreq_userspace.c
+++ b/drivers/cpufreq/cpufreq_userspace.c
@@ -37,9 +37,6 @@ static DEFINE_PER_CPU(unsigned int, cpu_is_managed);
static DEFINE_MUTEX(userspace_mutex);
static int cpus_using_userspace_governor;
-#define dprintk(msg...) \
- cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "userspace", msg)
-
/* keep track of frequency transitions */
static int
userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
@@ -50,7 +47,7 @@ userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
if (!per_cpu(cpu_is_managed, freq->cpu))
return 0;
- dprintk("saving cpu_cur_freq of cpu %u to be %u kHz\n",
+ pr_debug("saving cpu_cur_freq of cpu %u to be %u kHz\n",
freq->cpu, freq->new);
per_cpu(cpu_cur_freq, freq->cpu) = freq->new;
@@ -73,7 +70,7 @@ static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq)
{
int ret = -EINVAL;
- dprintk("cpufreq_set for cpu %u, freq %u kHz\n", policy->cpu, freq);
+ pr_debug("cpufreq_set for cpu %u, freq %u kHz\n", policy->cpu, freq);
mutex_lock(&userspace_mutex);
if (!per_cpu(cpu_is_managed, policy->cpu))
@@ -134,7 +131,7 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
per_cpu(cpu_max_freq, cpu) = policy->max;
per_cpu(cpu_cur_freq, cpu) = policy->cur;
per_cpu(cpu_set_freq, cpu) = policy->cur;
- dprintk("managing cpu %u started "
+ pr_debug("managing cpu %u started "
"(%u - %u kHz, currently %u kHz)\n",
cpu,
per_cpu(cpu_min_freq, cpu),
@@ -156,12 +153,12 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
per_cpu(cpu_min_freq, cpu) = 0;
per_cpu(cpu_max_freq, cpu) = 0;
per_cpu(cpu_set_freq, cpu) = 0;
- dprintk("managing cpu %u stopped\n", cpu);
+ pr_debug("managing cpu %u stopped\n", cpu);
mutex_unlock(&userspace_mutex);
break;
case CPUFREQ_GOV_LIMITS:
mutex_lock(&userspace_mutex);
- dprintk("limit event for cpu %u: %u - %u kHz, "
+ pr_debug("limit event for cpu %u: %u - %u kHz, "
"currently %u kHz, last set to %u kHz\n",
cpu, policy->min, policy->max,
per_cpu(cpu_cur_freq, cpu),
diff --git a/drivers/cpufreq/e_powersaver.c b/drivers/cpufreq/e_powersaver.c
new file mode 100644
index 00000000000..35a257dd4bb
--- /dev/null
+++ b/drivers/cpufreq/e_powersaver.c
@@ -0,0 +1,367 @@
+/*
+ * Based on documentation provided by Dave Jones. Thanks!
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ *
+ * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/timex.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+
+#include <asm/msr.h>
+#include <asm/tsc.h>
+
+#define EPS_BRAND_C7M 0
+#define EPS_BRAND_C7 1
+#define EPS_BRAND_EDEN 2
+#define EPS_BRAND_C3 3
+#define EPS_BRAND_C7D 4
+
+struct eps_cpu_data {
+ u32 fsb;
+ struct cpufreq_frequency_table freq_table[];
+};
+
+static struct eps_cpu_data *eps_cpu[NR_CPUS];
+
+
+static unsigned int eps_get(unsigned int cpu)
+{
+ struct eps_cpu_data *centaur;
+ u32 lo, hi;
+
+ if (cpu)
+ return 0;
+ centaur = eps_cpu[cpu];
+ if (centaur == NULL)
+ return 0;
+
+ /* Return current frequency */
+ rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
+ return centaur->fsb * ((lo >> 8) & 0xff);
+}
+
+static int eps_set_state(struct eps_cpu_data *centaur,
+ unsigned int cpu,
+ u32 dest_state)
+{
+ struct cpufreq_freqs freqs;
+ u32 lo, hi;
+ int err = 0;
+ int i;
+
+ freqs.old = eps_get(cpu);
+ freqs.new = centaur->fsb * ((dest_state >> 8) & 0xff);
+ freqs.cpu = cpu;
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ /* Wait while CPU is busy */
+ rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
+ i = 0;
+ while (lo & ((1 << 16) | (1 << 17))) {
+ udelay(16);
+ rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
+ i++;
+ if (unlikely(i > 64)) {
+ err = -ENODEV;
+ goto postchange;
+ }
+ }
+ /* Set new multiplier and voltage */
+ wrmsr(MSR_IA32_PERF_CTL, dest_state & 0xffff, 0);
+ /* Wait until transition end */
+ i = 0;
+ do {
+ udelay(16);
+ rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
+ i++;
+ if (unlikely(i > 64)) {
+ err = -ENODEV;
+ goto postchange;
+ }
+ } while (lo & ((1 << 16) | (1 << 17)));
+
+ /* Return current frequency */
+postchange:
+ rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
+ freqs.new = centaur->fsb * ((lo >> 8) & 0xff);
+
+#ifdef DEBUG
+ {
+ u8 current_multiplier, current_voltage;
+
+ /* Print voltage and multiplier */
+ rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
+ current_voltage = lo & 0xff;
+ printk(KERN_INFO "eps: Current voltage = %dmV\n",
+ current_voltage * 16 + 700);
+ current_multiplier = (lo >> 8) & 0xff;
+ printk(KERN_INFO "eps: Current multiplier = %d\n",
+ current_multiplier);
+ }
+#endif
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ return err;
+}
+
+static int eps_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ struct eps_cpu_data *centaur;
+ unsigned int newstate = 0;
+ unsigned int cpu = policy->cpu;
+ unsigned int dest_state;
+ int ret;
+
+ if (unlikely(eps_cpu[cpu] == NULL))
+ return -ENODEV;
+ centaur = eps_cpu[cpu];
+
+ if (unlikely(cpufreq_frequency_table_target(policy,
+ &eps_cpu[cpu]->freq_table[0],
+ target_freq,
+ relation,
+ &newstate))) {
+ return -EINVAL;
+ }
+
+ /* Make frequency transition */
+ dest_state = centaur->freq_table[newstate].index & 0xffff;
+ ret = eps_set_state(centaur, cpu, dest_state);
+ if (ret)
+ printk(KERN_ERR "eps: Timeout!\n");
+ return ret;
+}
+
+static int eps_verify(struct cpufreq_policy *policy)
+{
+ return cpufreq_frequency_table_verify(policy,
+ &eps_cpu[policy->cpu]->freq_table[0]);
+}
+
+static int eps_cpu_init(struct cpufreq_policy *policy)
+{
+ unsigned int i;
+ u32 lo, hi;
+ u64 val;
+ u8 current_multiplier, current_voltage;
+ u8 max_multiplier, max_voltage;
+ u8 min_multiplier, min_voltage;
+ u8 brand = 0;
+ u32 fsb;
+ struct eps_cpu_data *centaur;
+ struct cpuinfo_x86 *c = &cpu_data(0);
+ struct cpufreq_frequency_table *f_table;
+ int k, step, voltage;
+ int ret;
+ int states;
+
+ if (policy->cpu != 0)
+ return -ENODEV;
+
+ /* Check brand */
+ printk(KERN_INFO "eps: Detected VIA ");
+
+ switch (c->x86_model) {
+ case 10:
+ rdmsr(0x1153, lo, hi);
+ brand = (((lo >> 2) ^ lo) >> 18) & 3;
+ printk(KERN_CONT "Model A ");
+ break;
+ case 13:
+ rdmsr(0x1154, lo, hi);
+ brand = (((lo >> 4) ^ (lo >> 2))) & 0x000000ff;
+ printk(KERN_CONT "Model D ");
+ break;
+ }
+
+ switch (brand) {
+ case EPS_BRAND_C7M:
+ printk(KERN_CONT "C7-M\n");
+ break;
+ case EPS_BRAND_C7:
+ printk(KERN_CONT "C7\n");
+ break;
+ case EPS_BRAND_EDEN:
+ printk(KERN_CONT "Eden\n");
+ break;
+ case EPS_BRAND_C7D:
+ printk(KERN_CONT "C7-D\n");
+ break;
+ case EPS_BRAND_C3:
+ printk(KERN_CONT "C3\n");
+ return -ENODEV;
+ break;
+ }
+ /* Enable Enhanced PowerSaver */
+ rdmsrl(MSR_IA32_MISC_ENABLE, val);
+ if (!(val & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {
+ val |= MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP;
+ wrmsrl(MSR_IA32_MISC_ENABLE, val);
+ /* Can be locked at 0 */
+ rdmsrl(MSR_IA32_MISC_ENABLE, val);
+ if (!(val & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {
+ printk(KERN_INFO "eps: Can't enable Enhanced PowerSaver\n");
+ return -ENODEV;
+ }
+ }
+
+ /* Print voltage and multiplier */
+ rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
+ current_voltage = lo & 0xff;
+ printk(KERN_INFO "eps: Current voltage = %dmV\n",
+ current_voltage * 16 + 700);
+ current_multiplier = (lo >> 8) & 0xff;
+ printk(KERN_INFO "eps: Current multiplier = %d\n", current_multiplier);
+
+ /* Print limits */
+ max_voltage = hi & 0xff;
+ printk(KERN_INFO "eps: Highest voltage = %dmV\n",
+ max_voltage * 16 + 700);
+ max_multiplier = (hi >> 8) & 0xff;
+ printk(KERN_INFO "eps: Highest multiplier = %d\n", max_multiplier);
+ min_voltage = (hi >> 16) & 0xff;
+ printk(KERN_INFO "eps: Lowest voltage = %dmV\n",
+ min_voltage * 16 + 700);
+ min_multiplier = (hi >> 24) & 0xff;
+ printk(KERN_INFO "eps: Lowest multiplier = %d\n", min_multiplier);
+
+ /* Sanity checks */
+ if (current_multiplier == 0 || max_multiplier == 0
+ || min_multiplier == 0)
+ return -EINVAL;
+ if (current_multiplier > max_multiplier
+ || max_multiplier <= min_multiplier)
+ return -EINVAL;
+ if (current_voltage > 0x1f || max_voltage > 0x1f)
+ return -EINVAL;
+ if (max_voltage < min_voltage)
+ return -EINVAL;
+
+ /* Calc FSB speed */
+ fsb = cpu_khz / current_multiplier;
+ /* Calc number of p-states supported */
+ if (brand == EPS_BRAND_C7M)
+ states = max_multiplier - min_multiplier + 1;
+ else
+ states = 2;
+
+ /* Allocate private data and frequency table for current cpu */
+ centaur = kzalloc(sizeof(struct eps_cpu_data)
+ + (states + 1) * sizeof(struct cpufreq_frequency_table),
+ GFP_KERNEL);
+ if (!centaur)
+ return -ENOMEM;
+ eps_cpu[0] = centaur;
+
+ /* Copy basic values */
+ centaur->fsb = fsb;
+
+ /* Fill frequency and MSR value table */
+ f_table = &centaur->freq_table[0];
+ if (brand != EPS_BRAND_C7M) {
+ f_table[0].frequency = fsb * min_multiplier;
+ f_table[0].index = (min_multiplier << 8) | min_voltage;
+ f_table[1].frequency = fsb * max_multiplier;
+ f_table[1].index = (max_multiplier << 8) | max_voltage;
+ f_table[2].frequency = CPUFREQ_TABLE_END;
+ } else {
+ k = 0;
+ step = ((max_voltage - min_voltage) * 256)
+ / (max_multiplier - min_multiplier);
+ for (i = min_multiplier; i <= max_multiplier; i++) {
+ voltage = (k * step) / 256 + min_voltage;
+ f_table[k].frequency = fsb * i;
+ f_table[k].index = (i << 8) | voltage;
+ k++;
+ }
+ f_table[k].frequency = CPUFREQ_TABLE_END;
+ }
+
+ policy->cpuinfo.transition_latency = 140000; /* 844mV -> 700mV in ns */
+ policy->cur = fsb * current_multiplier;
+
+ ret = cpufreq_frequency_table_cpuinfo(policy, &centaur->freq_table[0]);
+ if (ret) {
+ kfree(centaur);
+ return ret;
+ }
+
+ cpufreq_frequency_table_get_attr(&centaur->freq_table[0], policy->cpu);
+ return 0;
+}
+
+static int eps_cpu_exit(struct cpufreq_policy *policy)
+{
+ unsigned int cpu = policy->cpu;
+ struct eps_cpu_data *centaur;
+ u32 lo, hi;
+
+ if (eps_cpu[cpu] == NULL)
+ return -ENODEV;
+ centaur = eps_cpu[cpu];
+
+ /* Get max frequency */
+ rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
+ /* Set max frequency */
+ eps_set_state(centaur, cpu, hi & 0xffff);
+ /* Bye */
+ cpufreq_frequency_table_put_attr(policy->cpu);
+ kfree(eps_cpu[cpu]);
+ eps_cpu[cpu] = NULL;
+ return 0;
+}
+
+static struct freq_attr *eps_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+static struct cpufreq_driver eps_driver = {
+ .verify = eps_verify,
+ .target = eps_target,
+ .init = eps_cpu_init,
+ .exit = eps_cpu_exit,
+ .get = eps_get,
+ .name = "e_powersaver",
+ .owner = THIS_MODULE,
+ .attr = eps_attr,
+};
+
+static int __init eps_init(void)
+{
+ struct cpuinfo_x86 *c = &cpu_data(0);
+
+ /* This driver will work only on Centaur C7 processors with
+ * Enhanced SpeedStep/PowerSaver registers */
+ if (c->x86_vendor != X86_VENDOR_CENTAUR
+ || c->x86 != 6 || c->x86_model < 10)
+ return -ENODEV;
+ if (!cpu_has(c, X86_FEATURE_EST))
+ return -ENODEV;
+
+ if (cpufreq_register_driver(&eps_driver))
+ return -EINVAL;
+ return 0;
+}
+
+static void __exit eps_exit(void)
+{
+ cpufreq_unregister_driver(&eps_driver);
+}
+
+MODULE_AUTHOR("Rafal Bilski <rafalbilski@interia.pl>");
+MODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's.");
+MODULE_LICENSE("GPL");
+
+module_init(eps_init);
+module_exit(eps_exit);
diff --git a/drivers/cpufreq/elanfreq.c b/drivers/cpufreq/elanfreq.c
new file mode 100644
index 00000000000..c587db472a7
--- /dev/null
+++ b/drivers/cpufreq/elanfreq.c
@@ -0,0 +1,309 @@
+/*
+ * elanfreq: cpufreq driver for the AMD ELAN family
+ *
+ * (c) Copyright 2002 Robert Schwebel <r.schwebel@pengutronix.de>
+ *
+ * Parts of this code are (c) Sven Geggus <sven@geggus.net>
+ *
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * 2002-02-13: - initial revision for 2.4.18-pre9 by Robert Schwebel
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/delay.h>
+#include <linux/cpufreq.h>
+
+#include <asm/msr.h>
+#include <linux/timex.h>
+#include <linux/io.h>
+
+#define REG_CSCIR 0x22 /* Chip Setup and Control Index Register */
+#define REG_CSCDR 0x23 /* Chip Setup and Control Data Register */
+
+/* Module parameter */
+static int max_freq;
+
+struct s_elan_multiplier {
+ int clock; /* frequency in kHz */
+ int val40h; /* PMU Force Mode register */
+ int val80h; /* CPU Clock Speed Register */
+};
+
+/*
+ * It is important that the frequencies
+ * are listed in ascending order here!
+ */
+static struct s_elan_multiplier elan_multiplier[] = {
+ {1000, 0x02, 0x18},
+ {2000, 0x02, 0x10},
+ {4000, 0x02, 0x08},
+ {8000, 0x00, 0x00},
+ {16000, 0x00, 0x02},
+ {33000, 0x00, 0x04},
+ {66000, 0x01, 0x04},
+ {99000, 0x01, 0x05}
+};
+
+static struct cpufreq_frequency_table elanfreq_table[] = {
+ {0, 1000},
+ {1, 2000},
+ {2, 4000},
+ {3, 8000},
+ {4, 16000},
+ {5, 33000},
+ {6, 66000},
+ {7, 99000},
+ {0, CPUFREQ_TABLE_END},
+};
+
+
+/**
+ * elanfreq_get_cpu_frequency: determine current cpu speed
+ *
+ * Finds out at which frequency the CPU of the Elan SOC runs
+ * at the moment. Frequencies from 1 to 33 MHz are generated
+ * the normal way, 66 and 99 MHz are called "Hyperspeed Mode"
+ * and have the rest of the chip running with 33 MHz.
+ */
+
+static unsigned int elanfreq_get_cpu_frequency(unsigned int cpu)
+{
+ u8 clockspeed_reg; /* Clock Speed Register */
+
+ local_irq_disable();
+ outb_p(0x80, REG_CSCIR);
+ clockspeed_reg = inb_p(REG_CSCDR);
+ local_irq_enable();
+
+ if ((clockspeed_reg & 0xE0) == 0xE0)
+ return 0;
+
+ /* Are we in CPU clock multiplied mode (66/99 MHz)? */
+ if ((clockspeed_reg & 0xE0) == 0xC0) {
+ if ((clockspeed_reg & 0x01) == 0)
+ return 66000;
+ else
+ return 99000;
+ }
+
+ /* 33 MHz is not 32 MHz... */
+ if ((clockspeed_reg & 0xE0) == 0xA0)
+ return 33000;
+
+ return (1<<((clockspeed_reg & 0xE0) >> 5)) * 1000;
+}
+
+
+/**
+ * elanfreq_set_cpu_frequency: Change the CPU core frequency
+ * @cpu: cpu number
+ * @freq: frequency in kHz
+ *
+ * This function takes a frequency value and changes the CPU frequency
+ * according to this. Note that the frequency has to be checked by
+ * elanfreq_validatespeed() for correctness!
+ *
+ * There is no return value.
+ */
+
+static void elanfreq_set_cpu_state(unsigned int state)
+{
+ struct cpufreq_freqs freqs;
+
+ freqs.old = elanfreq_get_cpu_frequency(0);
+ freqs.new = elan_multiplier[state].clock;
+ freqs.cpu = 0; /* elanfreq.c is UP only driver */
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ printk(KERN_INFO "elanfreq: attempting to set frequency to %i kHz\n",
+ elan_multiplier[state].clock);
+
+
+ /*
+ * Access to the Elan's internal registers is indexed via
+ * 0x22: Chip Setup & Control Register Index Register (CSCI)
+ * 0x23: Chip Setup & Control Register Data Register (CSCD)
+ *
+ */
+
+ /*
+ * 0x40 is the Power Management Unit's Force Mode Register.
+ * Bit 6 enables Hyperspeed Mode (66/100 MHz core frequency)
+ */
+
+ local_irq_disable();
+ outb_p(0x40, REG_CSCIR); /* Disable hyperspeed mode */
+ outb_p(0x00, REG_CSCDR);
+ local_irq_enable(); /* wait till internal pipelines and */
+ udelay(1000); /* buffers have cleaned up */
+
+ local_irq_disable();
+
+ /* now, set the CPU clock speed register (0x80) */
+ outb_p(0x80, REG_CSCIR);
+ outb_p(elan_multiplier[state].val80h, REG_CSCDR);
+
+ /* now, the hyperspeed bit in PMU Force Mode Register (0x40) */
+ outb_p(0x40, REG_CSCIR);
+ outb_p(elan_multiplier[state].val40h, REG_CSCDR);
+ udelay(10000);
+ local_irq_enable();
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+};
+
+
+/**
+ * elanfreq_validatespeed: test if frequency range is valid
+ * @policy: the policy to validate
+ *
+ * This function checks if a given frequency range in kHz is valid
+ * for the hardware supported by the driver.
+ */
+
+static int elanfreq_verify(struct cpufreq_policy *policy)
+{
+ return cpufreq_frequency_table_verify(policy, &elanfreq_table[0]);
+}
+
+static int elanfreq_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ unsigned int newstate = 0;
+
+ if (cpufreq_frequency_table_target(policy, &elanfreq_table[0],
+ target_freq, relation, &newstate))
+ return -EINVAL;
+
+ elanfreq_set_cpu_state(newstate);
+
+ return 0;
+}
+
+
+/*
+ * Module init and exit code
+ */
+
+static int elanfreq_cpu_init(struct cpufreq_policy *policy)
+{
+ struct cpuinfo_x86 *c = &cpu_data(0);
+ unsigned int i;
+ int result;
+
+ /* capability check */
+ if ((c->x86_vendor != X86_VENDOR_AMD) ||
+ (c->x86 != 4) || (c->x86_model != 10))
+ return -ENODEV;
+
+ /* max freq */
+ if (!max_freq)
+ max_freq = elanfreq_get_cpu_frequency(0);
+
+ /* table init */
+ for (i = 0; (elanfreq_table[i].frequency != CPUFREQ_TABLE_END); i++) {
+ if (elanfreq_table[i].frequency > max_freq)
+ elanfreq_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+ }
+
+ /* cpuinfo and default policy values */
+ policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+ policy->cur = elanfreq_get_cpu_frequency(0);
+
+ result = cpufreq_frequency_table_cpuinfo(policy, elanfreq_table);
+ if (result)
+ return result;
+
+ cpufreq_frequency_table_get_attr(elanfreq_table, policy->cpu);
+ return 0;
+}
+
+
+static int elanfreq_cpu_exit(struct cpufreq_policy *policy)
+{
+ cpufreq_frequency_table_put_attr(policy->cpu);
+ return 0;
+}
+
+
+#ifndef MODULE
+/**
+ * elanfreq_setup - elanfreq command line parameter parsing
+ *
+ * elanfreq command line parameter. Use:
+ * elanfreq=66000
+ * to set the maximum CPU frequency to 66 MHz. Note that in
+ * case you do not give this boot parameter, the maximum
+ * frequency will fall back to _current_ CPU frequency which
+ * might be lower. If you build this as a module, use the
+ * max_freq module parameter instead.
+ */
+static int __init elanfreq_setup(char *str)
+{
+ max_freq = simple_strtoul(str, &str, 0);
+ printk(KERN_WARNING "You're using the deprecated elanfreq command line option. Use elanfreq.max_freq instead, please!\n");
+ return 1;
+}
+__setup("elanfreq=", elanfreq_setup);
+#endif
+
+
+static struct freq_attr *elanfreq_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+
+static struct cpufreq_driver elanfreq_driver = {
+ .get = elanfreq_get_cpu_frequency,
+ .verify = elanfreq_verify,
+ .target = elanfreq_target,
+ .init = elanfreq_cpu_init,
+ .exit = elanfreq_cpu_exit,
+ .name = "elanfreq",
+ .owner = THIS_MODULE,
+ .attr = elanfreq_attr,
+};
+
+
+static int __init elanfreq_init(void)
+{
+ struct cpuinfo_x86 *c = &cpu_data(0);
+
+ /* Test if we have the right hardware */
+ if ((c->x86_vendor != X86_VENDOR_AMD) ||
+ (c->x86 != 4) || (c->x86_model != 10)) {
+ printk(KERN_INFO "elanfreq: error: no Elan processor found!\n");
+ return -ENODEV;
+ }
+ return cpufreq_register_driver(&elanfreq_driver);
+}
+
+
+static void __exit elanfreq_exit(void)
+{
+ cpufreq_unregister_driver(&elanfreq_driver);
+}
+
+
+module_param(max_freq, int, 0444);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Robert Schwebel <r.schwebel@pengutronix.de>, "
+ "Sven Geggus <sven@geggus.net>");
+MODULE_DESCRIPTION("cpufreq driver for AMD's Elan CPUs");
+
+module_init(elanfreq_init);
+module_exit(elanfreq_exit);
diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c
index 05432216e22..90431cb9280 100644
--- a/drivers/cpufreq/freq_table.c
+++ b/drivers/cpufreq/freq_table.c
@@ -14,9 +14,6 @@
#include <linux/init.h>
#include <linux/cpufreq.h>
-#define dprintk(msg...) \
- cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, "freq-table", msg)
-
/*********************************************************************
* FREQUENCY TABLE HELPERS *
*********************************************************************/
@@ -31,11 +28,11 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
unsigned int freq = table[i].frequency;
if (freq == CPUFREQ_ENTRY_INVALID) {
- dprintk("table entry %u is invalid, skipping\n", i);
+ pr_debug("table entry %u is invalid, skipping\n", i);
continue;
}
- dprintk("table entry %u: %u kHz, %u index\n",
+ pr_debug("table entry %u: %u kHz, %u index\n",
i, freq, table[i].index);
if (freq < min_freq)
min_freq = freq;
@@ -61,7 +58,7 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
unsigned int i;
unsigned int count = 0;
- dprintk("request for verification of policy (%u - %u kHz) for cpu %u\n",
+ pr_debug("request for verification of policy (%u - %u kHz) for cpu %u\n",
policy->min, policy->max, policy->cpu);
if (!cpu_online(policy->cpu))
@@ -86,7 +83,7 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
policy->cpuinfo.max_freq);
- dprintk("verification lead to (%u - %u kHz) for cpu %u\n",
+ pr_debug("verification lead to (%u - %u kHz) for cpu %u\n",
policy->min, policy->max, policy->cpu);
return 0;
@@ -110,7 +107,7 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
};
unsigned int i;
- dprintk("request for target %u kHz (relation: %u) for cpu %u\n",
+ pr_debug("request for target %u kHz (relation: %u) for cpu %u\n",
target_freq, relation, policy->cpu);
switch (relation) {
@@ -167,7 +164,7 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
} else
*index = optimal.index;
- dprintk("target is %u (%u kHz, %u)\n", *index, table[*index].frequency,
+ pr_debug("target is %u (%u kHz, %u)\n", *index, table[*index].frequency,
table[*index].index);
return 0;
@@ -216,14 +213,14 @@ EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs);
void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table,
unsigned int cpu)
{
- dprintk("setting show_table for cpu %u to %p\n", cpu, table);
+ pr_debug("setting show_table for cpu %u to %p\n", cpu, table);
per_cpu(cpufreq_show_table, cpu) = table;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_attr);
void cpufreq_frequency_table_put_attr(unsigned int cpu)
{
- dprintk("clearing show_table for cpu %u\n", cpu);
+ pr_debug("clearing show_table for cpu %u\n", cpu);
per_cpu(cpufreq_show_table, cpu) = NULL;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_put_attr);
diff --git a/drivers/cpufreq/gx-suspmod.c b/drivers/cpufreq/gx-suspmod.c
new file mode 100644
index 00000000000..ffe1f2c92ed
--- /dev/null
+++ b/drivers/cpufreq/gx-suspmod.c
@@ -0,0 +1,514 @@
+/*
+ * Cyrix MediaGX and NatSemi Geode Suspend Modulation
+ * (C) 2002 Zwane Mwaikambo <zwane@commfireservices.com>
+ * (C) 2002 Hiroshi Miura <miura@da-cha.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation
+ *
+ * The author(s) of this software shall not be held liable for damages
+ * of any nature resulting due to the use of this software. This
+ * software is provided AS-IS with no warranties.
+ *
+ * Theoretical note:
+ *
+ * (see Geode(tm) CS5530 manual (rev.4.1) page.56)
+ *
+ * CPU frequency control on NatSemi Geode GX1/GXLV processor and CS55x0
+ * are based on Suspend Modulation.
+ *
+ * Suspend Modulation works by asserting and de-asserting the SUSP# pin
+ * to CPU(GX1/GXLV) for configurable durations. When asserting SUSP#
+ * the CPU enters an idle state. GX1 stops its core clock when SUSP# is
+ * asserted then power consumption is reduced.
+ *
+ * Suspend Modulation's OFF/ON duration are configurable
+ * with 'Suspend Modulation OFF Count Register'
+ * and 'Suspend Modulation ON Count Register'.
+ * These registers are 8bit counters that represent the number of
+ * 32us intervals which the SUSP# pin is asserted(ON)/de-asserted(OFF)
+ * to the processor.
+ *
+ * These counters define a ratio which is the effective frequency
+ * of operation of the system.
+ *
+ * OFF Count
+ * F_eff = Fgx * ----------------------
+ * OFF Count + ON Count
+ *
+ * 0 <= On Count, Off Count <= 255
+ *
+ * From these limits, we can get register values
+ *
+ * off_duration + on_duration <= MAX_DURATION
+ * on_duration = off_duration * (stock_freq - freq) / freq
+ *
+ * off_duration = (freq * DURATION) / stock_freq
+ * on_duration = DURATION - off_duration
+ *
+ *
+ *---------------------------------------------------------------------------
+ *
+ * ChangeLog:
+ * Dec. 12, 2003 Hiroshi Miura <miura@da-cha.org>
+ * - fix on/off register mistake
+ * - fix cpu_khz calc when it stops cpu modulation.
+ *
+ * Dec. 11, 2002 Hiroshi Miura <miura@da-cha.org>
+ * - rewrite for Cyrix MediaGX Cx5510/5520 and
+ * NatSemi Geode Cs5530(A).
+ *
+ * Jul. ??, 2002 Zwane Mwaikambo <zwane@commfireservices.com>
+ * - cs5530_mod patch for 2.4.19-rc1.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * Todo
+ * Test on machines with 5510, 5530, 5530A
+ */
+
+/************************************************************************
+ * Suspend Modulation - Definitions *
+ ************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/cpufreq.h>
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#include <asm/processor-cyrix.h>
+
+/* PCI config registers, all at F0 */
+#define PCI_PMER1 0x80 /* power management enable register 1 */
+#define PCI_PMER2 0x81 /* power management enable register 2 */
+#define PCI_PMER3 0x82 /* power management enable register 3 */
+#define PCI_IRQTC 0x8c /* irq speedup timer counter register:typical 2 to 4ms */
+#define PCI_VIDTC 0x8d /* video speedup timer counter register: typical 50 to 100ms */
+#define PCI_MODOFF 0x94 /* suspend modulation OFF counter register, 1 = 32us */
+#define PCI_MODON 0x95 /* suspend modulation ON counter register */
+#define PCI_SUSCFG 0x96 /* suspend configuration register */
+
+/* PMER1 bits */
+#define GPM (1<<0) /* global power management */
+#define GIT (1<<1) /* globally enable PM device idle timers */
+#define GTR (1<<2) /* globally enable IO traps */
+#define IRQ_SPDUP (1<<3) /* disable clock throttle during interrupt handling */
+#define VID_SPDUP (1<<4) /* disable clock throttle during vga video handling */
+
+/* SUSCFG bits */
+#define SUSMOD (1<<0) /* enable/disable suspend modulation */
+/* the below is supported only with cs5530 (after rev.1.2)/cs5530A */
+#define SMISPDUP (1<<1) /* select how SMI re-enable suspend modulation: */
+ /* IRQTC timer or read SMI speedup disable reg.(F1BAR[08-09h]) */
+#define SUSCFG (1<<2) /* enable powering down a GXLV processor. "Special 3Volt Suspend" mode */
+/* the below is supported only with cs5530A */
+#define PWRSVE_ISA (1<<3) /* stop ISA clock */
+#define PWRSVE (1<<4) /* active idle */
+
+struct gxfreq_params {
+ u8 on_duration;
+ u8 off_duration;
+ u8 pci_suscfg;
+ u8 pci_pmer1;
+ u8 pci_pmer2;
+ struct pci_dev *cs55x0;
+};
+
+static struct gxfreq_params *gx_params;
+static int stock_freq;
+
+/* PCI bus clock - defaults to 30.000 if cpu_khz is not available */
+static int pci_busclk;
+module_param(pci_busclk, int, 0444);
+
+/* maximum duration for which the cpu may be suspended
+ * (32us * MAX_DURATION). If no parameter is given, this defaults
+ * to 255.
+ * Note that this leads to a maximum of 8 ms(!) where the CPU clock
+ * is suspended -- processing power is just 0.39% of what it used to be,
+ * though. 781.25 kHz(!) for a 200 MHz processor -- wow. */
+static int max_duration = 255;
+module_param(max_duration, int, 0444);
+
+/* For the default policy, we want at least some processing power
+ * - let's say 5%. (min = maxfreq / POLICY_MIN_DIV)
+ */
+#define POLICY_MIN_DIV 20
+
+
+/**
+ * we can detect a core multipiler from dir0_lsb
+ * from GX1 datasheet p.56,
+ * MULT[3:0]:
+ * 0000 = SYSCLK multiplied by 4 (test only)
+ * 0001 = SYSCLK multiplied by 10
+ * 0010 = SYSCLK multiplied by 4
+ * 0011 = SYSCLK multiplied by 6
+ * 0100 = SYSCLK multiplied by 9
+ * 0101 = SYSCLK multiplied by 5
+ * 0110 = SYSCLK multiplied by 7
+ * 0111 = SYSCLK multiplied by 8
+ * of 33.3MHz
+ **/
+static int gx_freq_mult[16] = {
+ 4, 10, 4, 6, 9, 5, 7, 8,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+
+/****************************************************************
+ * Low Level chipset interface *
+ ****************************************************************/
+static struct pci_device_id gx_chipset_tbl[] __initdata = {
+ { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY), },
+ { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5520), },
+ { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5510), },
+ { 0, },
+};
+
+static void gx_write_byte(int reg, int value)
+{
+ pci_write_config_byte(gx_params->cs55x0, reg, value);
+}
+
+/**
+ * gx_detect_chipset:
+ *
+ **/
+static __init struct pci_dev *gx_detect_chipset(void)
+{
+ struct pci_dev *gx_pci = NULL;
+
+ /* check if CPU is a MediaGX or a Geode. */
+ if ((boot_cpu_data.x86_vendor != X86_VENDOR_NSC) &&
+ (boot_cpu_data.x86_vendor != X86_VENDOR_CYRIX)) {
+ pr_debug("error: no MediaGX/Geode processor found!\n");
+ return NULL;
+ }
+
+ /* detect which companion chip is used */
+ for_each_pci_dev(gx_pci) {
+ if ((pci_match_id(gx_chipset_tbl, gx_pci)) != NULL)
+ return gx_pci;
+ }
+
+ pr_debug("error: no supported chipset found!\n");
+ return NULL;
+}
+
+/**
+ * gx_get_cpuspeed:
+ *
+ * Finds out at which efficient frequency the Cyrix MediaGX/NatSemi
+ * Geode CPU runs.
+ */
+static unsigned int gx_get_cpuspeed(unsigned int cpu)
+{
+ if ((gx_params->pci_suscfg & SUSMOD) == 0)
+ return stock_freq;
+
+ return (stock_freq * gx_params->off_duration)
+ / (gx_params->on_duration + gx_params->off_duration);
+}
+
+/**
+ * gx_validate_speed:
+ * determine current cpu speed
+ *
+ **/
+
+static unsigned int gx_validate_speed(unsigned int khz, u8 *on_duration,
+ u8 *off_duration)
+{
+ unsigned int i;
+ u8 tmp_on, tmp_off;
+ int old_tmp_freq = stock_freq;
+ int tmp_freq;
+
+ *off_duration = 1;
+ *on_duration = 0;
+
+ for (i = max_duration; i > 0; i--) {
+ tmp_off = ((khz * i) / stock_freq) & 0xff;
+ tmp_on = i - tmp_off;
+ tmp_freq = (stock_freq * tmp_off) / i;
+ /* if this relation is closer to khz, use this. If it's equal,
+ * prefer it, too - lower latency */
+ if (abs(tmp_freq - khz) <= abs(old_tmp_freq - khz)) {
+ *on_duration = tmp_on;
+ *off_duration = tmp_off;
+ old_tmp_freq = tmp_freq;
+ }
+ }
+
+ return old_tmp_freq;
+}
+
+
+/**
+ * gx_set_cpuspeed:
+ * set cpu speed in khz.
+ **/
+
+static void gx_set_cpuspeed(unsigned int khz)
+{
+ u8 suscfg, pmer1;
+ unsigned int new_khz;
+ unsigned long flags;
+ struct cpufreq_freqs freqs;
+
+ freqs.cpu = 0;
+ freqs.old = gx_get_cpuspeed(0);
+
+ new_khz = gx_validate_speed(khz, &gx_params->on_duration,
+ &gx_params->off_duration);
+
+ freqs.new = new_khz;
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ local_irq_save(flags);
+
+
+
+ if (new_khz != stock_freq) {
+ /* if new khz == 100% of CPU speed, it is special case */
+ switch (gx_params->cs55x0->device) {
+ case PCI_DEVICE_ID_CYRIX_5530_LEGACY:
+ pmer1 = gx_params->pci_pmer1 | IRQ_SPDUP | VID_SPDUP;
+ /* FIXME: need to test other values -- Zwane,Miura */
+ /* typical 2 to 4ms */
+ gx_write_byte(PCI_IRQTC, 4);
+ /* typical 50 to 100ms */
+ gx_write_byte(PCI_VIDTC, 100);
+ gx_write_byte(PCI_PMER1, pmer1);
+
+ if (gx_params->cs55x0->revision < 0x10) {
+ /* CS5530(rev 1.2, 1.3) */
+ suscfg = gx_params->pci_suscfg|SUSMOD;
+ } else {
+ /* CS5530A,B.. */
+ suscfg = gx_params->pci_suscfg|SUSMOD|PWRSVE;
+ }
+ break;
+ case PCI_DEVICE_ID_CYRIX_5520:
+ case PCI_DEVICE_ID_CYRIX_5510:
+ suscfg = gx_params->pci_suscfg | SUSMOD;
+ break;
+ default:
+ local_irq_restore(flags);
+ pr_debug("fatal: try to set unknown chipset.\n");
+ return;
+ }
+ } else {
+ suscfg = gx_params->pci_suscfg & ~(SUSMOD);
+ gx_params->off_duration = 0;
+ gx_params->on_duration = 0;
+ pr_debug("suspend modulation disabled: cpu runs 100%% speed.\n");
+ }
+
+ gx_write_byte(PCI_MODOFF, gx_params->off_duration);
+ gx_write_byte(PCI_MODON, gx_params->on_duration);
+
+ gx_write_byte(PCI_SUSCFG, suscfg);
+ pci_read_config_byte(gx_params->cs55x0, PCI_SUSCFG, &suscfg);
+
+ local_irq_restore(flags);
+
+ gx_params->pci_suscfg = suscfg;
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+ pr_debug("suspend modulation w/ duration of ON:%d us, OFF:%d us\n",
+ gx_params->on_duration * 32, gx_params->off_duration * 32);
+ pr_debug("suspend modulation w/ clock speed: %d kHz.\n", freqs.new);
+}
+
+/****************************************************************
+ * High level functions *
+ ****************************************************************/
+
+/*
+ * cpufreq_gx_verify: test if frequency range is valid
+ *
+ * This function checks if a given frequency range in kHz is valid
+ * for the hardware supported by the driver.
+ */
+
+static int cpufreq_gx_verify(struct cpufreq_policy *policy)
+{
+ unsigned int tmp_freq = 0;
+ u8 tmp1, tmp2;
+
+ if (!stock_freq || !policy)
+ return -EINVAL;
+
+ policy->cpu = 0;
+ cpufreq_verify_within_limits(policy, (stock_freq / max_duration),
+ stock_freq);
+
+ /* it needs to be assured that at least one supported frequency is
+ * within policy->min and policy->max. If it is not, policy->max
+ * needs to be increased until one freuqency is supported.
+ * policy->min may not be decreased, though. This way we guarantee a
+ * specific processing capacity.
+ */
+ tmp_freq = gx_validate_speed(policy->min, &tmp1, &tmp2);
+ if (tmp_freq < policy->min)
+ tmp_freq += stock_freq / max_duration;
+ policy->min = tmp_freq;
+ if (policy->min > policy->max)
+ policy->max = tmp_freq;
+ tmp_freq = gx_validate_speed(policy->max, &tmp1, &tmp2);
+ if (tmp_freq > policy->max)
+ tmp_freq -= stock_freq / max_duration;
+ policy->max = tmp_freq;
+ if (policy->max < policy->min)
+ policy->max = policy->min;
+ cpufreq_verify_within_limits(policy, (stock_freq / max_duration),
+ stock_freq);
+
+ return 0;
+}
+
+/*
+ * cpufreq_gx_target:
+ *
+ */
+static int cpufreq_gx_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ u8 tmp1, tmp2;
+ unsigned int tmp_freq;
+
+ if (!stock_freq || !policy)
+ return -EINVAL;
+
+ policy->cpu = 0;
+
+ tmp_freq = gx_validate_speed(target_freq, &tmp1, &tmp2);
+ while (tmp_freq < policy->min) {
+ tmp_freq += stock_freq / max_duration;
+ tmp_freq = gx_validate_speed(tmp_freq, &tmp1, &tmp2);
+ }
+ while (tmp_freq > policy->max) {
+ tmp_freq -= stock_freq / max_duration;
+ tmp_freq = gx_validate_speed(tmp_freq, &tmp1, &tmp2);
+ }
+
+ gx_set_cpuspeed(tmp_freq);
+
+ return 0;
+}
+
+static int cpufreq_gx_cpu_init(struct cpufreq_policy *policy)
+{
+ unsigned int maxfreq, curfreq;
+
+ if (!policy || policy->cpu != 0)
+ return -ENODEV;
+
+ /* determine maximum frequency */
+ if (pci_busclk)
+ maxfreq = pci_busclk * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f];
+ else if (cpu_khz)
+ maxfreq = cpu_khz;
+ else
+ maxfreq = 30000 * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f];
+
+ stock_freq = maxfreq;
+ curfreq = gx_get_cpuspeed(0);
+
+ pr_debug("cpu max frequency is %d.\n", maxfreq);
+ pr_debug("cpu current frequency is %dkHz.\n", curfreq);
+
+ /* setup basic struct for cpufreq API */
+ policy->cpu = 0;
+
+ if (max_duration < POLICY_MIN_DIV)
+ policy->min = maxfreq / max_duration;
+ else
+ policy->min = maxfreq / POLICY_MIN_DIV;
+ policy->max = maxfreq;
+ policy->cur = curfreq;
+ policy->cpuinfo.min_freq = maxfreq / max_duration;
+ policy->cpuinfo.max_freq = maxfreq;
+ policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+
+ return 0;
+}
+
+/*
+ * cpufreq_gx_init:
+ * MediaGX/Geode GX initialize cpufreq driver
+ */
+static struct cpufreq_driver gx_suspmod_driver = {
+ .get = gx_get_cpuspeed,
+ .verify = cpufreq_gx_verify,
+ .target = cpufreq_gx_target,
+ .init = cpufreq_gx_cpu_init,
+ .name = "gx-suspmod",
+ .owner = THIS_MODULE,
+};
+
+static int __init cpufreq_gx_init(void)
+{
+ int ret;
+ struct gxfreq_params *params;
+ struct pci_dev *gx_pci;
+
+ /* Test if we have the right hardware */
+ gx_pci = gx_detect_chipset();
+ if (gx_pci == NULL)
+ return -ENODEV;
+
+ /* check whether module parameters are sane */
+ if (max_duration > 0xff)
+ max_duration = 0xff;
+
+ pr_debug("geode suspend modulation available.\n");
+
+ params = kzalloc(sizeof(struct gxfreq_params), GFP_KERNEL);
+ if (params == NULL)
+ return -ENOMEM;
+
+ params->cs55x0 = gx_pci;
+ gx_params = params;
+
+ /* keep cs55x0 configurations */
+ pci_read_config_byte(params->cs55x0, PCI_SUSCFG, &(params->pci_suscfg));
+ pci_read_config_byte(params->cs55x0, PCI_PMER1, &(params->pci_pmer1));
+ pci_read_config_byte(params->cs55x0, PCI_PMER2, &(params->pci_pmer2));
+ pci_read_config_byte(params->cs55x0, PCI_MODON, &(params->on_duration));
+ pci_read_config_byte(params->cs55x0, PCI_MODOFF,
+ &(params->off_duration));
+
+ ret = cpufreq_register_driver(&gx_suspmod_driver);
+ if (ret) {
+ kfree(params);
+ return ret; /* register error! */
+ }
+
+ return 0;
+}
+
+static void __exit cpufreq_gx_exit(void)
+{
+ cpufreq_unregister_driver(&gx_suspmod_driver);
+ pci_dev_put(gx_params->cs55x0);
+ kfree(gx_params);
+}
+
+MODULE_AUTHOR("Hiroshi Miura <miura@da-cha.org>");
+MODULE_DESCRIPTION("Cpufreq driver for Cyrix MediaGX and NatSemi Geode");
+MODULE_LICENSE("GPL");
+
+module_init(cpufreq_gx_init);
+module_exit(cpufreq_gx_exit);
+
diff --git a/drivers/cpufreq/longhaul.c b/drivers/cpufreq/longhaul.c
new file mode 100644
index 00000000000..f47d26e2a13
--- /dev/null
+++ b/drivers/cpufreq/longhaul.c
@@ -0,0 +1,1024 @@
+/*
+ * (C) 2001-2004 Dave Jones. <davej@redhat.com>
+ * (C) 2002 Padraig Brady. <padraig@antefacto.com>
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ * Based upon datasheets & sample CPUs kindly provided by VIA.
+ *
+ * VIA have currently 3 different versions of Longhaul.
+ * Version 1 (Longhaul) uses the BCR2 MSR at 0x1147.
+ * It is present only in Samuel 1 (C5A), Samuel 2 (C5B) stepping 0.
+ * Version 2 of longhaul is backward compatible with v1, but adds
+ * LONGHAUL MSR for purpose of both frequency and voltage scaling.
+ * Present in Samuel 2 (steppings 1-7 only) (C5B), and Ezra (C5C).
+ * Version 3 of longhaul got renamed to Powersaver and redesigned
+ * to use only the POWERSAVER MSR at 0x110a.
+ * It is present in Ezra-T (C5M), Nehemiah (C5X) and above.
+ * It's pretty much the same feature wise to longhaul v2, though
+ * there is provision for scaling FSB too, but this doesn't work
+ * too well in practice so we don't even try to use this.
+ *
+ * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/timex.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+
+#include <asm/msr.h>
+#include <acpi/processor.h>
+
+#include "longhaul.h"
+
+#define PFX "longhaul: "
+
+#define TYPE_LONGHAUL_V1 1
+#define TYPE_LONGHAUL_V2 2
+#define TYPE_POWERSAVER 3
+
+#define CPU_SAMUEL 1
+#define CPU_SAMUEL2 2
+#define CPU_EZRA 3
+#define CPU_EZRA_T 4
+#define CPU_NEHEMIAH 5
+#define CPU_NEHEMIAH_C 6
+
+/* Flags */
+#define USE_ACPI_C3 (1 << 1)
+#define USE_NORTHBRIDGE (1 << 2)
+
+static int cpu_model;
+static unsigned int numscales = 16;
+static unsigned int fsb;
+
+static const struct mV_pos *vrm_mV_table;
+static const unsigned char *mV_vrm_table;
+
+static unsigned int highest_speed, lowest_speed; /* kHz */
+static unsigned int minmult, maxmult;
+static int can_scale_voltage;
+static struct acpi_processor *pr;
+static struct acpi_processor_cx *cx;
+static u32 acpi_regs_addr;
+static u8 longhaul_flags;
+static unsigned int longhaul_index;
+
+/* Module parameters */
+static int scale_voltage;
+static int disable_acpi_c3;
+static int revid_errata;
+
+
+/* Clock ratios multiplied by 10 */
+static int mults[32];
+static int eblcr[32];
+static int longhaul_version;
+static struct cpufreq_frequency_table *longhaul_table;
+
+static char speedbuffer[8];
+
+static char *print_speed(int speed)
+{
+ if (speed < 1000) {
+ snprintf(speedbuffer, sizeof(speedbuffer), "%dMHz", speed);
+ return speedbuffer;
+ }
+
+ if (speed%1000 == 0)
+ snprintf(speedbuffer, sizeof(speedbuffer),
+ "%dGHz", speed/1000);
+ else
+ snprintf(speedbuffer, sizeof(speedbuffer),
+ "%d.%dGHz", speed/1000, (speed%1000)/100);
+
+ return speedbuffer;
+}
+
+
+static unsigned int calc_speed(int mult)
+{
+ int khz;
+ khz = (mult/10)*fsb;
+ if (mult%10)
+ khz += fsb/2;
+ khz *= 1000;
+ return khz;
+}
+
+
+static int longhaul_get_cpu_mult(void)
+{
+ unsigned long invalue = 0, lo, hi;
+
+ rdmsr(MSR_IA32_EBL_CR_POWERON, lo, hi);
+ invalue = (lo & (1<<22|1<<23|1<<24|1<<25))>>22;
+ if (longhaul_version == TYPE_LONGHAUL_V2 ||
+ longhaul_version == TYPE_POWERSAVER) {
+ if (lo & (1<<27))
+ invalue += 16;
+ }
+ return eblcr[invalue];
+}
+
+/* For processor with BCR2 MSR */
+
+static void do_longhaul1(unsigned int mults_index)
+{
+ union msr_bcr2 bcr2;
+
+ rdmsrl(MSR_VIA_BCR2, bcr2.val);
+ /* Enable software clock multiplier */
+ bcr2.bits.ESOFTBF = 1;
+ bcr2.bits.CLOCKMUL = mults_index & 0xff;
+
+ /* Sync to timer tick */
+ safe_halt();
+ /* Change frequency on next halt or sleep */
+ wrmsrl(MSR_VIA_BCR2, bcr2.val);
+ /* Invoke transition */
+ ACPI_FLUSH_CPU_CACHE();
+ halt();
+
+ /* Disable software clock multiplier */
+ local_irq_disable();
+ rdmsrl(MSR_VIA_BCR2, bcr2.val);
+ bcr2.bits.ESOFTBF = 0;
+ wrmsrl(MSR_VIA_BCR2, bcr2.val);
+}
+
+/* For processor with Longhaul MSR */
+
+static void do_powersaver(int cx_address, unsigned int mults_index,
+ unsigned int dir)
+{
+ union msr_longhaul longhaul;
+ u32 t;
+
+ rdmsrl(MSR_VIA_LONGHAUL, longhaul.val);
+ /* Setup new frequency */
+ if (!revid_errata)
+ longhaul.bits.RevisionKey = longhaul.bits.RevisionID;
+ else
+ longhaul.bits.RevisionKey = 0;
+ longhaul.bits.SoftBusRatio = mults_index & 0xf;
+ longhaul.bits.SoftBusRatio4 = (mults_index & 0x10) >> 4;
+ /* Setup new voltage */
+ if (can_scale_voltage)
+ longhaul.bits.SoftVID = (mults_index >> 8) & 0x1f;
+ /* Sync to timer tick */
+ safe_halt();
+ /* Raise voltage if necessary */
+ if (can_scale_voltage && dir) {
+ longhaul.bits.EnableSoftVID = 1;
+ wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
+ /* Change voltage */
+ if (!cx_address) {
+ ACPI_FLUSH_CPU_CACHE();
+ halt();
+ } else {
+ ACPI_FLUSH_CPU_CACHE();
+ /* Invoke C3 */
+ inb(cx_address);
+ /* Dummy op - must do something useless after P_LVL3
+ * read */
+ t = inl(acpi_gbl_FADT.xpm_timer_block.address);
+ }
+ longhaul.bits.EnableSoftVID = 0;
+ wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
+ }
+
+ /* Change frequency on next halt or sleep */
+ longhaul.bits.EnableSoftBusRatio = 1;
+ wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
+ if (!cx_address) {
+ ACPI_FLUSH_CPU_CACHE();
+ halt();
+ } else {
+ ACPI_FLUSH_CPU_CACHE();
+ /* Invoke C3 */
+ inb(cx_address);
+ /* Dummy op - must do something useless after P_LVL3 read */
+ t = inl(acpi_gbl_FADT.xpm_timer_block.address);
+ }
+ /* Disable bus ratio bit */
+ longhaul.bits.EnableSoftBusRatio = 0;
+ wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
+
+ /* Reduce voltage if necessary */
+ if (can_scale_voltage && !dir) {
+ longhaul.bits.EnableSoftVID = 1;
+ wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
+ /* Change voltage */
+ if (!cx_address) {
+ ACPI_FLUSH_CPU_CACHE();
+ halt();
+ } else {
+ ACPI_FLUSH_CPU_CACHE();
+ /* Invoke C3 */
+ inb(cx_address);
+ /* Dummy op - must do something useless after P_LVL3
+ * read */
+ t = inl(acpi_gbl_FADT.xpm_timer_block.address);
+ }
+ longhaul.bits.EnableSoftVID = 0;
+ wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
+ }
+}
+
+/**
+ * longhaul_set_cpu_frequency()
+ * @mults_index : bitpattern of the new multiplier.
+ *
+ * Sets a new clock ratio.
+ */
+
+static void longhaul_setstate(unsigned int table_index)
+{
+ unsigned int mults_index;
+ int speed, mult;
+ struct cpufreq_freqs freqs;
+ unsigned long flags;
+ unsigned int pic1_mask, pic2_mask;
+ u16 bm_status = 0;
+ u32 bm_timeout = 1000;
+ unsigned int dir = 0;
+
+ mults_index = longhaul_table[table_index].index;
+ /* Safety precautions */
+ mult = mults[mults_index & 0x1f];
+ if (mult == -1)
+ return;
+ speed = calc_speed(mult);
+ if ((speed > highest_speed) || (speed < lowest_speed))
+ return;
+ /* Voltage transition before frequency transition? */
+ if (can_scale_voltage && longhaul_index < table_index)
+ dir = 1;
+
+ freqs.old = calc_speed(longhaul_get_cpu_mult());
+ freqs.new = speed;
+ freqs.cpu = 0; /* longhaul.c is UP only driver */
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ pr_debug("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n",
+ fsb, mult/10, mult%10, print_speed(speed/1000));
+retry_loop:
+ preempt_disable();
+ local_irq_save(flags);
+
+ pic2_mask = inb(0xA1);
+ pic1_mask = inb(0x21); /* works on C3. save mask. */
+ outb(0xFF, 0xA1); /* Overkill */
+ outb(0xFE, 0x21); /* TMR0 only */
+
+ /* Wait while PCI bus is busy. */
+ if (acpi_regs_addr && (longhaul_flags & USE_NORTHBRIDGE
+ || ((pr != NULL) && pr->flags.bm_control))) {
+ bm_status = inw(acpi_regs_addr);
+ bm_status &= 1 << 4;
+ while (bm_status && bm_timeout) {
+ outw(1 << 4, acpi_regs_addr);
+ bm_timeout--;
+ bm_status = inw(acpi_regs_addr);
+ bm_status &= 1 << 4;
+ }
+ }
+
+ if (longhaul_flags & USE_NORTHBRIDGE) {
+ /* Disable AGP and PCI arbiters */
+ outb(3, 0x22);
+ } else if ((pr != NULL) && pr->flags.bm_control) {
+ /* Disable bus master arbitration */
+ acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 1);
+ }
+ switch (longhaul_version) {
+
+ /*
+ * Longhaul v1. (Samuel[C5A] and Samuel2 stepping 0[C5B])
+ * Software controlled multipliers only.
+ */
+ case TYPE_LONGHAUL_V1:
+ do_longhaul1(mults_index);
+ break;
+
+ /*
+ * Longhaul v2 appears in Samuel2 Steppings 1->7 [C5B] and Ezra [C5C]
+ *
+ * Longhaul v3 (aka Powersaver). (Ezra-T [C5M] & Nehemiah [C5N])
+ * Nehemiah can do FSB scaling too, but this has never been proven
+ * to work in practice.
+ */
+ case TYPE_LONGHAUL_V2:
+ case TYPE_POWERSAVER:
+ if (longhaul_flags & USE_ACPI_C3) {
+ /* Don't allow wakeup */
+ acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, 0);
+ do_powersaver(cx->address, mults_index, dir);
+ } else {
+ do_powersaver(0, mults_index, dir);
+ }
+ break;
+ }
+
+ if (longhaul_flags & USE_NORTHBRIDGE) {
+ /* Enable arbiters */
+ outb(0, 0x22);
+ } else if ((pr != NULL) && pr->flags.bm_control) {
+ /* Enable bus master arbitration */
+ acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 0);
+ }
+ outb(pic2_mask, 0xA1); /* restore mask */
+ outb(pic1_mask, 0x21);
+
+ local_irq_restore(flags);
+ preempt_enable();
+
+ freqs.new = calc_speed(longhaul_get_cpu_mult());
+ /* Check if requested frequency is set. */
+ if (unlikely(freqs.new != speed)) {
+ printk(KERN_INFO PFX "Failed to set requested frequency!\n");
+ /* Revision ID = 1 but processor is expecting revision key
+ * equal to 0. Jumpers at the bottom of processor will change
+ * multiplier and FSB, but will not change bits in Longhaul
+ * MSR nor enable voltage scaling. */
+ if (!revid_errata) {
+ printk(KERN_INFO PFX "Enabling \"Ignore Revision ID\" "
+ "option.\n");
+ revid_errata = 1;
+ msleep(200);
+ goto retry_loop;
+ }
+ /* Why ACPI C3 sometimes doesn't work is a mystery for me.
+ * But it does happen. Processor is entering ACPI C3 state,
+ * but it doesn't change frequency. I tried poking various
+ * bits in northbridge registers, but without success. */
+ if (longhaul_flags & USE_ACPI_C3) {
+ printk(KERN_INFO PFX "Disabling ACPI C3 support.\n");
+ longhaul_flags &= ~USE_ACPI_C3;
+ if (revid_errata) {
+ printk(KERN_INFO PFX "Disabling \"Ignore "
+ "Revision ID\" option.\n");
+ revid_errata = 0;
+ }
+ msleep(200);
+ goto retry_loop;
+ }
+ /* This shouldn't happen. Longhaul ver. 2 was reported not
+ * working on processors without voltage scaling, but with
+ * RevID = 1. RevID errata will make things right. Just
+ * to be 100% sure. */
+ if (longhaul_version == TYPE_LONGHAUL_V2) {
+ printk(KERN_INFO PFX "Switching to Longhaul ver. 1\n");
+ longhaul_version = TYPE_LONGHAUL_V1;
+ msleep(200);
+ goto retry_loop;
+ }
+ }
+ /* Report true CPU frequency */
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+ if (!bm_timeout)
+ printk(KERN_INFO PFX "Warning: Timeout while waiting for "
+ "idle PCI bus.\n");
+}
+
+/*
+ * Centaur decided to make life a little more tricky.
+ * Only longhaul v1 is allowed to read EBLCR BSEL[0:1].
+ * Samuel2 and above have to try and guess what the FSB is.
+ * We do this by assuming we booted at maximum multiplier, and interpolate
+ * between that value multiplied by possible FSBs and cpu_mhz which
+ * was calculated at boot time. Really ugly, but no other way to do this.
+ */
+
+#define ROUNDING 0xf
+
+static int guess_fsb(int mult)
+{
+ int speed = cpu_khz / 1000;
+ int i;
+ int speeds[] = { 666, 1000, 1333, 2000 };
+ int f_max, f_min;
+
+ for (i = 0; i < 4; i++) {
+ f_max = ((speeds[i] * mult) + 50) / 100;
+ f_max += (ROUNDING / 2);
+ f_min = f_max - ROUNDING;
+ if ((speed <= f_max) && (speed >= f_min))
+ return speeds[i] / 10;
+ }
+ return 0;
+}
+
+
+static int __cpuinit longhaul_get_ranges(void)
+{
+ unsigned int i, j, k = 0;
+ unsigned int ratio;
+ int mult;
+
+ /* Get current frequency */
+ mult = longhaul_get_cpu_mult();
+ if (mult == -1) {
+ printk(KERN_INFO PFX "Invalid (reserved) multiplier!\n");
+ return -EINVAL;
+ }
+ fsb = guess_fsb(mult);
+ if (fsb == 0) {
+ printk(KERN_INFO PFX "Invalid (reserved) FSB!\n");
+ return -EINVAL;
+ }
+ /* Get max multiplier - as we always did.
+ * Longhaul MSR is useful only when voltage scaling is enabled.
+ * C3 is booting at max anyway. */
+ maxmult = mult;
+ /* Get min multiplier */
+ switch (cpu_model) {
+ case CPU_NEHEMIAH:
+ minmult = 50;
+ break;
+ case CPU_NEHEMIAH_C:
+ minmult = 40;
+ break;
+ default:
+ minmult = 30;
+ break;
+ }
+
+ pr_debug("MinMult:%d.%dx MaxMult:%d.%dx\n",
+ minmult/10, minmult%10, maxmult/10, maxmult%10);
+
+ highest_speed = calc_speed(maxmult);
+ lowest_speed = calc_speed(minmult);
+ pr_debug("FSB:%dMHz Lowest speed: %s Highest speed:%s\n", fsb,
+ print_speed(lowest_speed/1000),
+ print_speed(highest_speed/1000));
+
+ if (lowest_speed == highest_speed) {
+ printk(KERN_INFO PFX "highestspeed == lowest, aborting.\n");
+ return -EINVAL;
+ }
+ if (lowest_speed > highest_speed) {
+ printk(KERN_INFO PFX "nonsense! lowest (%d > %d) !\n",
+ lowest_speed, highest_speed);
+ return -EINVAL;
+ }
+
+ longhaul_table = kmalloc((numscales + 1) * sizeof(*longhaul_table),
+ GFP_KERNEL);
+ if (!longhaul_table)
+ return -ENOMEM;
+
+ for (j = 0; j < numscales; j++) {
+ ratio = mults[j];
+ if (ratio == -1)
+ continue;
+ if (ratio > maxmult || ratio < minmult)
+ continue;
+ longhaul_table[k].frequency = calc_speed(ratio);
+ longhaul_table[k].index = j;
+ k++;
+ }
+ if (k <= 1) {
+ kfree(longhaul_table);
+ return -ENODEV;
+ }
+ /* Sort */
+ for (j = 0; j < k - 1; j++) {
+ unsigned int min_f, min_i;
+ min_f = longhaul_table[j].frequency;
+ min_i = j;
+ for (i = j + 1; i < k; i++) {
+ if (longhaul_table[i].frequency < min_f) {
+ min_f = longhaul_table[i].frequency;
+ min_i = i;
+ }
+ }
+ if (min_i != j) {
+ swap(longhaul_table[j].frequency,
+ longhaul_table[min_i].frequency);
+ swap(longhaul_table[j].index,
+ longhaul_table[min_i].index);
+ }
+ }
+
+ longhaul_table[k].frequency = CPUFREQ_TABLE_END;
+
+ /* Find index we are running on */
+ for (j = 0; j < k; j++) {
+ if (mults[longhaul_table[j].index & 0x1f] == mult) {
+ longhaul_index = j;
+ break;
+ }
+ }
+ return 0;
+}
+
+
+static void __cpuinit longhaul_setup_voltagescaling(void)
+{
+ union msr_longhaul longhaul;
+ struct mV_pos minvid, maxvid, vid;
+ unsigned int j, speed, pos, kHz_step, numvscales;
+ int min_vid_speed;
+
+ rdmsrl(MSR_VIA_LONGHAUL, longhaul.val);
+ if (!(longhaul.bits.RevisionID & 1)) {
+ printk(KERN_INFO PFX "Voltage scaling not supported by CPU.\n");
+ return;
+ }
+
+ if (!longhaul.bits.VRMRev) {
+ printk(KERN_INFO PFX "VRM 8.5\n");
+ vrm_mV_table = &vrm85_mV[0];
+ mV_vrm_table = &mV_vrm85[0];
+ } else {
+ printk(KERN_INFO PFX "Mobile VRM\n");
+ if (cpu_model < CPU_NEHEMIAH)
+ return;
+ vrm_mV_table = &mobilevrm_mV[0];
+ mV_vrm_table = &mV_mobilevrm[0];
+ }
+
+ minvid = vrm_mV_table[longhaul.bits.MinimumVID];
+ maxvid = vrm_mV_table[longhaul.bits.MaximumVID];
+
+ if (minvid.mV == 0 || maxvid.mV == 0 || minvid.mV > maxvid.mV) {
+ printk(KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d. "
+ "Voltage scaling disabled.\n",
+ minvid.mV/1000, minvid.mV%1000,
+ maxvid.mV/1000, maxvid.mV%1000);
+ return;
+ }
+
+ if (minvid.mV == maxvid.mV) {
+ printk(KERN_INFO PFX "Claims to support voltage scaling but "
+ "min & max are both %d.%03d. "
+ "Voltage scaling disabled\n",
+ maxvid.mV/1000, maxvid.mV%1000);
+ return;
+ }
+
+ /* How many voltage steps*/
+ numvscales = maxvid.pos - minvid.pos + 1;
+ printk(KERN_INFO PFX
+ "Max VID=%d.%03d "
+ "Min VID=%d.%03d, "
+ "%d possible voltage scales\n",
+ maxvid.mV/1000, maxvid.mV%1000,
+ minvid.mV/1000, minvid.mV%1000,
+ numvscales);
+
+ /* Calculate max frequency at min voltage */
+ j = longhaul.bits.MinMHzBR;
+ if (longhaul.bits.MinMHzBR4)
+ j += 16;
+ min_vid_speed = eblcr[j];
+ if (min_vid_speed == -1)
+ return;
+ switch (longhaul.bits.MinMHzFSB) {
+ case 0:
+ min_vid_speed *= 13333;
+ break;
+ case 1:
+ min_vid_speed *= 10000;
+ break;
+ case 3:
+ min_vid_speed *= 6666;
+ break;
+ default:
+ return;
+ break;
+ }
+ if (min_vid_speed >= highest_speed)
+ return;
+ /* Calculate kHz for one voltage step */
+ kHz_step = (highest_speed - min_vid_speed) / numvscales;
+
+ j = 0;
+ while (longhaul_table[j].frequency != CPUFREQ_TABLE_END) {
+ speed = longhaul_table[j].frequency;
+ if (speed > min_vid_speed)
+ pos = (speed - min_vid_speed) / kHz_step + minvid.pos;
+ else
+ pos = minvid.pos;
+ longhaul_table[j].index |= mV_vrm_table[pos] << 8;
+ vid = vrm_mV_table[mV_vrm_table[pos]];
+ printk(KERN_INFO PFX "f: %d kHz, index: %d, vid: %d mV\n",
+ speed, j, vid.mV);
+ j++;
+ }
+
+ can_scale_voltage = 1;
+ printk(KERN_INFO PFX "Voltage scaling enabled.\n");
+}
+
+
+static int longhaul_verify(struct cpufreq_policy *policy)
+{
+ return cpufreq_frequency_table_verify(policy, longhaul_table);
+}
+
+
+static int longhaul_target(struct cpufreq_policy *policy,
+ unsigned int target_freq, unsigned int relation)
+{
+ unsigned int table_index = 0;
+ unsigned int i;
+ unsigned int dir = 0;
+ u8 vid, current_vid;
+
+ if (cpufreq_frequency_table_target(policy, longhaul_table, target_freq,
+ relation, &table_index))
+ return -EINVAL;
+
+ /* Don't set same frequency again */
+ if (longhaul_index == table_index)
+ return 0;
+
+ if (!can_scale_voltage)
+ longhaul_setstate(table_index);
+ else {
+ /* On test system voltage transitions exceeding single
+ * step up or down were turning motherboard off. Both
+ * "ondemand" and "userspace" are unsafe. C7 is doing
+ * this in hardware, C3 is old and we need to do this
+ * in software. */
+ i = longhaul_index;
+ current_vid = (longhaul_table[longhaul_index].index >> 8);
+ current_vid &= 0x1f;
+ if (table_index > longhaul_index)
+ dir = 1;
+ while (i != table_index) {
+ vid = (longhaul_table[i].index >> 8) & 0x1f;
+ if (vid != current_vid) {
+ longhaul_setstate(i);
+ current_vid = vid;
+ msleep(200);
+ }
+ if (dir)
+ i++;
+ else
+ i--;
+ }
+ longhaul_setstate(table_index);
+ }
+ longhaul_index = table_index;
+ return 0;
+}
+
+
+static unsigned int longhaul_get(unsigned int cpu)
+{
+ if (cpu)
+ return 0;
+ return calc_speed(longhaul_get_cpu_mult());
+}
+
+static acpi_status longhaul_walk_callback(acpi_handle obj_handle,
+ u32 nesting_level,
+ void *context, void **return_value)
+{
+ struct acpi_device *d;
+
+ if (acpi_bus_get_device(obj_handle, &d))
+ return 0;
+
+ *return_value = acpi_driver_data(d);
+ return 1;
+}
+
+/* VIA don't support PM2 reg, but have something similar */
+static int enable_arbiter_disable(void)
+{
+ struct pci_dev *dev;
+ int status = 1;
+ int reg;
+ u8 pci_cmd;
+
+ /* Find PLE133 host bridge */
+ reg = 0x78;
+ dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8601_0,
+ NULL);
+ /* Find PM133/VT8605 host bridge */
+ if (dev == NULL)
+ dev = pci_get_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_8605_0, NULL);
+ /* Find CLE266 host bridge */
+ if (dev == NULL) {
+ reg = 0x76;
+ dev = pci_get_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_862X_0, NULL);
+ /* Find CN400 V-Link host bridge */
+ if (dev == NULL)
+ dev = pci_get_device(PCI_VENDOR_ID_VIA, 0x7259, NULL);
+ }
+ if (dev != NULL) {
+ /* Enable access to port 0x22 */
+ pci_read_config_byte(dev, reg, &pci_cmd);
+ if (!(pci_cmd & 1<<7)) {
+ pci_cmd |= 1<<7;
+ pci_write_config_byte(dev, reg, pci_cmd);
+ pci_read_config_byte(dev, reg, &pci_cmd);
+ if (!(pci_cmd & 1<<7)) {
+ printk(KERN_ERR PFX
+ "Can't enable access to port 0x22.\n");
+ status = 0;
+ }
+ }
+ pci_dev_put(dev);
+ return status;
+ }
+ return 0;
+}
+
+static int longhaul_setup_southbridge(void)
+{
+ struct pci_dev *dev;
+ u8 pci_cmd;
+
+ /* Find VT8235 southbridge */
+ dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235, NULL);
+ if (dev == NULL)
+ /* Find VT8237 southbridge */
+ dev = pci_get_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_8237, NULL);
+ if (dev != NULL) {
+ /* Set transition time to max */
+ pci_read_config_byte(dev, 0xec, &pci_cmd);
+ pci_cmd &= ~(1 << 2);
+ pci_write_config_byte(dev, 0xec, pci_cmd);
+ pci_read_config_byte(dev, 0xe4, &pci_cmd);
+ pci_cmd &= ~(1 << 7);
+ pci_write_config_byte(dev, 0xe4, pci_cmd);
+ pci_read_config_byte(dev, 0xe5, &pci_cmd);
+ pci_cmd |= 1 << 7;
+ pci_write_config_byte(dev, 0xe5, pci_cmd);
+ /* Get address of ACPI registers block*/
+ pci_read_config_byte(dev, 0x81, &pci_cmd);
+ if (pci_cmd & 1 << 7) {
+ pci_read_config_dword(dev, 0x88, &acpi_regs_addr);
+ acpi_regs_addr &= 0xff00;
+ printk(KERN_INFO PFX "ACPI I/O at 0x%x\n",
+ acpi_regs_addr);
+ }
+
+ pci_dev_put(dev);
+ return 1;
+ }
+ return 0;
+}
+
+static int __cpuinit longhaul_cpu_init(struct cpufreq_policy *policy)
+{
+ struct cpuinfo_x86 *c = &cpu_data(0);
+ char *cpuname = NULL;
+ int ret;
+ u32 lo, hi;
+
+ /* Check what we have on this motherboard */
+ switch (c->x86_model) {
+ case 6:
+ cpu_model = CPU_SAMUEL;
+ cpuname = "C3 'Samuel' [C5A]";
+ longhaul_version = TYPE_LONGHAUL_V1;
+ memcpy(mults, samuel1_mults, sizeof(samuel1_mults));
+ memcpy(eblcr, samuel1_eblcr, sizeof(samuel1_eblcr));
+ break;
+
+ case 7:
+ switch (c->x86_mask) {
+ case 0:
+ longhaul_version = TYPE_LONGHAUL_V1;
+ cpu_model = CPU_SAMUEL2;
+ cpuname = "C3 'Samuel 2' [C5B]";
+ /* Note, this is not a typo, early Samuel2's had
+ * Samuel1 ratios. */
+ memcpy(mults, samuel1_mults, sizeof(samuel1_mults));
+ memcpy(eblcr, samuel2_eblcr, sizeof(samuel2_eblcr));
+ break;
+ case 1 ... 15:
+ longhaul_version = TYPE_LONGHAUL_V2;
+ if (c->x86_mask < 8) {
+ cpu_model = CPU_SAMUEL2;
+ cpuname = "C3 'Samuel 2' [C5B]";
+ } else {
+ cpu_model = CPU_EZRA;
+ cpuname = "C3 'Ezra' [C5C]";
+ }
+ memcpy(mults, ezra_mults, sizeof(ezra_mults));
+ memcpy(eblcr, ezra_eblcr, sizeof(ezra_eblcr));
+ break;
+ }
+ break;
+
+ case 8:
+ cpu_model = CPU_EZRA_T;
+ cpuname = "C3 'Ezra-T' [C5M]";
+ longhaul_version = TYPE_POWERSAVER;
+ numscales = 32;
+ memcpy(mults, ezrat_mults, sizeof(ezrat_mults));
+ memcpy(eblcr, ezrat_eblcr, sizeof(ezrat_eblcr));
+ break;
+
+ case 9:
+ longhaul_version = TYPE_POWERSAVER;
+ numscales = 32;
+ memcpy(mults, nehemiah_mults, sizeof(nehemiah_mults));
+ memcpy(eblcr, nehemiah_eblcr, sizeof(nehemiah_eblcr));
+ switch (c->x86_mask) {
+ case 0 ... 1:
+ cpu_model = CPU_NEHEMIAH;
+ cpuname = "C3 'Nehemiah A' [C5XLOE]";
+ break;
+ case 2 ... 4:
+ cpu_model = CPU_NEHEMIAH;
+ cpuname = "C3 'Nehemiah B' [C5XLOH]";
+ break;
+ case 5 ... 15:
+ cpu_model = CPU_NEHEMIAH_C;
+ cpuname = "C3 'Nehemiah C' [C5P]";
+ break;
+ }
+ break;
+
+ default:
+ cpuname = "Unknown";
+ break;
+ }
+ /* Check Longhaul ver. 2 */
+ if (longhaul_version == TYPE_LONGHAUL_V2) {
+ rdmsr(MSR_VIA_LONGHAUL, lo, hi);
+ if (lo == 0 && hi == 0)
+ /* Looks like MSR isn't present */
+ longhaul_version = TYPE_LONGHAUL_V1;
+ }
+
+ printk(KERN_INFO PFX "VIA %s CPU detected. ", cpuname);
+ switch (longhaul_version) {
+ case TYPE_LONGHAUL_V1:
+ case TYPE_LONGHAUL_V2:
+ printk(KERN_CONT "Longhaul v%d supported.\n", longhaul_version);
+ break;
+ case TYPE_POWERSAVER:
+ printk(KERN_CONT "Powersaver supported.\n");
+ break;
+ };
+
+ /* Doesn't hurt */
+ longhaul_setup_southbridge();
+
+ /* Find ACPI data for processor */
+ acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, &longhaul_walk_callback, NULL,
+ NULL, (void *)&pr);
+
+ /* Check ACPI support for C3 state */
+ if (pr != NULL && longhaul_version == TYPE_POWERSAVER) {
+ cx = &pr->power.states[ACPI_STATE_C3];
+ if (cx->address > 0 && cx->latency <= 1000)
+ longhaul_flags |= USE_ACPI_C3;
+ }
+ /* Disable if it isn't working */
+ if (disable_acpi_c3)
+ longhaul_flags &= ~USE_ACPI_C3;
+ /* Check if northbridge is friendly */
+ if (enable_arbiter_disable())
+ longhaul_flags |= USE_NORTHBRIDGE;
+
+ /* Check ACPI support for bus master arbiter disable */
+ if (!(longhaul_flags & USE_ACPI_C3
+ || longhaul_flags & USE_NORTHBRIDGE)
+ && ((pr == NULL) || !(pr->flags.bm_control))) {
+ printk(KERN_ERR PFX
+ "No ACPI support. Unsupported northbridge.\n");
+ return -ENODEV;
+ }
+
+ if (longhaul_flags & USE_NORTHBRIDGE)
+ printk(KERN_INFO PFX "Using northbridge support.\n");
+ if (longhaul_flags & USE_ACPI_C3)
+ printk(KERN_INFO PFX "Using ACPI support.\n");
+
+ ret = longhaul_get_ranges();
+ if (ret != 0)
+ return ret;
+
+ if ((longhaul_version != TYPE_LONGHAUL_V1) && (scale_voltage != 0))
+ longhaul_setup_voltagescaling();
+
+ policy->cpuinfo.transition_latency = 200000; /* nsec */
+ policy->cur = calc_speed(longhaul_get_cpu_mult());
+
+ ret = cpufreq_frequency_table_cpuinfo(policy, longhaul_table);
+ if (ret)
+ return ret;
+
+ cpufreq_frequency_table_get_attr(longhaul_table, policy->cpu);
+
+ return 0;
+}
+
+static int __devexit longhaul_cpu_exit(struct cpufreq_policy *policy)
+{
+ cpufreq_frequency_table_put_attr(policy->cpu);
+ return 0;
+}
+
+static struct freq_attr *longhaul_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+static struct cpufreq_driver longhaul_driver = {
+ .verify = longhaul_verify,
+ .target = longhaul_target,
+ .get = longhaul_get,
+ .init = longhaul_cpu_init,
+ .exit = __devexit_p(longhaul_cpu_exit),
+ .name = "longhaul",
+ .owner = THIS_MODULE,
+ .attr = longhaul_attr,
+};
+
+
+static int __init longhaul_init(void)
+{
+ struct cpuinfo_x86 *c = &cpu_data(0);
+
+ if (c->x86_vendor != X86_VENDOR_CENTAUR || c->x86 != 6)
+ return -ENODEV;
+
+#ifdef CONFIG_SMP
+ if (num_online_cpus() > 1) {
+ printk(KERN_ERR PFX "More than 1 CPU detected, "
+ "longhaul disabled.\n");
+ return -ENODEV;
+ }
+#endif
+#ifdef CONFIG_X86_IO_APIC
+ if (cpu_has_apic) {
+ printk(KERN_ERR PFX "APIC detected. Longhaul is currently "
+ "broken in this configuration.\n");
+ return -ENODEV;
+ }
+#endif
+ switch (c->x86_model) {
+ case 6 ... 9:
+ return cpufreq_register_driver(&longhaul_driver);
+ case 10:
+ printk(KERN_ERR PFX "Use acpi-cpufreq driver for VIA C7\n");
+ default:
+ ;
+ }
+
+ return -ENODEV;
+}
+
+
+static void __exit longhaul_exit(void)
+{
+ int i;
+
+ for (i = 0; i < numscales; i++) {
+ if (mults[i] == maxmult) {
+ longhaul_setstate(i);
+ break;
+ }
+ }
+
+ cpufreq_unregister_driver(&longhaul_driver);
+ kfree(longhaul_table);
+}
+
+/* Even if BIOS is exporting ACPI C3 state, and it is used
+ * with success when CPU is idle, this state doesn't
+ * trigger frequency transition in some cases. */
+module_param(disable_acpi_c3, int, 0644);
+MODULE_PARM_DESC(disable_acpi_c3, "Don't use ACPI C3 support");
+/* Change CPU voltage with frequency. Very useful to save
+ * power, but most VIA C3 processors aren't supporting it. */
+module_param(scale_voltage, int, 0644);
+MODULE_PARM_DESC(scale_voltage, "Scale voltage of processor");
+/* Force revision key to 0 for processors which doesn't
+ * support voltage scaling, but are introducing itself as
+ * such. */
+module_param(revid_errata, int, 0644);
+MODULE_PARM_DESC(revid_errata, "Ignore CPU Revision ID");
+
+MODULE_AUTHOR("Dave Jones <davej@redhat.com>");
+MODULE_DESCRIPTION("Longhaul driver for VIA Cyrix processors.");
+MODULE_LICENSE("GPL");
+
+late_initcall(longhaul_init);
+module_exit(longhaul_exit);
diff --git a/drivers/cpufreq/longhaul.h b/drivers/cpufreq/longhaul.h
new file mode 100644
index 00000000000..cbf48fbca88
--- /dev/null
+++ b/drivers/cpufreq/longhaul.h
@@ -0,0 +1,353 @@
+/*
+ * longhaul.h
+ * (C) 2003 Dave Jones.
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ *
+ * VIA-specific information
+ */
+
+union msr_bcr2 {
+ struct {
+ unsigned Reseved:19, // 18:0
+ ESOFTBF:1, // 19
+ Reserved2:3, // 22:20
+ CLOCKMUL:4, // 26:23
+ Reserved3:5; // 31:27
+ } bits;
+ unsigned long val;
+};
+
+union msr_longhaul {
+ struct {
+ unsigned RevisionID:4, // 3:0
+ RevisionKey:4, // 7:4
+ EnableSoftBusRatio:1, // 8
+ EnableSoftVID:1, // 9
+ EnableSoftBSEL:1, // 10
+ Reserved:3, // 11:13
+ SoftBusRatio4:1, // 14
+ VRMRev:1, // 15
+ SoftBusRatio:4, // 19:16
+ SoftVID:5, // 24:20
+ Reserved2:3, // 27:25
+ SoftBSEL:2, // 29:28
+ Reserved3:2, // 31:30
+ MaxMHzBR:4, // 35:32
+ MaximumVID:5, // 40:36
+ MaxMHzFSB:2, // 42:41
+ MaxMHzBR4:1, // 43
+ Reserved4:4, // 47:44
+ MinMHzBR:4, // 51:48
+ MinimumVID:5, // 56:52
+ MinMHzFSB:2, // 58:57
+ MinMHzBR4:1, // 59
+ Reserved5:4; // 63:60
+ } bits;
+ unsigned long long val;
+};
+
+/*
+ * Clock ratio tables. Div/Mod by 10 to get ratio.
+ * The eblcr values specify the ratio read from the CPU.
+ * The mults values specify what to write to the CPU.
+ */
+
+/*
+ * VIA C3 Samuel 1 & Samuel 2 (stepping 0)
+ */
+static const int __cpuinitdata samuel1_mults[16] = {
+ -1, /* 0000 -> RESERVED */
+ 30, /* 0001 -> 3.0x */
+ 40, /* 0010 -> 4.0x */
+ -1, /* 0011 -> RESERVED */
+ -1, /* 0100 -> RESERVED */
+ 35, /* 0101 -> 3.5x */
+ 45, /* 0110 -> 4.5x */
+ 55, /* 0111 -> 5.5x */
+ 60, /* 1000 -> 6.0x */
+ 70, /* 1001 -> 7.0x */
+ 80, /* 1010 -> 8.0x */
+ 50, /* 1011 -> 5.0x */
+ 65, /* 1100 -> 6.5x */
+ 75, /* 1101 -> 7.5x */
+ -1, /* 1110 -> RESERVED */
+ -1, /* 1111 -> RESERVED */
+};
+
+static const int __cpuinitdata samuel1_eblcr[16] = {
+ 50, /* 0000 -> RESERVED */
+ 30, /* 0001 -> 3.0x */
+ 40, /* 0010 -> 4.0x */
+ -1, /* 0011 -> RESERVED */
+ 55, /* 0100 -> 5.5x */
+ 35, /* 0101 -> 3.5x */
+ 45, /* 0110 -> 4.5x */
+ -1, /* 0111 -> RESERVED */
+ -1, /* 1000 -> RESERVED */
+ 70, /* 1001 -> 7.0x */
+ 80, /* 1010 -> 8.0x */
+ 60, /* 1011 -> 6.0x */
+ -1, /* 1100 -> RESERVED */
+ 75, /* 1101 -> 7.5x */
+ -1, /* 1110 -> RESERVED */
+ 65, /* 1111 -> 6.5x */
+};
+
+/*
+ * VIA C3 Samuel2 Stepping 1->15
+ */
+static const int __cpuinitdata samuel2_eblcr[16] = {
+ 50, /* 0000 -> 5.0x */
+ 30, /* 0001 -> 3.0x */
+ 40, /* 0010 -> 4.0x */
+ 100, /* 0011 -> 10.0x */
+ 55, /* 0100 -> 5.5x */
+ 35, /* 0101 -> 3.5x */
+ 45, /* 0110 -> 4.5x */
+ 110, /* 0111 -> 11.0x */
+ 90, /* 1000 -> 9.0x */
+ 70, /* 1001 -> 7.0x */
+ 80, /* 1010 -> 8.0x */
+ 60, /* 1011 -> 6.0x */
+ 120, /* 1100 -> 12.0x */
+ 75, /* 1101 -> 7.5x */
+ 130, /* 1110 -> 13.0x */
+ 65, /* 1111 -> 6.5x */
+};
+
+/*
+ * VIA C3 Ezra
+ */
+static const int __cpuinitdata ezra_mults[16] = {
+ 100, /* 0000 -> 10.0x */
+ 30, /* 0001 -> 3.0x */
+ 40, /* 0010 -> 4.0x */
+ 90, /* 0011 -> 9.0x */
+ 95, /* 0100 -> 9.5x */
+ 35, /* 0101 -> 3.5x */
+ 45, /* 0110 -> 4.5x */
+ 55, /* 0111 -> 5.5x */
+ 60, /* 1000 -> 6.0x */
+ 70, /* 1001 -> 7.0x */
+ 80, /* 1010 -> 8.0x */
+ 50, /* 1011 -> 5.0x */
+ 65, /* 1100 -> 6.5x */
+ 75, /* 1101 -> 7.5x */
+ 85, /* 1110 -> 8.5x */
+ 120, /* 1111 -> 12.0x */
+};
+
+static const int __cpuinitdata ezra_eblcr[16] = {
+ 50, /* 0000 -> 5.0x */
+ 30, /* 0001 -> 3.0x */
+ 40, /* 0010 -> 4.0x */
+ 100, /* 0011 -> 10.0x */
+ 55, /* 0100 -> 5.5x */
+ 35, /* 0101 -> 3.5x */
+ 45, /* 0110 -> 4.5x */
+ 95, /* 0111 -> 9.5x */
+ 90, /* 1000 -> 9.0x */
+ 70, /* 1001 -> 7.0x */
+ 80, /* 1010 -> 8.0x */
+ 60, /* 1011 -> 6.0x */
+ 120, /* 1100 -> 12.0x */
+ 75, /* 1101 -> 7.5x */
+ 85, /* 1110 -> 8.5x */
+ 65, /* 1111 -> 6.5x */
+};
+
+/*
+ * VIA C3 (Ezra-T) [C5M].
+ */
+static const int __cpuinitdata ezrat_mults[32] = {
+ 100, /* 0000 -> 10.0x */
+ 30, /* 0001 -> 3.0x */
+ 40, /* 0010 -> 4.0x */
+ 90, /* 0011 -> 9.0x */
+ 95, /* 0100 -> 9.5x */
+ 35, /* 0101 -> 3.5x */
+ 45, /* 0110 -> 4.5x */
+ 55, /* 0111 -> 5.5x */
+ 60, /* 1000 -> 6.0x */
+ 70, /* 1001 -> 7.0x */
+ 80, /* 1010 -> 8.0x */
+ 50, /* 1011 -> 5.0x */
+ 65, /* 1100 -> 6.5x */
+ 75, /* 1101 -> 7.5x */
+ 85, /* 1110 -> 8.5x */
+ 120, /* 1111 -> 12.0x */
+
+ -1, /* 0000 -> RESERVED (10.0x) */
+ 110, /* 0001 -> 11.0x */
+ -1, /* 0010 -> 12.0x */
+ -1, /* 0011 -> RESERVED (9.0x)*/
+ 105, /* 0100 -> 10.5x */
+ 115, /* 0101 -> 11.5x */
+ 125, /* 0110 -> 12.5x */
+ 135, /* 0111 -> 13.5x */
+ 140, /* 1000 -> 14.0x */
+ 150, /* 1001 -> 15.0x */
+ 160, /* 1010 -> 16.0x */
+ 130, /* 1011 -> 13.0x */
+ 145, /* 1100 -> 14.5x */
+ 155, /* 1101 -> 15.5x */
+ -1, /* 1110 -> RESERVED (13.0x) */
+ -1, /* 1111 -> RESERVED (12.0x) */
+};
+
+static const int __cpuinitdata ezrat_eblcr[32] = {
+ 50, /* 0000 -> 5.0x */
+ 30, /* 0001 -> 3.0x */
+ 40, /* 0010 -> 4.0x */
+ 100, /* 0011 -> 10.0x */
+ 55, /* 0100 -> 5.5x */
+ 35, /* 0101 -> 3.5x */
+ 45, /* 0110 -> 4.5x */
+ 95, /* 0111 -> 9.5x */
+ 90, /* 1000 -> 9.0x */
+ 70, /* 1001 -> 7.0x */
+ 80, /* 1010 -> 8.0x */
+ 60, /* 1011 -> 6.0x */
+ 120, /* 1100 -> 12.0x */
+ 75, /* 1101 -> 7.5x */
+ 85, /* 1110 -> 8.5x */
+ 65, /* 1111 -> 6.5x */
+
+ -1, /* 0000 -> RESERVED (9.0x) */
+ 110, /* 0001 -> 11.0x */
+ 120, /* 0010 -> 12.0x */
+ -1, /* 0011 -> RESERVED (10.0x)*/
+ 135, /* 0100 -> 13.5x */
+ 115, /* 0101 -> 11.5x */
+ 125, /* 0110 -> 12.5x */
+ 105, /* 0111 -> 10.5x */
+ 130, /* 1000 -> 13.0x */
+ 150, /* 1001 -> 15.0x */
+ 160, /* 1010 -> 16.0x */
+ 140, /* 1011 -> 14.0x */
+ -1, /* 1100 -> RESERVED (12.0x) */
+ 155, /* 1101 -> 15.5x */
+ -1, /* 1110 -> RESERVED (13.0x) */
+ 145, /* 1111 -> 14.5x */
+};
+
+/*
+ * VIA C3 Nehemiah */
+
+static const int __cpuinitdata nehemiah_mults[32] = {
+ 100, /* 0000 -> 10.0x */
+ -1, /* 0001 -> 16.0x */
+ 40, /* 0010 -> 4.0x */
+ 90, /* 0011 -> 9.0x */
+ 95, /* 0100 -> 9.5x */
+ -1, /* 0101 -> RESERVED */
+ 45, /* 0110 -> 4.5x */
+ 55, /* 0111 -> 5.5x */
+ 60, /* 1000 -> 6.0x */
+ 70, /* 1001 -> 7.0x */
+ 80, /* 1010 -> 8.0x */
+ 50, /* 1011 -> 5.0x */
+ 65, /* 1100 -> 6.5x */
+ 75, /* 1101 -> 7.5x */
+ 85, /* 1110 -> 8.5x */
+ 120, /* 1111 -> 12.0x */
+ -1, /* 0000 -> 10.0x */
+ 110, /* 0001 -> 11.0x */
+ -1, /* 0010 -> 12.0x */
+ -1, /* 0011 -> 9.0x */
+ 105, /* 0100 -> 10.5x */
+ 115, /* 0101 -> 11.5x */
+ 125, /* 0110 -> 12.5x */
+ 135, /* 0111 -> 13.5x */
+ 140, /* 1000 -> 14.0x */
+ 150, /* 1001 -> 15.0x */
+ 160, /* 1010 -> 16.0x */
+ 130, /* 1011 -> 13.0x */
+ 145, /* 1100 -> 14.5x */
+ 155, /* 1101 -> 15.5x */
+ -1, /* 1110 -> RESERVED (13.0x) */
+ -1, /* 1111 -> 12.0x */
+};
+
+static const int __cpuinitdata nehemiah_eblcr[32] = {
+ 50, /* 0000 -> 5.0x */
+ 160, /* 0001 -> 16.0x */
+ 40, /* 0010 -> 4.0x */
+ 100, /* 0011 -> 10.0x */
+ 55, /* 0100 -> 5.5x */
+ -1, /* 0101 -> RESERVED */
+ 45, /* 0110 -> 4.5x */
+ 95, /* 0111 -> 9.5x */
+ 90, /* 1000 -> 9.0x */
+ 70, /* 1001 -> 7.0x */
+ 80, /* 1010 -> 8.0x */
+ 60, /* 1011 -> 6.0x */
+ 120, /* 1100 -> 12.0x */
+ 75, /* 1101 -> 7.5x */
+ 85, /* 1110 -> 8.5x */
+ 65, /* 1111 -> 6.5x */
+ 90, /* 0000 -> 9.0x */
+ 110, /* 0001 -> 11.0x */
+ 120, /* 0010 -> 12.0x */
+ 100, /* 0011 -> 10.0x */
+ 135, /* 0100 -> 13.5x */
+ 115, /* 0101 -> 11.5x */
+ 125, /* 0110 -> 12.5x */
+ 105, /* 0111 -> 10.5x */
+ 130, /* 1000 -> 13.0x */
+ 150, /* 1001 -> 15.0x */
+ 160, /* 1010 -> 16.0x */
+ 140, /* 1011 -> 14.0x */
+ 120, /* 1100 -> 12.0x */
+ 155, /* 1101 -> 15.5x */
+ -1, /* 1110 -> RESERVED (13.0x) */
+ 145 /* 1111 -> 14.5x */
+};
+
+/*
+ * Voltage scales. Div/Mod by 1000 to get actual voltage.
+ * Which scale to use depends on the VRM type in use.
+ */
+
+struct mV_pos {
+ unsigned short mV;
+ unsigned short pos;
+};
+
+static const struct mV_pos __cpuinitdata vrm85_mV[32] = {
+ {1250, 8}, {1200, 6}, {1150, 4}, {1100, 2},
+ {1050, 0}, {1800, 30}, {1750, 28}, {1700, 26},
+ {1650, 24}, {1600, 22}, {1550, 20}, {1500, 18},
+ {1450, 16}, {1400, 14}, {1350, 12}, {1300, 10},
+ {1275, 9}, {1225, 7}, {1175, 5}, {1125, 3},
+ {1075, 1}, {1825, 31}, {1775, 29}, {1725, 27},
+ {1675, 25}, {1625, 23}, {1575, 21}, {1525, 19},
+ {1475, 17}, {1425, 15}, {1375, 13}, {1325, 11}
+};
+
+static const unsigned char __cpuinitdata mV_vrm85[32] = {
+ 0x04, 0x14, 0x03, 0x13, 0x02, 0x12, 0x01, 0x11,
+ 0x00, 0x10, 0x0f, 0x1f, 0x0e, 0x1e, 0x0d, 0x1d,
+ 0x0c, 0x1c, 0x0b, 0x1b, 0x0a, 0x1a, 0x09, 0x19,
+ 0x08, 0x18, 0x07, 0x17, 0x06, 0x16, 0x05, 0x15
+};
+
+static const struct mV_pos __cpuinitdata mobilevrm_mV[32] = {
+ {1750, 31}, {1700, 30}, {1650, 29}, {1600, 28},
+ {1550, 27}, {1500, 26}, {1450, 25}, {1400, 24},
+ {1350, 23}, {1300, 22}, {1250, 21}, {1200, 20},
+ {1150, 19}, {1100, 18}, {1050, 17}, {1000, 16},
+ {975, 15}, {950, 14}, {925, 13}, {900, 12},
+ {875, 11}, {850, 10}, {825, 9}, {800, 8},
+ {775, 7}, {750, 6}, {725, 5}, {700, 4},
+ {675, 3}, {650, 2}, {625, 1}, {600, 0}
+};
+
+static const unsigned char __cpuinitdata mV_mobilevrm[32] = {
+ 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
+ 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
+ 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+ 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
+};
+
diff --git a/drivers/cpufreq/longrun.c b/drivers/cpufreq/longrun.c
new file mode 100644
index 00000000000..34ea359b370
--- /dev/null
+++ b/drivers/cpufreq/longrun.c
@@ -0,0 +1,324 @@
+/*
+ * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de>
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ *
+ * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/timex.h>
+
+#include <asm/msr.h>
+#include <asm/processor.h>
+
+static struct cpufreq_driver longrun_driver;
+
+/**
+ * longrun_{low,high}_freq is needed for the conversion of cpufreq kHz
+ * values into per cent values. In TMTA microcode, the following is valid:
+ * performance_pctg = (current_freq - low_freq)/(high_freq - low_freq)
+ */
+static unsigned int longrun_low_freq, longrun_high_freq;
+
+
+/**
+ * longrun_get_policy - get the current LongRun policy
+ * @policy: struct cpufreq_policy where current policy is written into
+ *
+ * Reads the current LongRun policy by access to MSR_TMTA_LONGRUN_FLAGS
+ * and MSR_TMTA_LONGRUN_CTRL
+ */
+static void __cpuinit longrun_get_policy(struct cpufreq_policy *policy)
+{
+ u32 msr_lo, msr_hi;
+
+ rdmsr(MSR_TMTA_LONGRUN_FLAGS, msr_lo, msr_hi);
+ pr_debug("longrun flags are %x - %x\n", msr_lo, msr_hi);
+ if (msr_lo & 0x01)
+ policy->policy = CPUFREQ_POLICY_PERFORMANCE;
+ else
+ policy->policy = CPUFREQ_POLICY_POWERSAVE;
+
+ rdmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi);
+ pr_debug("longrun ctrl is %x - %x\n", msr_lo, msr_hi);
+ msr_lo &= 0x0000007F;
+ msr_hi &= 0x0000007F;
+
+ if (longrun_high_freq <= longrun_low_freq) {
+ /* Assume degenerate Longrun table */
+ policy->min = policy->max = longrun_high_freq;
+ } else {
+ policy->min = longrun_low_freq + msr_lo *
+ ((longrun_high_freq - longrun_low_freq) / 100);
+ policy->max = longrun_low_freq + msr_hi *
+ ((longrun_high_freq - longrun_low_freq) / 100);
+ }
+ policy->cpu = 0;
+}
+
+
+/**
+ * longrun_set_policy - sets a new CPUFreq policy
+ * @policy: new policy
+ *
+ * Sets a new CPUFreq policy on LongRun-capable processors. This function
+ * has to be called with cpufreq_driver locked.
+ */
+static int longrun_set_policy(struct cpufreq_policy *policy)
+{
+ u32 msr_lo, msr_hi;
+ u32 pctg_lo, pctg_hi;
+
+ if (!policy)
+ return -EINVAL;
+
+ if (longrun_high_freq <= longrun_low_freq) {
+ /* Assume degenerate Longrun table */
+ pctg_lo = pctg_hi = 100;
+ } else {
+ pctg_lo = (policy->min - longrun_low_freq) /
+ ((longrun_high_freq - longrun_low_freq) / 100);
+ pctg_hi = (policy->max - longrun_low_freq) /
+ ((longrun_high_freq - longrun_low_freq) / 100);
+ }
+
+ if (pctg_hi > 100)
+ pctg_hi = 100;
+ if (pctg_lo > pctg_hi)
+ pctg_lo = pctg_hi;
+
+ /* performance or economy mode */
+ rdmsr(MSR_TMTA_LONGRUN_FLAGS, msr_lo, msr_hi);
+ msr_lo &= 0xFFFFFFFE;
+ switch (policy->policy) {
+ case CPUFREQ_POLICY_PERFORMANCE:
+ msr_lo |= 0x00000001;
+ break;
+ case CPUFREQ_POLICY_POWERSAVE:
+ break;
+ }
+ wrmsr(MSR_TMTA_LONGRUN_FLAGS, msr_lo, msr_hi);
+
+ /* lower and upper boundary */
+ rdmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi);
+ msr_lo &= 0xFFFFFF80;
+ msr_hi &= 0xFFFFFF80;
+ msr_lo |= pctg_lo;
+ msr_hi |= pctg_hi;
+ wrmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi);
+
+ return 0;
+}
+
+
+/**
+ * longrun_verify_poliy - verifies a new CPUFreq policy
+ * @policy: the policy to verify
+ *
+ * Validates a new CPUFreq policy. This function has to be called with
+ * cpufreq_driver locked.
+ */
+static int longrun_verify_policy(struct cpufreq_policy *policy)
+{
+ if (!policy)
+ return -EINVAL;
+
+ policy->cpu = 0;
+ cpufreq_verify_within_limits(policy,
+ policy->cpuinfo.min_freq,
+ policy->cpuinfo.max_freq);
+
+ if ((policy->policy != CPUFREQ_POLICY_POWERSAVE) &&
+ (policy->policy != CPUFREQ_POLICY_PERFORMANCE))
+ return -EINVAL;
+
+ return 0;
+}
+
+static unsigned int longrun_get(unsigned int cpu)
+{
+ u32 eax, ebx, ecx, edx;
+
+ if (cpu)
+ return 0;
+
+ cpuid(0x80860007, &eax, &ebx, &ecx, &edx);
+ pr_debug("cpuid eax is %u\n", eax);
+
+ return eax * 1000;
+}
+
+/**
+ * longrun_determine_freqs - determines the lowest and highest possible core frequency
+ * @low_freq: an int to put the lowest frequency into
+ * @high_freq: an int to put the highest frequency into
+ *
+ * Determines the lowest and highest possible core frequencies on this CPU.
+ * This is necessary to calculate the performance percentage according to
+ * TMTA rules:
+ * performance_pctg = (target_freq - low_freq)/(high_freq - low_freq)
+ */
+static int __cpuinit longrun_determine_freqs(unsigned int *low_freq,
+ unsigned int *high_freq)
+{
+ u32 msr_lo, msr_hi;
+ u32 save_lo, save_hi;
+ u32 eax, ebx, ecx, edx;
+ u32 try_hi;
+ struct cpuinfo_x86 *c = &cpu_data(0);
+
+ if (!low_freq || !high_freq)
+ return -EINVAL;
+
+ if (cpu_has(c, X86_FEATURE_LRTI)) {
+ /* if the LongRun Table Interface is present, the
+ * detection is a bit easier:
+ * For minimum frequency, read out the maximum
+ * level (msr_hi), write that into "currently
+ * selected level", and read out the frequency.
+ * For maximum frequency, read out level zero.
+ */
+ /* minimum */
+ rdmsr(MSR_TMTA_LRTI_READOUT, msr_lo, msr_hi);
+ wrmsr(MSR_TMTA_LRTI_READOUT, msr_hi, msr_hi);
+ rdmsr(MSR_TMTA_LRTI_VOLT_MHZ, msr_lo, msr_hi);
+ *low_freq = msr_lo * 1000; /* to kHz */
+
+ /* maximum */
+ wrmsr(MSR_TMTA_LRTI_READOUT, 0, msr_hi);
+ rdmsr(MSR_TMTA_LRTI_VOLT_MHZ, msr_lo, msr_hi);
+ *high_freq = msr_lo * 1000; /* to kHz */
+
+ pr_debug("longrun table interface told %u - %u kHz\n",
+ *low_freq, *high_freq);
+
+ if (*low_freq > *high_freq)
+ *low_freq = *high_freq;
+ return 0;
+ }
+
+ /* set the upper border to the value determined during TSC init */
+ *high_freq = (cpu_khz / 1000);
+ *high_freq = *high_freq * 1000;
+ pr_debug("high frequency is %u kHz\n", *high_freq);
+
+ /* get current borders */
+ rdmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi);
+ save_lo = msr_lo & 0x0000007F;
+ save_hi = msr_hi & 0x0000007F;
+
+ /* if current perf_pctg is larger than 90%, we need to decrease the
+ * upper limit to make the calculation more accurate.
+ */
+ cpuid(0x80860007, &eax, &ebx, &ecx, &edx);
+ /* try decreasing in 10% steps, some processors react only
+ * on some barrier values */
+ for (try_hi = 80; try_hi > 0 && ecx > 90; try_hi -= 10) {
+ /* set to 0 to try_hi perf_pctg */
+ msr_lo &= 0xFFFFFF80;
+ msr_hi &= 0xFFFFFF80;
+ msr_hi |= try_hi;
+ wrmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi);
+
+ /* read out current core MHz and current perf_pctg */
+ cpuid(0x80860007, &eax, &ebx, &ecx, &edx);
+
+ /* restore values */
+ wrmsr(MSR_TMTA_LONGRUN_CTRL, save_lo, save_hi);
+ }
+ pr_debug("percentage is %u %%, freq is %u MHz\n", ecx, eax);
+
+ /* performance_pctg = (current_freq - low_freq)/(high_freq - low_freq)
+ * eqals
+ * low_freq * (1 - perf_pctg) = (cur_freq - high_freq * perf_pctg)
+ *
+ * high_freq * perf_pctg is stored tempoarily into "ebx".
+ */
+ ebx = (((cpu_khz / 1000) * ecx) / 100); /* to MHz */
+
+ if ((ecx > 95) || (ecx == 0) || (eax < ebx))
+ return -EIO;
+
+ edx = ((eax - ebx) * 100) / (100 - ecx);
+ *low_freq = edx * 1000; /* back to kHz */
+
+ pr_debug("low frequency is %u kHz\n", *low_freq);
+
+ if (*low_freq > *high_freq)
+ *low_freq = *high_freq;
+
+ return 0;
+}
+
+
+static int __cpuinit longrun_cpu_init(struct cpufreq_policy *policy)
+{
+ int result = 0;
+
+ /* capability check */
+ if (policy->cpu != 0)
+ return -ENODEV;
+
+ /* detect low and high frequency */
+ result = longrun_determine_freqs(&longrun_low_freq, &longrun_high_freq);
+ if (result)
+ return result;
+
+ /* cpuinfo and default policy values */
+ policy->cpuinfo.min_freq = longrun_low_freq;
+ policy->cpuinfo.max_freq = longrun_high_freq;
+ policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+ longrun_get_policy(policy);
+
+ return 0;
+}
+
+
+static struct cpufreq_driver longrun_driver = {
+ .flags = CPUFREQ_CONST_LOOPS,
+ .verify = longrun_verify_policy,
+ .setpolicy = longrun_set_policy,
+ .get = longrun_get,
+ .init = longrun_cpu_init,
+ .name = "longrun",
+ .owner = THIS_MODULE,
+};
+
+
+/**
+ * longrun_init - initializes the Transmeta Crusoe LongRun CPUFreq driver
+ *
+ * Initializes the LongRun support.
+ */
+static int __init longrun_init(void)
+{
+ struct cpuinfo_x86 *c = &cpu_data(0);
+
+ if (c->x86_vendor != X86_VENDOR_TRANSMETA ||
+ !cpu_has(c, X86_FEATURE_LONGRUN))
+ return -ENODEV;
+
+ return cpufreq_register_driver(&longrun_driver);
+}
+
+
+/**
+ * longrun_exit - unregisters LongRun support
+ */
+static void __exit longrun_exit(void)
+{
+ cpufreq_unregister_driver(&longrun_driver);
+}
+
+
+MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
+MODULE_DESCRIPTION("LongRun driver for Transmeta Crusoe and "
+ "Efficeon processors.");
+MODULE_LICENSE("GPL");
+
+module_init(longrun_init);
+module_exit(longrun_exit);
diff --git a/drivers/cpufreq/mperf.c b/drivers/cpufreq/mperf.c
new file mode 100644
index 00000000000..911e193018a
--- /dev/null
+++ b/drivers/cpufreq/mperf.c
@@ -0,0 +1,51 @@
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/slab.h>
+
+#include "mperf.h"
+
+static DEFINE_PER_CPU(struct aperfmperf, acfreq_old_perf);
+
+/* Called via smp_call_function_single(), on the target CPU */
+static void read_measured_perf_ctrs(void *_cur)
+{
+ struct aperfmperf *am = _cur;
+
+ get_aperfmperf(am);
+}
+
+/*
+ * Return the measured active (C0) frequency on this CPU since last call
+ * to this function.
+ * Input: cpu number
+ * Return: Average CPU frequency in terms of max frequency (zero on error)
+ *
+ * We use IA32_MPERF and IA32_APERF MSRs to get the measured performance
+ * over a period of time, while CPU is in C0 state.
+ * IA32_MPERF counts at the rate of max advertised frequency
+ * IA32_APERF counts at the rate of actual CPU frequency
+ * Only IA32_APERF/IA32_MPERF ratio is architecturally defined and
+ * no meaning should be associated with absolute values of these MSRs.
+ */
+unsigned int cpufreq_get_measured_perf(struct cpufreq_policy *policy,
+ unsigned int cpu)
+{
+ struct aperfmperf perf;
+ unsigned long ratio;
+ unsigned int retval;
+
+ if (smp_call_function_single(cpu, read_measured_perf_ctrs, &perf, 1))
+ return 0;
+
+ ratio = calc_aperfmperf_ratio(&per_cpu(acfreq_old_perf, cpu), &perf);
+ per_cpu(acfreq_old_perf, cpu) = perf;
+
+ retval = (policy->cpuinfo.max_freq * ratio) >> APERFMPERF_SHIFT;
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(cpufreq_get_measured_perf);
+MODULE_LICENSE("GPL");
diff --git a/drivers/cpufreq/mperf.h b/drivers/cpufreq/mperf.h
new file mode 100644
index 00000000000..5dbf2950dc2
--- /dev/null
+++ b/drivers/cpufreq/mperf.h
@@ -0,0 +1,9 @@
+/*
+ * (c) 2010 Advanced Micro Devices, Inc.
+ * Your use of this code is subject to the terms and conditions of the
+ * GNU general public license version 2. See "COPYING" or
+ * http://www.gnu.org/licenses/gpl.html
+ */
+
+unsigned int cpufreq_get_measured_perf(struct cpufreq_policy *policy,
+ unsigned int cpu);
diff --git a/drivers/cpufreq/p4-clockmod.c b/drivers/cpufreq/p4-clockmod.c
new file mode 100644
index 00000000000..6be3e0760c2
--- /dev/null
+++ b/drivers/cpufreq/p4-clockmod.c
@@ -0,0 +1,329 @@
+/*
+ * Pentium 4/Xeon CPU on demand clock modulation/speed scaling
+ * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de>
+ * (C) 2002 Zwane Mwaikambo <zwane@commfireservices.com>
+ * (C) 2002 Arjan van de Ven <arjanv@redhat.com>
+ * (C) 2002 Tora T. Engstad
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * The author(s) of this software shall not be held liable for damages
+ * of any nature resulting due to the use of this software. This
+ * software is provided AS-IS with no warranties.
+ *
+ * Date Errata Description
+ * 20020525 N44, O17 12.5% or 25% DC causes lockup
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/cpufreq.h>
+#include <linux/cpumask.h>
+#include <linux/timex.h>
+
+#include <asm/processor.h>
+#include <asm/msr.h>
+#include <asm/timer.h>
+
+#include "speedstep-lib.h"
+
+#define PFX "p4-clockmod: "
+
+/*
+ * Duty Cycle (3bits), note DC_DISABLE is not specified in
+ * intel docs i just use it to mean disable
+ */
+enum {
+ DC_RESV, DC_DFLT, DC_25PT, DC_38PT, DC_50PT,
+ DC_64PT, DC_75PT, DC_88PT, DC_DISABLE
+};
+
+#define DC_ENTRIES 8
+
+
+static int has_N44_O17_errata[NR_CPUS];
+static unsigned int stock_freq;
+static struct cpufreq_driver p4clockmod_driver;
+static unsigned int cpufreq_p4_get(unsigned int cpu);
+
+static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate)
+{
+ u32 l, h;
+
+ if (!cpu_online(cpu) ||
+ (newstate > DC_DISABLE) || (newstate == DC_RESV))
+ return -EINVAL;
+
+ rdmsr_on_cpu(cpu, MSR_IA32_THERM_STATUS, &l, &h);
+
+ if (l & 0x01)
+ pr_debug("CPU#%d currently thermal throttled\n", cpu);
+
+ if (has_N44_O17_errata[cpu] &&
+ (newstate == DC_25PT || newstate == DC_DFLT))
+ newstate = DC_38PT;
+
+ rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h);
+ if (newstate == DC_DISABLE) {
+ pr_debug("CPU#%d disabling modulation\n", cpu);
+ wrmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, l & ~(1<<4), h);
+ } else {
+ pr_debug("CPU#%d setting duty cycle to %d%%\n",
+ cpu, ((125 * newstate) / 10));
+ /* bits 63 - 5 : reserved
+ * bit 4 : enable/disable
+ * bits 3-1 : duty cycle
+ * bit 0 : reserved
+ */
+ l = (l & ~14);
+ l = l | (1<<4) | ((newstate & 0x7)<<1);
+ wrmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, l, h);
+ }
+
+ return 0;
+}
+
+
+static struct cpufreq_frequency_table p4clockmod_table[] = {
+ {DC_RESV, CPUFREQ_ENTRY_INVALID},
+ {DC_DFLT, 0},
+ {DC_25PT, 0},
+ {DC_38PT, 0},
+ {DC_50PT, 0},
+ {DC_64PT, 0},
+ {DC_75PT, 0},
+ {DC_88PT, 0},
+ {DC_DISABLE, 0},
+ {DC_RESV, CPUFREQ_TABLE_END},
+};
+
+
+static int cpufreq_p4_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ unsigned int newstate = DC_RESV;
+ struct cpufreq_freqs freqs;
+ int i;
+
+ if (cpufreq_frequency_table_target(policy, &p4clockmod_table[0],
+ target_freq, relation, &newstate))
+ return -EINVAL;
+
+ freqs.old = cpufreq_p4_get(policy->cpu);
+ freqs.new = stock_freq * p4clockmod_table[newstate].index / 8;
+
+ if (freqs.new == freqs.old)
+ return 0;
+
+ /* notifiers */
+ for_each_cpu(i, policy->cpus) {
+ freqs.cpu = i;
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ }
+
+ /* run on each logical CPU,
+ * see section 13.15.3 of IA32 Intel Architecture Software
+ * Developer's Manual, Volume 3
+ */
+ for_each_cpu(i, policy->cpus)
+ cpufreq_p4_setdc(i, p4clockmod_table[newstate].index);
+
+ /* notifiers */
+ for_each_cpu(i, policy->cpus) {
+ freqs.cpu = i;
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
+
+ return 0;
+}
+
+
+static int cpufreq_p4_verify(struct cpufreq_policy *policy)
+{
+ return cpufreq_frequency_table_verify(policy, &p4clockmod_table[0]);
+}
+
+
+static unsigned int cpufreq_p4_get_frequency(struct cpuinfo_x86 *c)
+{
+ if (c->x86 == 0x06) {
+ if (cpu_has(c, X86_FEATURE_EST))
+ printk_once(KERN_WARNING PFX "Warning: EST-capable "
+ "CPU detected. The acpi-cpufreq module offers "
+ "voltage scaling in addition to frequency "
+ "scaling. You should use that instead of "
+ "p4-clockmod, if possible.\n");
+ switch (c->x86_model) {
+ case 0x0E: /* Core */
+ case 0x0F: /* Core Duo */
+ case 0x16: /* Celeron Core */
+ case 0x1C: /* Atom */
+ p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
+ return speedstep_get_frequency(SPEEDSTEP_CPU_PCORE);
+ case 0x0D: /* Pentium M (Dothan) */
+ p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
+ /* fall through */
+ case 0x09: /* Pentium M (Banias) */
+ return speedstep_get_frequency(SPEEDSTEP_CPU_PM);
+ }
+ }
+
+ if (c->x86 != 0xF)
+ return 0;
+
+ /* on P-4s, the TSC runs with constant frequency independent whether
+ * throttling is active or not. */
+ p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
+
+ if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4M) {
+ printk(KERN_WARNING PFX "Warning: Pentium 4-M detected. "
+ "The speedstep-ich or acpi cpufreq modules offer "
+ "voltage scaling in addition of frequency scaling. "
+ "You should use either one instead of p4-clockmod, "
+ "if possible.\n");
+ return speedstep_get_frequency(SPEEDSTEP_CPU_P4M);
+ }
+
+ return speedstep_get_frequency(SPEEDSTEP_CPU_P4D);
+}
+
+
+
+static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy)
+{
+ struct cpuinfo_x86 *c = &cpu_data(policy->cpu);
+ int cpuid = 0;
+ unsigned int i;
+
+#ifdef CONFIG_SMP
+ cpumask_copy(policy->cpus, cpu_sibling_mask(policy->cpu));
+#endif
+
+ /* Errata workaround */
+ cpuid = (c->x86 << 8) | (c->x86_model << 4) | c->x86_mask;
+ switch (cpuid) {
+ case 0x0f07:
+ case 0x0f0a:
+ case 0x0f11:
+ case 0x0f12:
+ has_N44_O17_errata[policy->cpu] = 1;
+ pr_debug("has errata -- disabling low frequencies\n");
+ }
+
+ if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4D &&
+ c->x86_model < 2) {
+ /* switch to maximum frequency and measure result */
+ cpufreq_p4_setdc(policy->cpu, DC_DISABLE);
+ recalibrate_cpu_khz();
+ }
+ /* get max frequency */
+ stock_freq = cpufreq_p4_get_frequency(c);
+ if (!stock_freq)
+ return -EINVAL;
+
+ /* table init */
+ for (i = 1; (p4clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++) {
+ if ((i < 2) && (has_N44_O17_errata[policy->cpu]))
+ p4clockmod_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+ else
+ p4clockmod_table[i].frequency = (stock_freq * i)/8;
+ }
+ cpufreq_frequency_table_get_attr(p4clockmod_table, policy->cpu);
+
+ /* cpuinfo and default policy values */
+
+ /* the transition latency is set to be 1 higher than the maximum
+ * transition latency of the ondemand governor */
+ policy->cpuinfo.transition_latency = 10000001;
+ policy->cur = stock_freq;
+
+ return cpufreq_frequency_table_cpuinfo(policy, &p4clockmod_table[0]);
+}
+
+
+static int cpufreq_p4_cpu_exit(struct cpufreq_policy *policy)
+{
+ cpufreq_frequency_table_put_attr(policy->cpu);
+ return 0;
+}
+
+static unsigned int cpufreq_p4_get(unsigned int cpu)
+{
+ u32 l, h;
+
+ rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h);
+
+ if (l & 0x10) {
+ l = l >> 1;
+ l &= 0x7;
+ } else
+ l = DC_DISABLE;
+
+ if (l != DC_DISABLE)
+ return stock_freq * l / 8;
+
+ return stock_freq;
+}
+
+static struct freq_attr *p4clockmod_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+static struct cpufreq_driver p4clockmod_driver = {
+ .verify = cpufreq_p4_verify,
+ .target = cpufreq_p4_target,
+ .init = cpufreq_p4_cpu_init,
+ .exit = cpufreq_p4_cpu_exit,
+ .get = cpufreq_p4_get,
+ .name = "p4-clockmod",
+ .owner = THIS_MODULE,
+ .attr = p4clockmod_attr,
+};
+
+
+static int __init cpufreq_p4_init(void)
+{
+ struct cpuinfo_x86 *c = &cpu_data(0);
+ int ret;
+
+ /*
+ * THERM_CONTROL is architectural for IA32 now, so
+ * we can rely on the capability checks
+ */
+ if (c->x86_vendor != X86_VENDOR_INTEL)
+ return -ENODEV;
+
+ if (!test_cpu_cap(c, X86_FEATURE_ACPI) ||
+ !test_cpu_cap(c, X86_FEATURE_ACC))
+ return -ENODEV;
+
+ ret = cpufreq_register_driver(&p4clockmod_driver);
+ if (!ret)
+ printk(KERN_INFO PFX "P4/Xeon(TM) CPU On-Demand Clock "
+ "Modulation available\n");
+
+ return ret;
+}
+
+
+static void __exit cpufreq_p4_exit(void)
+{
+ cpufreq_unregister_driver(&p4clockmod_driver);
+}
+
+
+MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
+MODULE_DESCRIPTION("cpufreq driver for Pentium(TM) 4/Xeon(TM)");
+MODULE_LICENSE("GPL");
+
+late_initcall(cpufreq_p4_init);
+module_exit(cpufreq_p4_exit);
diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c
new file mode 100644
index 00000000000..7b0603eb012
--- /dev/null
+++ b/drivers/cpufreq/pcc-cpufreq.c
@@ -0,0 +1,621 @@
+/*
+ * pcc-cpufreq.c - Processor Clocking Control firmware cpufreq interface
+ *
+ * Copyright (C) 2009 Red Hat, Matthew Garrett <mjg@redhat.com>
+ * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
+ * Nagananda Chumbalkar <nagananda.chumbalkar@hp.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or NON
+ * INFRINGEMENT. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/sched.h>
+#include <linux/cpufreq.h>
+#include <linux/compiler.h>
+#include <linux/slab.h>
+
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+
+#include <acpi/processor.h>
+
+#define PCC_VERSION "1.10.00"
+#define POLL_LOOPS 300
+
+#define CMD_COMPLETE 0x1
+#define CMD_GET_FREQ 0x0
+#define CMD_SET_FREQ 0x1
+
+#define BUF_SZ 4
+
+struct pcc_register_resource {
+ u8 descriptor;
+ u16 length;
+ u8 space_id;
+ u8 bit_width;
+ u8 bit_offset;
+ u8 access_size;
+ u64 address;
+} __attribute__ ((packed));
+
+struct pcc_memory_resource {
+ u8 descriptor;
+ u16 length;
+ u8 space_id;
+ u8 resource_usage;
+ u8 type_specific;
+ u64 granularity;
+ u64 minimum;
+ u64 maximum;
+ u64 translation_offset;
+ u64 address_length;
+} __attribute__ ((packed));
+
+static struct cpufreq_driver pcc_cpufreq_driver;
+
+struct pcc_header {
+ u32 signature;
+ u16 length;
+ u8 major;
+ u8 minor;
+ u32 features;
+ u16 command;
+ u16 status;
+ u32 latency;
+ u32 minimum_time;
+ u32 maximum_time;
+ u32 nominal;
+ u32 throttled_frequency;
+ u32 minimum_frequency;
+};
+
+static void __iomem *pcch_virt_addr;
+static struct pcc_header __iomem *pcch_hdr;
+
+static DEFINE_SPINLOCK(pcc_lock);
+
+static struct acpi_generic_address doorbell;
+
+static u64 doorbell_preserve;
+static u64 doorbell_write;
+
+static u8 OSC_UUID[16] = {0x9F, 0x2C, 0x9B, 0x63, 0x91, 0x70, 0x1f, 0x49,
+ 0xBB, 0x4F, 0xA5, 0x98, 0x2F, 0xA1, 0xB5, 0x46};
+
+struct pcc_cpu {
+ u32 input_offset;
+ u32 output_offset;
+};
+
+static struct pcc_cpu __percpu *pcc_cpu_info;
+
+static int pcc_cpufreq_verify(struct cpufreq_policy *policy)
+{
+ cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+ policy->cpuinfo.max_freq);
+ return 0;
+}
+
+static inline void pcc_cmd(void)
+{
+ u64 doorbell_value;
+ int i;
+
+ acpi_read(&doorbell_value, &doorbell);
+ acpi_write((doorbell_value & doorbell_preserve) | doorbell_write,
+ &doorbell);
+
+ for (i = 0; i < POLL_LOOPS; i++) {
+ if (ioread16(&pcch_hdr->status) & CMD_COMPLETE)
+ break;
+ }
+}
+
+static inline void pcc_clear_mapping(void)
+{
+ if (pcch_virt_addr)
+ iounmap(pcch_virt_addr);
+ pcch_virt_addr = NULL;
+}
+
+static unsigned int pcc_get_freq(unsigned int cpu)
+{
+ struct pcc_cpu *pcc_cpu_data;
+ unsigned int curr_freq;
+ unsigned int freq_limit;
+ u16 status;
+ u32 input_buffer;
+ u32 output_buffer;
+
+ spin_lock(&pcc_lock);
+
+ pr_debug("get: get_freq for CPU %d\n", cpu);
+ pcc_cpu_data = per_cpu_ptr(pcc_cpu_info, cpu);
+
+ input_buffer = 0x1;
+ iowrite32(input_buffer,
+ (pcch_virt_addr + pcc_cpu_data->input_offset));
+ iowrite16(CMD_GET_FREQ, &pcch_hdr->command);
+
+ pcc_cmd();
+
+ output_buffer =
+ ioread32(pcch_virt_addr + pcc_cpu_data->output_offset);
+
+ /* Clear the input buffer - we are done with the current command */
+ memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ);
+
+ status = ioread16(&pcch_hdr->status);
+ if (status != CMD_COMPLETE) {
+ pr_debug("get: FAILED: for CPU %d, status is %d\n",
+ cpu, status);
+ goto cmd_incomplete;
+ }
+ iowrite16(0, &pcch_hdr->status);
+ curr_freq = (((ioread32(&pcch_hdr->nominal) * (output_buffer & 0xff))
+ / 100) * 1000);
+
+ pr_debug("get: SUCCESS: (virtual) output_offset for cpu %d is "
+ "0x%p, contains a value of: 0x%x. Speed is: %d MHz\n",
+ cpu, (pcch_virt_addr + pcc_cpu_data->output_offset),
+ output_buffer, curr_freq);
+
+ freq_limit = (output_buffer >> 8) & 0xff;
+ if (freq_limit != 0xff) {
+ pr_debug("get: frequency for cpu %d is being temporarily"
+ " capped at %d\n", cpu, curr_freq);
+ }
+
+ spin_unlock(&pcc_lock);
+ return curr_freq;
+
+cmd_incomplete:
+ iowrite16(0, &pcch_hdr->status);
+ spin_unlock(&pcc_lock);
+ return 0;
+}
+
+static int pcc_cpufreq_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ struct pcc_cpu *pcc_cpu_data;
+ struct cpufreq_freqs freqs;
+ u16 status;
+ u32 input_buffer;
+ int cpu;
+
+ spin_lock(&pcc_lock);
+ cpu = policy->cpu;
+ pcc_cpu_data = per_cpu_ptr(pcc_cpu_info, cpu);
+
+ pr_debug("target: CPU %d should go to target freq: %d "
+ "(virtual) input_offset is 0x%p\n",
+ cpu, target_freq,
+ (pcch_virt_addr + pcc_cpu_data->input_offset));
+
+ freqs.new = target_freq;
+ freqs.cpu = cpu;
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ input_buffer = 0x1 | (((target_freq * 100)
+ / (ioread32(&pcch_hdr->nominal) * 1000)) << 8);
+ iowrite32(input_buffer,
+ (pcch_virt_addr + pcc_cpu_data->input_offset));
+ iowrite16(CMD_SET_FREQ, &pcch_hdr->command);
+
+ pcc_cmd();
+
+ /* Clear the input buffer - we are done with the current command */
+ memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ);
+
+ status = ioread16(&pcch_hdr->status);
+ if (status != CMD_COMPLETE) {
+ pr_debug("target: FAILED for cpu %d, with status: 0x%x\n",
+ cpu, status);
+ goto cmd_incomplete;
+ }
+ iowrite16(0, &pcch_hdr->status);
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ pr_debug("target: was SUCCESSFUL for cpu %d\n", cpu);
+ spin_unlock(&pcc_lock);
+
+ return 0;
+
+cmd_incomplete:
+ iowrite16(0, &pcch_hdr->status);
+ spin_unlock(&pcc_lock);
+ return -EINVAL;
+}
+
+static int pcc_get_offset(int cpu)
+{
+ acpi_status status;
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object *pccp, *offset;
+ struct pcc_cpu *pcc_cpu_data;
+ struct acpi_processor *pr;
+ int ret = 0;
+
+ pr = per_cpu(processors, cpu);
+ pcc_cpu_data = per_cpu_ptr(pcc_cpu_info, cpu);
+
+ status = acpi_evaluate_object(pr->handle, "PCCP", NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ pccp = buffer.pointer;
+ if (!pccp || pccp->type != ACPI_TYPE_PACKAGE) {
+ ret = -ENODEV;
+ goto out_free;
+ };
+
+ offset = &(pccp->package.elements[0]);
+ if (!offset || offset->type != ACPI_TYPE_INTEGER) {
+ ret = -ENODEV;
+ goto out_free;
+ }
+
+ pcc_cpu_data->input_offset = offset->integer.value;
+
+ offset = &(pccp->package.elements[1]);
+ if (!offset || offset->type != ACPI_TYPE_INTEGER) {
+ ret = -ENODEV;
+ goto out_free;
+ }
+
+ pcc_cpu_data->output_offset = offset->integer.value;
+
+ memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ);
+ memset_io((pcch_virt_addr + pcc_cpu_data->output_offset), 0, BUF_SZ);
+
+ pr_debug("pcc_get_offset: for CPU %d: pcc_cpu_data "
+ "input_offset: 0x%x, pcc_cpu_data output_offset: 0x%x\n",
+ cpu, pcc_cpu_data->input_offset, pcc_cpu_data->output_offset);
+out_free:
+ kfree(buffer.pointer);
+ return ret;
+}
+
+static int __init pcc_cpufreq_do_osc(acpi_handle *handle)
+{
+ acpi_status status;
+ struct acpi_object_list input;
+ struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object in_params[4];
+ union acpi_object *out_obj;
+ u32 capabilities[2];
+ u32 errors;
+ u32 supported;
+ int ret = 0;
+
+ input.count = 4;
+ input.pointer = in_params;
+ in_params[0].type = ACPI_TYPE_BUFFER;
+ in_params[0].buffer.length = 16;
+ in_params[0].buffer.pointer = OSC_UUID;
+ in_params[1].type = ACPI_TYPE_INTEGER;
+ in_params[1].integer.value = 1;
+ in_params[2].type = ACPI_TYPE_INTEGER;
+ in_params[2].integer.value = 2;
+ in_params[3].type = ACPI_TYPE_BUFFER;
+ in_params[3].buffer.length = 8;
+ in_params[3].buffer.pointer = (u8 *)&capabilities;
+
+ capabilities[0] = OSC_QUERY_ENABLE;
+ capabilities[1] = 0x1;
+
+ status = acpi_evaluate_object(*handle, "_OSC", &input, &output);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ if (!output.length)
+ return -ENODEV;
+
+ out_obj = output.pointer;
+ if (out_obj->type != ACPI_TYPE_BUFFER) {
+ ret = -ENODEV;
+ goto out_free;
+ }
+
+ errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0);
+ if (errors) {
+ ret = -ENODEV;
+ goto out_free;
+ }
+
+ supported = *((u32 *)(out_obj->buffer.pointer + 4));
+ if (!(supported & 0x1)) {
+ ret = -ENODEV;
+ goto out_free;
+ }
+
+ kfree(output.pointer);
+ capabilities[0] = 0x0;
+ capabilities[1] = 0x1;
+
+ status = acpi_evaluate_object(*handle, "_OSC", &input, &output);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ if (!output.length)
+ return -ENODEV;
+
+ out_obj = output.pointer;
+ if (out_obj->type != ACPI_TYPE_BUFFER) {
+ ret = -ENODEV;
+ goto out_free;
+ }
+
+ errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0);
+ if (errors) {
+ ret = -ENODEV;
+ goto out_free;
+ }
+
+ supported = *((u32 *)(out_obj->buffer.pointer + 4));
+ if (!(supported & 0x1)) {
+ ret = -ENODEV;
+ goto out_free;
+ }
+
+out_free:
+ kfree(output.pointer);
+ return ret;
+}
+
+static int __init pcc_cpufreq_probe(void)
+{
+ acpi_status status;
+ struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
+ struct pcc_memory_resource *mem_resource;
+ struct pcc_register_resource *reg_resource;
+ union acpi_object *out_obj, *member;
+ acpi_handle handle, osc_handle, pcch_handle;
+ int ret = 0;
+
+ status = acpi_get_handle(NULL, "\\_SB", &handle);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ status = acpi_get_handle(handle, "PCCH", &pcch_handle);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ status = acpi_get_handle(handle, "_OSC", &osc_handle);
+ if (ACPI_SUCCESS(status)) {
+ ret = pcc_cpufreq_do_osc(&osc_handle);
+ if (ret)
+ pr_debug("probe: _OSC evaluation did not succeed\n");
+ /* Firmware's use of _OSC is optional */
+ ret = 0;
+ }
+
+ status = acpi_evaluate_object(handle, "PCCH", NULL, &output);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ out_obj = output.pointer;
+ if (out_obj->type != ACPI_TYPE_PACKAGE) {
+ ret = -ENODEV;
+ goto out_free;
+ }
+
+ member = &out_obj->package.elements[0];
+ if (member->type != ACPI_TYPE_BUFFER) {
+ ret = -ENODEV;
+ goto out_free;
+ }
+
+ mem_resource = (struct pcc_memory_resource *)member->buffer.pointer;
+
+ pr_debug("probe: mem_resource descriptor: 0x%x,"
+ " length: %d, space_id: %d, resource_usage: %d,"
+ " type_specific: %d, granularity: 0x%llx,"
+ " minimum: 0x%llx, maximum: 0x%llx,"
+ " translation_offset: 0x%llx, address_length: 0x%llx\n",
+ mem_resource->descriptor, mem_resource->length,
+ mem_resource->space_id, mem_resource->resource_usage,
+ mem_resource->type_specific, mem_resource->granularity,
+ mem_resource->minimum, mem_resource->maximum,
+ mem_resource->translation_offset,
+ mem_resource->address_length);
+
+ if (mem_resource->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+ ret = -ENODEV;
+ goto out_free;
+ }
+
+ pcch_virt_addr = ioremap_nocache(mem_resource->minimum,
+ mem_resource->address_length);
+ if (pcch_virt_addr == NULL) {
+ pr_debug("probe: could not map shared mem region\n");
+ goto out_free;
+ }
+ pcch_hdr = pcch_virt_addr;
+
+ pr_debug("probe: PCCH header (virtual) addr: 0x%p\n", pcch_hdr);
+ pr_debug("probe: PCCH header is at physical address: 0x%llx,"
+ " signature: 0x%x, length: %d bytes, major: %d, minor: %d,"
+ " supported features: 0x%x, command field: 0x%x,"
+ " status field: 0x%x, nominal latency: %d us\n",
+ mem_resource->minimum, ioread32(&pcch_hdr->signature),
+ ioread16(&pcch_hdr->length), ioread8(&pcch_hdr->major),
+ ioread8(&pcch_hdr->minor), ioread32(&pcch_hdr->features),
+ ioread16(&pcch_hdr->command), ioread16(&pcch_hdr->status),
+ ioread32(&pcch_hdr->latency));
+
+ pr_debug("probe: min time between commands: %d us,"
+ " max time between commands: %d us,"
+ " nominal CPU frequency: %d MHz,"
+ " minimum CPU frequency: %d MHz,"
+ " minimum CPU frequency without throttling: %d MHz\n",
+ ioread32(&pcch_hdr->minimum_time),
+ ioread32(&pcch_hdr->maximum_time),
+ ioread32(&pcch_hdr->nominal),
+ ioread32(&pcch_hdr->throttled_frequency),
+ ioread32(&pcch_hdr->minimum_frequency));
+
+ member = &out_obj->package.elements[1];
+ if (member->type != ACPI_TYPE_BUFFER) {
+ ret = -ENODEV;
+ goto pcch_free;
+ }
+
+ reg_resource = (struct pcc_register_resource *)member->buffer.pointer;
+
+ doorbell.space_id = reg_resource->space_id;
+ doorbell.bit_width = reg_resource->bit_width;
+ doorbell.bit_offset = reg_resource->bit_offset;
+ doorbell.access_width = 64;
+ doorbell.address = reg_resource->address;
+
+ pr_debug("probe: doorbell: space_id is %d, bit_width is %d, "
+ "bit_offset is %d, access_width is %d, address is 0x%llx\n",
+ doorbell.space_id, doorbell.bit_width, doorbell.bit_offset,
+ doorbell.access_width, reg_resource->address);
+
+ member = &out_obj->package.elements[2];
+ if (member->type != ACPI_TYPE_INTEGER) {
+ ret = -ENODEV;
+ goto pcch_free;
+ }
+
+ doorbell_preserve = member->integer.value;
+
+ member = &out_obj->package.elements[3];
+ if (member->type != ACPI_TYPE_INTEGER) {
+ ret = -ENODEV;
+ goto pcch_free;
+ }
+
+ doorbell_write = member->integer.value;
+
+ pr_debug("probe: doorbell_preserve: 0x%llx,"
+ " doorbell_write: 0x%llx\n",
+ doorbell_preserve, doorbell_write);
+
+ pcc_cpu_info = alloc_percpu(struct pcc_cpu);
+ if (!pcc_cpu_info) {
+ ret = -ENOMEM;
+ goto pcch_free;
+ }
+
+ printk(KERN_DEBUG "pcc-cpufreq: (v%s) driver loaded with frequency"
+ " limits: %d MHz, %d MHz\n", PCC_VERSION,
+ ioread32(&pcch_hdr->minimum_frequency),
+ ioread32(&pcch_hdr->nominal));
+ kfree(output.pointer);
+ return ret;
+pcch_free:
+ pcc_clear_mapping();
+out_free:
+ kfree(output.pointer);
+ return ret;
+}
+
+static int pcc_cpufreq_cpu_init(struct cpufreq_policy *policy)
+{
+ unsigned int cpu = policy->cpu;
+ unsigned int result = 0;
+
+ if (!pcch_virt_addr) {
+ result = -1;
+ goto out;
+ }
+
+ result = pcc_get_offset(cpu);
+ if (result) {
+ pr_debug("init: PCCP evaluation failed\n");
+ goto out;
+ }
+
+ policy->max = policy->cpuinfo.max_freq =
+ ioread32(&pcch_hdr->nominal) * 1000;
+ policy->min = policy->cpuinfo.min_freq =
+ ioread32(&pcch_hdr->minimum_frequency) * 1000;
+ policy->cur = pcc_get_freq(cpu);
+
+ if (!policy->cur) {
+ pr_debug("init: Unable to get current CPU frequency\n");
+ result = -EINVAL;
+ goto out;
+ }
+
+ pr_debug("init: policy->max is %d, policy->min is %d\n",
+ policy->max, policy->min);
+out:
+ return result;
+}
+
+static int pcc_cpufreq_cpu_exit(struct cpufreq_policy *policy)
+{
+ return 0;
+}
+
+static struct cpufreq_driver pcc_cpufreq_driver = {
+ .flags = CPUFREQ_CONST_LOOPS,
+ .get = pcc_get_freq,
+ .verify = pcc_cpufreq_verify,
+ .target = pcc_cpufreq_target,
+ .init = pcc_cpufreq_cpu_init,
+ .exit = pcc_cpufreq_cpu_exit,
+ .name = "pcc-cpufreq",
+ .owner = THIS_MODULE,
+};
+
+static int __init pcc_cpufreq_init(void)
+{
+ int ret;
+
+ if (acpi_disabled)
+ return 0;
+
+ ret = pcc_cpufreq_probe();
+ if (ret) {
+ pr_debug("pcc_cpufreq_init: PCCH evaluation failed\n");
+ return ret;
+ }
+
+ ret = cpufreq_register_driver(&pcc_cpufreq_driver);
+
+ return ret;
+}
+
+static void __exit pcc_cpufreq_exit(void)
+{
+ cpufreq_unregister_driver(&pcc_cpufreq_driver);
+
+ pcc_clear_mapping();
+
+ free_percpu(pcc_cpu_info);
+}
+
+MODULE_AUTHOR("Matthew Garrett, Naga Chumbalkar");
+MODULE_VERSION(PCC_VERSION);
+MODULE_DESCRIPTION("Processor Clocking Control interface driver");
+MODULE_LICENSE("GPL");
+
+late_initcall(pcc_cpufreq_init);
+module_exit(pcc_cpufreq_exit);
diff --git a/drivers/cpufreq/powernow-k6.c b/drivers/cpufreq/powernow-k6.c
new file mode 100644
index 00000000000..b3379d6a5c5
--- /dev/null
+++ b/drivers/cpufreq/powernow-k6.c
@@ -0,0 +1,261 @@
+/*
+ * This file was based upon code in Powertweak Linux (http://powertweak.sf.net)
+ * (C) 2000-2003 Dave Jones, Arjan van de Ven, Janne Pänkälä,
+ * Dominik Brodowski.
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ *
+ * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/ioport.h>
+#include <linux/timex.h>
+#include <linux/io.h>
+
+#include <asm/msr.h>
+
+#define POWERNOW_IOPORT 0xfff0 /* it doesn't matter where, as long
+ as it is unused */
+
+#define PFX "powernow-k6: "
+static unsigned int busfreq; /* FSB, in 10 kHz */
+static unsigned int max_multiplier;
+
+
+/* Clock ratio multiplied by 10 - see table 27 in AMD#23446 */
+static struct cpufreq_frequency_table clock_ratio[] = {
+ {45, /* 000 -> 4.5x */ 0},
+ {50, /* 001 -> 5.0x */ 0},
+ {40, /* 010 -> 4.0x */ 0},
+ {55, /* 011 -> 5.5x */ 0},
+ {20, /* 100 -> 2.0x */ 0},
+ {30, /* 101 -> 3.0x */ 0},
+ {60, /* 110 -> 6.0x */ 0},
+ {35, /* 111 -> 3.5x */ 0},
+ {0, CPUFREQ_TABLE_END}
+};
+
+
+/**
+ * powernow_k6_get_cpu_multiplier - returns the current FSB multiplier
+ *
+ * Returns the current setting of the frequency multiplier. Core clock
+ * speed is frequency of the Front-Side Bus multiplied with this value.
+ */
+static int powernow_k6_get_cpu_multiplier(void)
+{
+ u64 invalue = 0;
+ u32 msrval;
+
+ msrval = POWERNOW_IOPORT + 0x1;
+ wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */
+ invalue = inl(POWERNOW_IOPORT + 0x8);
+ msrval = POWERNOW_IOPORT + 0x0;
+ wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */
+
+ return clock_ratio[(invalue >> 5)&7].index;
+}
+
+
+/**
+ * powernow_k6_set_state - set the PowerNow! multiplier
+ * @best_i: clock_ratio[best_i] is the target multiplier
+ *
+ * Tries to change the PowerNow! multiplier
+ */
+static void powernow_k6_set_state(unsigned int best_i)
+{
+ unsigned long outvalue = 0, invalue = 0;
+ unsigned long msrval;
+ struct cpufreq_freqs freqs;
+
+ if (clock_ratio[best_i].index > max_multiplier) {
+ printk(KERN_ERR PFX "invalid target frequency\n");
+ return;
+ }
+
+ freqs.old = busfreq * powernow_k6_get_cpu_multiplier();
+ freqs.new = busfreq * clock_ratio[best_i].index;
+ freqs.cpu = 0; /* powernow-k6.c is UP only driver */
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ /* we now need to transform best_i to the BVC format, see AMD#23446 */
+
+ outvalue = (1<<12) | (1<<10) | (1<<9) | (best_i<<5);
+
+ msrval = POWERNOW_IOPORT + 0x1;
+ wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */
+ invalue = inl(POWERNOW_IOPORT + 0x8);
+ invalue = invalue & 0xf;
+ outvalue = outvalue | invalue;
+ outl(outvalue , (POWERNOW_IOPORT + 0x8));
+ msrval = POWERNOW_IOPORT + 0x0;
+ wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+ return;
+}
+
+
+/**
+ * powernow_k6_verify - verifies a new CPUfreq policy
+ * @policy: new policy
+ *
+ * Policy must be within lowest and highest possible CPU Frequency,
+ * and at least one possible state must be within min and max.
+ */
+static int powernow_k6_verify(struct cpufreq_policy *policy)
+{
+ return cpufreq_frequency_table_verify(policy, &clock_ratio[0]);
+}
+
+
+/**
+ * powernow_k6_setpolicy - sets a new CPUFreq policy
+ * @policy: new policy
+ * @target_freq: the target frequency
+ * @relation: how that frequency relates to achieved frequency
+ * (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
+ *
+ * sets a new CPUFreq policy
+ */
+static int powernow_k6_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ unsigned int newstate = 0;
+
+ if (cpufreq_frequency_table_target(policy, &clock_ratio[0],
+ target_freq, relation, &newstate))
+ return -EINVAL;
+
+ powernow_k6_set_state(newstate);
+
+ return 0;
+}
+
+
+static int powernow_k6_cpu_init(struct cpufreq_policy *policy)
+{
+ unsigned int i, f;
+ int result;
+
+ if (policy->cpu != 0)
+ return -ENODEV;
+
+ /* get frequencies */
+ max_multiplier = powernow_k6_get_cpu_multiplier();
+ busfreq = cpu_khz / max_multiplier;
+
+ /* table init */
+ for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) {
+ f = clock_ratio[i].index;
+ if (f > max_multiplier)
+ clock_ratio[i].frequency = CPUFREQ_ENTRY_INVALID;
+ else
+ clock_ratio[i].frequency = busfreq * f;
+ }
+
+ /* cpuinfo and default policy values */
+ policy->cpuinfo.transition_latency = 200000;
+ policy->cur = busfreq * max_multiplier;
+
+ result = cpufreq_frequency_table_cpuinfo(policy, clock_ratio);
+ if (result)
+ return result;
+
+ cpufreq_frequency_table_get_attr(clock_ratio, policy->cpu);
+
+ return 0;
+}
+
+
+static int powernow_k6_cpu_exit(struct cpufreq_policy *policy)
+{
+ unsigned int i;
+ for (i = 0; i < 8; i++) {
+ if (i == max_multiplier)
+ powernow_k6_set_state(i);
+ }
+ cpufreq_frequency_table_put_attr(policy->cpu);
+ return 0;
+}
+
+static unsigned int powernow_k6_get(unsigned int cpu)
+{
+ unsigned int ret;
+ ret = (busfreq * powernow_k6_get_cpu_multiplier());
+ return ret;
+}
+
+static struct freq_attr *powernow_k6_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+static struct cpufreq_driver powernow_k6_driver = {
+ .verify = powernow_k6_verify,
+ .target = powernow_k6_target,
+ .init = powernow_k6_cpu_init,
+ .exit = powernow_k6_cpu_exit,
+ .get = powernow_k6_get,
+ .name = "powernow-k6",
+ .owner = THIS_MODULE,
+ .attr = powernow_k6_attr,
+};
+
+
+/**
+ * powernow_k6_init - initializes the k6 PowerNow! CPUFreq driver
+ *
+ * Initializes the K6 PowerNow! support. Returns -ENODEV on unsupported
+ * devices, -EINVAL or -ENOMEM on problems during initiatization, and zero
+ * on success.
+ */
+static int __init powernow_k6_init(void)
+{
+ struct cpuinfo_x86 *c = &cpu_data(0);
+
+ if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 5) ||
+ ((c->x86_model != 12) && (c->x86_model != 13)))
+ return -ENODEV;
+
+ if (!request_region(POWERNOW_IOPORT, 16, "PowerNow!")) {
+ printk(KERN_INFO PFX "PowerNow IOPORT region already used.\n");
+ return -EIO;
+ }
+
+ if (cpufreq_register_driver(&powernow_k6_driver)) {
+ release_region(POWERNOW_IOPORT, 16);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+/**
+ * powernow_k6_exit - unregisters AMD K6-2+/3+ PowerNow! support
+ *
+ * Unregisters AMD K6-2+ / K6-3+ PowerNow! support.
+ */
+static void __exit powernow_k6_exit(void)
+{
+ cpufreq_unregister_driver(&powernow_k6_driver);
+ release_region(POWERNOW_IOPORT, 16);
+}
+
+
+MODULE_AUTHOR("Arjan van de Ven, Dave Jones <davej@redhat.com>, "
+ "Dominik Brodowski <linux@brodo.de>");
+MODULE_DESCRIPTION("PowerNow! driver for AMD K6-2+ / K6-3+ processors.");
+MODULE_LICENSE("GPL");
+
+module_init(powernow_k6_init);
+module_exit(powernow_k6_exit);
diff --git a/drivers/cpufreq/powernow-k7.c b/drivers/cpufreq/powernow-k7.c
new file mode 100644
index 00000000000..d71d9f37235
--- /dev/null
+++ b/drivers/cpufreq/powernow-k7.c
@@ -0,0 +1,747 @@
+/*
+ * AMD K7 Powernow driver.
+ * (C) 2003 Dave Jones on behalf of SuSE Labs.
+ * (C) 2003-2004 Dave Jones <davej@redhat.com>
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ * Based upon datasheets & sample CPUs kindly provided by AMD.
+ *
+ * Errata 5:
+ * CPU may fail to execute a FID/VID change in presence of interrupt.
+ * - We cli/sti on stepping A0 CPUs around the FID/VID transition.
+ * Errata 15:
+ * CPU with half frequency multipliers may hang upon wakeup from disconnect.
+ * - We disable half multipliers if ACPI is used on A0 stepping CPUs.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/dmi.h>
+#include <linux/timex.h>
+#include <linux/io.h>
+
+#include <asm/timer.h> /* Needed for recalibrate_cpu_khz() */
+#include <asm/msr.h>
+#include <asm/system.h>
+
+#ifdef CONFIG_X86_POWERNOW_K7_ACPI
+#include <linux/acpi.h>
+#include <acpi/processor.h>
+#endif
+
+#include "powernow-k7.h"
+
+#define PFX "powernow: "
+
+
+struct psb_s {
+ u8 signature[10];
+ u8 tableversion;
+ u8 flags;
+ u16 settlingtime;
+ u8 reserved1;
+ u8 numpst;
+};
+
+struct pst_s {
+ u32 cpuid;
+ u8 fsbspeed;
+ u8 maxfid;
+ u8 startvid;
+ u8 numpstates;
+};
+
+#ifdef CONFIG_X86_POWERNOW_K7_ACPI
+union powernow_acpi_control_t {
+ struct {
+ unsigned long fid:5,
+ vid:5,
+ sgtc:20,
+ res1:2;
+ } bits;
+ unsigned long val;
+};
+#endif
+
+/* divide by 1000 to get VCore voltage in V. */
+static const int mobile_vid_table[32] = {
+ 2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
+ 1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
+ 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
+ 1075, 1050, 1025, 1000, 975, 950, 925, 0,
+};
+
+/* divide by 10 to get FID. */
+static const int fid_codes[32] = {
+ 110, 115, 120, 125, 50, 55, 60, 65,
+ 70, 75, 80, 85, 90, 95, 100, 105,
+ 30, 190, 40, 200, 130, 135, 140, 210,
+ 150, 225, 160, 165, 170, 180, -1, -1,
+};
+
+/* This parameter is used in order to force ACPI instead of legacy method for
+ * configuration purpose.
+ */
+
+static int acpi_force;
+
+static struct cpufreq_frequency_table *powernow_table;
+
+static unsigned int can_scale_bus;
+static unsigned int can_scale_vid;
+static unsigned int minimum_speed = -1;
+static unsigned int maximum_speed;
+static unsigned int number_scales;
+static unsigned int fsb;
+static unsigned int latency;
+static char have_a0;
+
+static int check_fsb(unsigned int fsbspeed)
+{
+ int delta;
+ unsigned int f = fsb / 1000;
+
+ delta = (fsbspeed > f) ? fsbspeed - f : f - fsbspeed;
+ return delta < 5;
+}
+
+static int check_powernow(void)
+{
+ struct cpuinfo_x86 *c = &cpu_data(0);
+ unsigned int maxei, eax, ebx, ecx, edx;
+
+ if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 6)) {
+#ifdef MODULE
+ printk(KERN_INFO PFX "This module only works with "
+ "AMD K7 CPUs\n");
+#endif
+ return 0;
+ }
+
+ /* Get maximum capabilities */
+ maxei = cpuid_eax(0x80000000);
+ if (maxei < 0x80000007) { /* Any powernow info ? */
+#ifdef MODULE
+ printk(KERN_INFO PFX "No powernow capabilities detected\n");
+#endif
+ return 0;
+ }
+
+ if ((c->x86_model == 6) && (c->x86_mask == 0)) {
+ printk(KERN_INFO PFX "K7 660[A0] core detected, "
+ "enabling errata workarounds\n");
+ have_a0 = 1;
+ }
+
+ cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
+
+ /* Check we can actually do something before we say anything.*/
+ if (!(edx & (1 << 1 | 1 << 2)))
+ return 0;
+
+ printk(KERN_INFO PFX "PowerNOW! Technology present. Can scale: ");
+
+ if (edx & 1 << 1) {
+ printk("frequency");
+ can_scale_bus = 1;
+ }
+
+ if ((edx & (1 << 1 | 1 << 2)) == 0x6)
+ printk(" and ");
+
+ if (edx & 1 << 2) {
+ printk("voltage");
+ can_scale_vid = 1;
+ }
+
+ printk(".\n");
+ return 1;
+}
+
+#ifdef CONFIG_X86_POWERNOW_K7_ACPI
+static void invalidate_entry(unsigned int entry)
+{
+ powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID;
+}
+#endif
+
+static int get_ranges(unsigned char *pst)
+{
+ unsigned int j;
+ unsigned int speed;
+ u8 fid, vid;
+
+ powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) *
+ (number_scales + 1)), GFP_KERNEL);
+ if (!powernow_table)
+ return -ENOMEM;
+
+ for (j = 0 ; j < number_scales; j++) {
+ fid = *pst++;
+
+ powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10;
+ powernow_table[j].index = fid; /* lower 8 bits */
+
+ speed = powernow_table[j].frequency;
+
+ if ((fid_codes[fid] % 10) == 5) {
+#ifdef CONFIG_X86_POWERNOW_K7_ACPI
+ if (have_a0 == 1)
+ invalidate_entry(j);
+#endif
+ }
+
+ if (speed < minimum_speed)
+ minimum_speed = speed;
+ if (speed > maximum_speed)
+ maximum_speed = speed;
+
+ vid = *pst++;
+ powernow_table[j].index |= (vid << 8); /* upper 8 bits */
+
+ pr_debug(" FID: 0x%x (%d.%dx [%dMHz]) "
+ "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
+ fid_codes[fid] % 10, speed/1000, vid,
+ mobile_vid_table[vid]/1000,
+ mobile_vid_table[vid]%1000);
+ }
+ powernow_table[number_scales].frequency = CPUFREQ_TABLE_END;
+ powernow_table[number_scales].index = 0;
+
+ return 0;
+}
+
+
+static void change_FID(int fid)
+{
+ union msr_fidvidctl fidvidctl;
+
+ rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
+ if (fidvidctl.bits.FID != fid) {
+ fidvidctl.bits.SGTC = latency;
+ fidvidctl.bits.FID = fid;
+ fidvidctl.bits.VIDC = 0;
+ fidvidctl.bits.FIDC = 1;
+ wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
+ }
+}
+
+
+static void change_VID(int vid)
+{
+ union msr_fidvidctl fidvidctl;
+
+ rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
+ if (fidvidctl.bits.VID != vid) {
+ fidvidctl.bits.SGTC = latency;
+ fidvidctl.bits.VID = vid;
+ fidvidctl.bits.FIDC = 0;
+ fidvidctl.bits.VIDC = 1;
+ wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
+ }
+}
+
+
+static void change_speed(unsigned int index)
+{
+ u8 fid, vid;
+ struct cpufreq_freqs freqs;
+ union msr_fidvidstatus fidvidstatus;
+ int cfid;
+
+ /* fid are the lower 8 bits of the index we stored into
+ * the cpufreq frequency table in powernow_decode_bios,
+ * vid are the upper 8 bits.
+ */
+
+ fid = powernow_table[index].index & 0xFF;
+ vid = (powernow_table[index].index & 0xFF00) >> 8;
+
+ freqs.cpu = 0;
+
+ rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
+ cfid = fidvidstatus.bits.CFID;
+ freqs.old = fsb * fid_codes[cfid] / 10;
+
+ freqs.new = powernow_table[index].frequency;
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ /* Now do the magic poking into the MSRs. */
+
+ if (have_a0 == 1) /* A0 errata 5 */
+ local_irq_disable();
+
+ if (freqs.old > freqs.new) {
+ /* Going down, so change FID first */
+ change_FID(fid);
+ change_VID(vid);
+ } else {
+ /* Going up, so change VID first */
+ change_VID(vid);
+ change_FID(fid);
+ }
+
+
+ if (have_a0 == 1)
+ local_irq_enable();
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+}
+
+
+#ifdef CONFIG_X86_POWERNOW_K7_ACPI
+
+static struct acpi_processor_performance *acpi_processor_perf;
+
+static int powernow_acpi_init(void)
+{
+ int i;
+ int retval = 0;
+ union powernow_acpi_control_t pc;
+
+ if (acpi_processor_perf != NULL && powernow_table != NULL) {
+ retval = -EINVAL;
+ goto err0;
+ }
+
+ acpi_processor_perf = kzalloc(sizeof(struct acpi_processor_performance),
+ GFP_KERNEL);
+ if (!acpi_processor_perf) {
+ retval = -ENOMEM;
+ goto err0;
+ }
+
+ if (!zalloc_cpumask_var(&acpi_processor_perf->shared_cpu_map,
+ GFP_KERNEL)) {
+ retval = -ENOMEM;
+ goto err05;
+ }
+
+ if (acpi_processor_register_performance(acpi_processor_perf, 0)) {
+ retval = -EIO;
+ goto err1;
+ }
+
+ if (acpi_processor_perf->control_register.space_id !=
+ ACPI_ADR_SPACE_FIXED_HARDWARE) {
+ retval = -ENODEV;
+ goto err2;
+ }
+
+ if (acpi_processor_perf->status_register.space_id !=
+ ACPI_ADR_SPACE_FIXED_HARDWARE) {
+ retval = -ENODEV;
+ goto err2;
+ }
+
+ number_scales = acpi_processor_perf->state_count;
+
+ if (number_scales < 2) {
+ retval = -ENODEV;
+ goto err2;
+ }
+
+ powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) *
+ (number_scales + 1)), GFP_KERNEL);
+ if (!powernow_table) {
+ retval = -ENOMEM;
+ goto err2;
+ }
+
+ pc.val = (unsigned long) acpi_processor_perf->states[0].control;
+ for (i = 0; i < number_scales; i++) {
+ u8 fid, vid;
+ struct acpi_processor_px *state =
+ &acpi_processor_perf->states[i];
+ unsigned int speed, speed_mhz;
+
+ pc.val = (unsigned long) state->control;
+ pr_debug("acpi: P%d: %d MHz %d mW %d uS control %08x SGTC %d\n",
+ i,
+ (u32) state->core_frequency,
+ (u32) state->power,
+ (u32) state->transition_latency,
+ (u32) state->control,
+ pc.bits.sgtc);
+
+ vid = pc.bits.vid;
+ fid = pc.bits.fid;
+
+ powernow_table[i].frequency = fsb * fid_codes[fid] / 10;
+ powernow_table[i].index = fid; /* lower 8 bits */
+ powernow_table[i].index |= (vid << 8); /* upper 8 bits */
+
+ speed = powernow_table[i].frequency;
+ speed_mhz = speed / 1000;
+
+ /* processor_perflib will multiply the MHz value by 1000 to
+ * get a KHz value (e.g. 1266000). However, powernow-k7 works
+ * with true KHz values (e.g. 1266768). To ensure that all
+ * powernow frequencies are available, we must ensure that
+ * ACPI doesn't restrict them, so we round up the MHz value
+ * to ensure that perflib's computed KHz value is greater than
+ * or equal to powernow's KHz value.
+ */
+ if (speed % 1000 > 0)
+ speed_mhz++;
+
+ if ((fid_codes[fid] % 10) == 5) {
+ if (have_a0 == 1)
+ invalidate_entry(i);
+ }
+
+ pr_debug(" FID: 0x%x (%d.%dx [%dMHz]) "
+ "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
+ fid_codes[fid] % 10, speed_mhz, vid,
+ mobile_vid_table[vid]/1000,
+ mobile_vid_table[vid]%1000);
+
+ if (state->core_frequency != speed_mhz) {
+ state->core_frequency = speed_mhz;
+ pr_debug(" Corrected ACPI frequency to %d\n",
+ speed_mhz);
+ }
+
+ if (latency < pc.bits.sgtc)
+ latency = pc.bits.sgtc;
+
+ if (speed < minimum_speed)
+ minimum_speed = speed;
+ if (speed > maximum_speed)
+ maximum_speed = speed;
+ }
+
+ powernow_table[i].frequency = CPUFREQ_TABLE_END;
+ powernow_table[i].index = 0;
+
+ /* notify BIOS that we exist */
+ acpi_processor_notify_smm(THIS_MODULE);
+
+ return 0;
+
+err2:
+ acpi_processor_unregister_performance(acpi_processor_perf, 0);
+err1:
+ free_cpumask_var(acpi_processor_perf->shared_cpu_map);
+err05:
+ kfree(acpi_processor_perf);
+err0:
+ printk(KERN_WARNING PFX "ACPI perflib can not be used on "
+ "this platform\n");
+ acpi_processor_perf = NULL;
+ return retval;
+}
+#else
+static int powernow_acpi_init(void)
+{
+ printk(KERN_INFO PFX "no support for ACPI processor found."
+ " Please recompile your kernel with ACPI processor\n");
+ return -EINVAL;
+}
+#endif
+
+static void print_pst_entry(struct pst_s *pst, unsigned int j)
+{
+ pr_debug("PST:%d (@%p)\n", j, pst);
+ pr_debug(" cpuid: 0x%x fsb: %d maxFID: 0x%x startvid: 0x%x\n",
+ pst->cpuid, pst->fsbspeed, pst->maxfid, pst->startvid);
+}
+
+static int powernow_decode_bios(int maxfid, int startvid)
+{
+ struct psb_s *psb;
+ struct pst_s *pst;
+ unsigned int i, j;
+ unsigned char *p;
+ unsigned int etuple;
+ unsigned int ret;
+
+ etuple = cpuid_eax(0x80000001);
+
+ for (i = 0xC0000; i < 0xffff0 ; i += 16) {
+
+ p = phys_to_virt(i);
+
+ if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
+ pr_debug("Found PSB header at %p\n", p);
+ psb = (struct psb_s *) p;
+ pr_debug("Table version: 0x%x\n", psb->tableversion);
+ if (psb->tableversion != 0x12) {
+ printk(KERN_INFO PFX "Sorry, only v1.2 tables"
+ " supported right now\n");
+ return -ENODEV;
+ }
+
+ pr_debug("Flags: 0x%x\n", psb->flags);
+ if ((psb->flags & 1) == 0)
+ pr_debug("Mobile voltage regulator\n");
+ else
+ pr_debug("Desktop voltage regulator\n");
+
+ latency = psb->settlingtime;
+ if (latency < 100) {
+ printk(KERN_INFO PFX "BIOS set settling time "
+ "to %d microseconds. "
+ "Should be at least 100. "
+ "Correcting.\n", latency);
+ latency = 100;
+ }
+ pr_debug("Settling Time: %d microseconds.\n",
+ psb->settlingtime);
+ pr_debug("Has %d PST tables. (Only dumping ones "
+ "relevant to this CPU).\n",
+ psb->numpst);
+
+ p += sizeof(struct psb_s);
+
+ pst = (struct pst_s *) p;
+
+ for (j = 0; j < psb->numpst; j++) {
+ pst = (struct pst_s *) p;
+ number_scales = pst->numpstates;
+
+ if ((etuple == pst->cpuid) &&
+ check_fsb(pst->fsbspeed) &&
+ (maxfid == pst->maxfid) &&
+ (startvid == pst->startvid)) {
+ print_pst_entry(pst, j);
+ p = (char *)pst + sizeof(struct pst_s);
+ ret = get_ranges(p);
+ return ret;
+ } else {
+ unsigned int k;
+ p = (char *)pst + sizeof(struct pst_s);
+ for (k = 0; k < number_scales; k++)
+ p += 2;
+ }
+ }
+ printk(KERN_INFO PFX "No PST tables match this cpuid "
+ "(0x%x)\n", etuple);
+ printk(KERN_INFO PFX "This is indicative of a broken "
+ "BIOS.\n");
+
+ return -EINVAL;
+ }
+ p++;
+ }
+
+ return -ENODEV;
+}
+
+
+static int powernow_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ unsigned int newstate;
+
+ if (cpufreq_frequency_table_target(policy, powernow_table, target_freq,
+ relation, &newstate))
+ return -EINVAL;
+
+ change_speed(newstate);
+
+ return 0;
+}
+
+
+static int powernow_verify(struct cpufreq_policy *policy)
+{
+ return cpufreq_frequency_table_verify(policy, powernow_table);
+}
+
+/*
+ * We use the fact that the bus frequency is somehow
+ * a multiple of 100000/3 khz, then we compute sgtc according
+ * to this multiple.
+ * That way, we match more how AMD thinks all of that work.
+ * We will then get the same kind of behaviour already tested under
+ * the "well-known" other OS.
+ */
+static int __cpuinit fixup_sgtc(void)
+{
+ unsigned int sgtc;
+ unsigned int m;
+
+ m = fsb / 3333;
+ if ((m % 10) >= 5)
+ m += 5;
+
+ m /= 10;
+
+ sgtc = 100 * m * latency;
+ sgtc = sgtc / 3;
+ if (sgtc > 0xfffff) {
+ printk(KERN_WARNING PFX "SGTC too large %d\n", sgtc);
+ sgtc = 0xfffff;
+ }
+ return sgtc;
+}
+
+static unsigned int powernow_get(unsigned int cpu)
+{
+ union msr_fidvidstatus fidvidstatus;
+ unsigned int cfid;
+
+ if (cpu)
+ return 0;
+ rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
+ cfid = fidvidstatus.bits.CFID;
+
+ return fsb * fid_codes[cfid] / 10;
+}
+
+
+static int __cpuinit acer_cpufreq_pst(const struct dmi_system_id *d)
+{
+ printk(KERN_WARNING PFX
+ "%s laptop with broken PST tables in BIOS detected.\n",
+ d->ident);
+ printk(KERN_WARNING PFX
+ "You need to downgrade to 3A21 (09/09/2002), or try a newer "
+ "BIOS than 3A71 (01/20/2003)\n");
+ printk(KERN_WARNING PFX
+ "cpufreq scaling has been disabled as a result of this.\n");
+ return 0;
+}
+
+/*
+ * Some Athlon laptops have really fucked PST tables.
+ * A BIOS update is all that can save them.
+ * Mention this, and disable cpufreq.
+ */
+static struct dmi_system_id __cpuinitdata powernow_dmi_table[] = {
+ {
+ .callback = acer_cpufreq_pst,
+ .ident = "Acer Aspire",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Insyde Software"),
+ DMI_MATCH(DMI_BIOS_VERSION, "3A71"),
+ },
+ },
+ { }
+};
+
+static int __cpuinit powernow_cpu_init(struct cpufreq_policy *policy)
+{
+ union msr_fidvidstatus fidvidstatus;
+ int result;
+
+ if (policy->cpu != 0)
+ return -ENODEV;
+
+ rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
+
+ recalibrate_cpu_khz();
+
+ fsb = (10 * cpu_khz) / fid_codes[fidvidstatus.bits.CFID];
+ if (!fsb) {
+ printk(KERN_WARNING PFX "can not determine bus frequency\n");
+ return -EINVAL;
+ }
+ pr_debug("FSB: %3dMHz\n", fsb/1000);
+
+ if (dmi_check_system(powernow_dmi_table) || acpi_force) {
+ printk(KERN_INFO PFX "PSB/PST known to be broken. "
+ "Trying ACPI instead\n");
+ result = powernow_acpi_init();
+ } else {
+ result = powernow_decode_bios(fidvidstatus.bits.MFID,
+ fidvidstatus.bits.SVID);
+ if (result) {
+ printk(KERN_INFO PFX "Trying ACPI perflib\n");
+ maximum_speed = 0;
+ minimum_speed = -1;
+ latency = 0;
+ result = powernow_acpi_init();
+ if (result) {
+ printk(KERN_INFO PFX
+ "ACPI and legacy methods failed\n");
+ }
+ } else {
+ /* SGTC use the bus clock as timer */
+ latency = fixup_sgtc();
+ printk(KERN_INFO PFX "SGTC: %d\n", latency);
+ }
+ }
+
+ if (result)
+ return result;
+
+ printk(KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d MHz.\n",
+ minimum_speed/1000, maximum_speed/1000);
+
+ policy->cpuinfo.transition_latency =
+ cpufreq_scale(2000000UL, fsb, latency);
+
+ policy->cur = powernow_get(0);
+
+ cpufreq_frequency_table_get_attr(powernow_table, policy->cpu);
+
+ return cpufreq_frequency_table_cpuinfo(policy, powernow_table);
+}
+
+static int powernow_cpu_exit(struct cpufreq_policy *policy)
+{
+ cpufreq_frequency_table_put_attr(policy->cpu);
+
+#ifdef CONFIG_X86_POWERNOW_K7_ACPI
+ if (acpi_processor_perf) {
+ acpi_processor_unregister_performance(acpi_processor_perf, 0);
+ free_cpumask_var(acpi_processor_perf->shared_cpu_map);
+ kfree(acpi_processor_perf);
+ }
+#endif
+
+ kfree(powernow_table);
+ return 0;
+}
+
+static struct freq_attr *powernow_table_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+static struct cpufreq_driver powernow_driver = {
+ .verify = powernow_verify,
+ .target = powernow_target,
+ .get = powernow_get,
+#ifdef CONFIG_X86_POWERNOW_K7_ACPI
+ .bios_limit = acpi_processor_get_bios_limit,
+#endif
+ .init = powernow_cpu_init,
+ .exit = powernow_cpu_exit,
+ .name = "powernow-k7",
+ .owner = THIS_MODULE,
+ .attr = powernow_table_attr,
+};
+
+static int __init powernow_init(void)
+{
+ if (check_powernow() == 0)
+ return -ENODEV;
+ return cpufreq_register_driver(&powernow_driver);
+}
+
+
+static void __exit powernow_exit(void)
+{
+ cpufreq_unregister_driver(&powernow_driver);
+}
+
+module_param(acpi_force, int, 0444);
+MODULE_PARM_DESC(acpi_force, "Force ACPI to be used.");
+
+MODULE_AUTHOR("Dave Jones <davej@redhat.com>");
+MODULE_DESCRIPTION("Powernow driver for AMD K7 processors.");
+MODULE_LICENSE("GPL");
+
+late_initcall(powernow_init);
+module_exit(powernow_exit);
+
diff --git a/drivers/cpufreq/powernow-k7.h b/drivers/cpufreq/powernow-k7.h
new file mode 100644
index 00000000000..35fb4eaf6e1
--- /dev/null
+++ b/drivers/cpufreq/powernow-k7.h
@@ -0,0 +1,43 @@
+/*
+ * (C) 2003 Dave Jones.
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ *
+ * AMD-specific information
+ *
+ */
+
+union msr_fidvidctl {
+ struct {
+ unsigned FID:5, // 4:0
+ reserved1:3, // 7:5
+ VID:5, // 12:8
+ reserved2:3, // 15:13
+ FIDC:1, // 16
+ VIDC:1, // 17
+ reserved3:2, // 19:18
+ FIDCHGRATIO:1, // 20
+ reserved4:11, // 31-21
+ SGTC:20, // 32:51
+ reserved5:12; // 63:52
+ } bits;
+ unsigned long long val;
+};
+
+union msr_fidvidstatus {
+ struct {
+ unsigned CFID:5, // 4:0
+ reserved1:3, // 7:5
+ SFID:5, // 12:8
+ reserved2:3, // 15:13
+ MFID:5, // 20:16
+ reserved3:11, // 31:21
+ CVID:5, // 36:32
+ reserved4:3, // 39:37
+ SVID:5, // 44:40
+ reserved5:3, // 47:45
+ MVID:5, // 52:48
+ reserved6:11; // 63:53
+ } bits;
+ unsigned long long val;
+};
diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c
new file mode 100644
index 00000000000..83479b6fb9a
--- /dev/null
+++ b/drivers/cpufreq/powernow-k8.c
@@ -0,0 +1,1607 @@
+/*
+ * (c) 2003-2010 Advanced Micro Devices, Inc.
+ * Your use of this code is subject to the terms and conditions of the
+ * GNU general public license version 2. See "COPYING" or
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Support : mark.langsdorf@amd.com
+ *
+ * Based on the powernow-k7.c module written by Dave Jones.
+ * (C) 2003 Dave Jones on behalf of SuSE Labs
+ * (C) 2004 Dominik Brodowski <linux@brodo.de>
+ * (C) 2004 Pavel Machek <pavel@ucw.cz>
+ * Licensed under the terms of the GNU GPL License version 2.
+ * Based upon datasheets & sample CPUs kindly provided by AMD.
+ *
+ * Valuable input gratefully received from Dave Jones, Pavel Machek,
+ * Dominik Brodowski, Jacob Shin, and others.
+ * Originally developed by Paul Devriendt.
+ * Processor information obtained from Chapter 9 (Power and Thermal Management)
+ * of the "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD
+ * Opteron Processors" available for download from www.amd.com
+ *
+ * Tables for specific CPUs can be inferred from
+ * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/30430.pdf
+ */
+
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#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>
+
+#include <asm/msr.h>
+
+#include <linux/acpi.h>
+#include <linux/mutex.h>
+#include <acpi/processor.h>
+
+#define PFX "powernow-k8: "
+#define VERSION "version 2.20.00"
+#include "powernow-k8.h"
+#include "mperf.h"
+
+/* serialize freq changes */
+static DEFINE_MUTEX(fidvid_mutex);
+
+static DEFINE_PER_CPU(struct powernow_k8_data *, powernow_data);
+
+static int cpu_family = CPU_OPTERON;
+
+/* core performance boost */
+static bool cpb_capable, cpb_enabled;
+static struct msr __percpu *msrs;
+
+static struct cpufreq_driver cpufreq_amd64_driver;
+
+#ifndef CONFIG_SMP
+static inline const struct cpumask *cpu_core_mask(int cpu)
+{
+ return cpumask_of(0);
+}
+#endif
+
+/* Return a frequency in MHz, given an input fid */
+static u32 find_freq_from_fid(u32 fid)
+{
+ return 800 + (fid * 100);
+}
+
+/* Return a frequency in KHz, given an input fid */
+static u32 find_khz_freq_from_fid(u32 fid)
+{
+ return 1000 * find_freq_from_fid(fid);
+}
+
+static u32 find_khz_freq_from_pstate(struct cpufreq_frequency_table *data,
+ u32 pstate)
+{
+ return data[pstate].frequency;
+}
+
+/* Return the vco fid for an input fid
+ *
+ * Each "low" fid has corresponding "high" fid, and you can get to "low" fids
+ * only from corresponding high fids. This returns "high" fid corresponding to
+ * "low" one.
+ */
+static u32 convert_fid_to_vco_fid(u32 fid)
+{
+ if (fid < HI_FID_TABLE_BOTTOM)
+ return 8 + (2 * fid);
+ else
+ return fid;
+}
+
+/*
+ * Return 1 if the pending bit is set. Unless we just instructed the processor
+ * to transition to a new state, seeing this bit set is really bad news.
+ */
+static int pending_bit_stuck(void)
+{
+ u32 lo, hi;
+
+ if (cpu_family == CPU_HW_PSTATE)
+ return 0;
+
+ rdmsr(MSR_FIDVID_STATUS, lo, hi);
+ return lo & MSR_S_LO_CHANGE_PENDING ? 1 : 0;
+}
+
+/*
+ * Update the global current fid / vid values from the status msr.
+ * Returns 1 on error.
+ */
+static int query_current_values_with_pending_wait(struct powernow_k8_data *data)
+{
+ u32 lo, hi;
+ u32 i = 0;
+
+ if (cpu_family == CPU_HW_PSTATE) {
+ rdmsr(MSR_PSTATE_STATUS, lo, hi);
+ i = lo & HW_PSTATE_MASK;
+ data->currpstate = i;
+
+ /*
+ * a workaround for family 11h erratum 311 might cause
+ * an "out-of-range Pstate if the core is in Pstate-0
+ */
+ if ((boot_cpu_data.x86 == 0x11) && (i >= data->numps))
+ data->currpstate = HW_PSTATE_0;
+
+ return 0;
+ }
+ do {
+ if (i++ > 10000) {
+ pr_debug("detected change pending stuck\n");
+ return 1;
+ }
+ rdmsr(MSR_FIDVID_STATUS, lo, hi);
+ } while (lo & MSR_S_LO_CHANGE_PENDING);
+
+ data->currvid = hi & MSR_S_HI_CURRENT_VID;
+ data->currfid = lo & MSR_S_LO_CURRENT_FID;
+
+ return 0;
+}
+
+/* the isochronous relief time */
+static void count_off_irt(struct powernow_k8_data *data)
+{
+ udelay((1 << data->irt) * 10);
+ return;
+}
+
+/* the voltage stabilization time */
+static void count_off_vst(struct powernow_k8_data *data)
+{
+ udelay(data->vstable * VST_UNITS_20US);
+ return;
+}
+
+/* need to init the control msr to a safe value (for each cpu) */
+static void fidvid_msr_init(void)
+{
+ u32 lo, hi;
+ u8 fid, vid;
+
+ rdmsr(MSR_FIDVID_STATUS, lo, hi);
+ vid = hi & MSR_S_HI_CURRENT_VID;
+ fid = lo & MSR_S_LO_CURRENT_FID;
+ lo = fid | (vid << MSR_C_LO_VID_SHIFT);
+ hi = MSR_C_HI_STP_GNT_BENIGN;
+ pr_debug("cpu%d, init lo 0x%x, hi 0x%x\n", smp_processor_id(), lo, hi);
+ wrmsr(MSR_FIDVID_CTL, lo, hi);
+}
+
+/* write the new fid value along with the other control fields to the msr */
+static int write_new_fid(struct powernow_k8_data *data, u32 fid)
+{
+ u32 lo;
+ u32 savevid = data->currvid;
+ u32 i = 0;
+
+ if ((fid & INVALID_FID_MASK) || (data->currvid & INVALID_VID_MASK)) {
+ printk(KERN_ERR PFX "internal error - overflow on fid write\n");
+ return 1;
+ }
+
+ lo = fid;
+ lo |= (data->currvid << MSR_C_LO_VID_SHIFT);
+ lo |= MSR_C_LO_INIT_FID_VID;
+
+ pr_debug("writing fid 0x%x, lo 0x%x, hi 0x%x\n",
+ fid, lo, data->plllock * PLL_LOCK_CONVERSION);
+
+ do {
+ wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION);
+ if (i++ > 100) {
+ printk(KERN_ERR PFX
+ "Hardware error - pending bit very stuck - "
+ "no further pstate changes possible\n");
+ return 1;
+ }
+ } while (query_current_values_with_pending_wait(data));
+
+ count_off_irt(data);
+
+ if (savevid != data->currvid) {
+ printk(KERN_ERR PFX
+ "vid change on fid trans, old 0x%x, new 0x%x\n",
+ savevid, data->currvid);
+ return 1;
+ }
+
+ if (fid != data->currfid) {
+ printk(KERN_ERR PFX
+ "fid trans failed, fid 0x%x, curr 0x%x\n", fid,
+ data->currfid);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Write a new vid to the hardware */
+static int write_new_vid(struct powernow_k8_data *data, u32 vid)
+{
+ u32 lo;
+ u32 savefid = data->currfid;
+ int i = 0;
+
+ if ((data->currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) {
+ printk(KERN_ERR PFX "internal error - overflow on vid write\n");
+ return 1;
+ }
+
+ lo = data->currfid;
+ lo |= (vid << MSR_C_LO_VID_SHIFT);
+ lo |= MSR_C_LO_INIT_FID_VID;
+
+ pr_debug("writing vid 0x%x, lo 0x%x, hi 0x%x\n",
+ vid, lo, STOP_GRANT_5NS);
+
+ do {
+ wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS);
+ if (i++ > 100) {
+ printk(KERN_ERR PFX "internal error - pending bit "
+ "very stuck - no further pstate "
+ "changes possible\n");
+ return 1;
+ }
+ } while (query_current_values_with_pending_wait(data));
+
+ if (savefid != data->currfid) {
+ printk(KERN_ERR PFX "fid changed on vid trans, old "
+ "0x%x new 0x%x\n",
+ savefid, data->currfid);
+ return 1;
+ }
+
+ if (vid != data->currvid) {
+ printk(KERN_ERR PFX "vid trans failed, vid 0x%x, "
+ "curr 0x%x\n",
+ vid, data->currvid);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Reduce the vid by the max of step or reqvid.
+ * Decreasing vid codes represent increasing voltages:
+ * vid of 0 is 1.550V, vid of 0x1e is 0.800V, vid of VID_OFF is off.
+ */
+static int decrease_vid_code_by_step(struct powernow_k8_data *data,
+ u32 reqvid, u32 step)
+{
+ if ((data->currvid - reqvid) > step)
+ reqvid = data->currvid - step;
+
+ if (write_new_vid(data, reqvid))
+ return 1;
+
+ count_off_vst(data);
+
+ return 0;
+}
+
+/* Change hardware pstate by single MSR write */
+static int transition_pstate(struct powernow_k8_data *data, u32 pstate)
+{
+ wrmsr(MSR_PSTATE_CTRL, pstate, 0);
+ data->currpstate = pstate;
+ return 0;
+}
+
+/* Change Opteron/Athlon64 fid and vid, by the 3 phases. */
+static int transition_fid_vid(struct powernow_k8_data *data,
+ u32 reqfid, u32 reqvid)
+{
+ if (core_voltage_pre_transition(data, reqvid, reqfid))
+ return 1;
+
+ if (core_frequency_transition(data, reqfid))
+ return 1;
+
+ if (core_voltage_post_transition(data, reqvid))
+ return 1;
+
+ if (query_current_values_with_pending_wait(data))
+ return 1;
+
+ if ((reqfid != data->currfid) || (reqvid != data->currvid)) {
+ printk(KERN_ERR PFX "failed (cpu%d): req 0x%x 0x%x, "
+ "curr 0x%x 0x%x\n",
+ smp_processor_id(),
+ reqfid, reqvid, data->currfid, data->currvid);
+ return 1;
+ }
+
+ pr_debug("transitioned (cpu%d): new fid 0x%x, vid 0x%x\n",
+ smp_processor_id(), data->currfid, data->currvid);
+
+ return 0;
+}
+
+/* Phase 1 - core voltage transition ... setup voltage */
+static int core_voltage_pre_transition(struct powernow_k8_data *data,
+ u32 reqvid, u32 reqfid)
+{
+ u32 rvosteps = data->rvo;
+ u32 savefid = data->currfid;
+ u32 maxvid, lo, rvomult = 1;
+
+ pr_debug("ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, "
+ "reqvid 0x%x, rvo 0x%x\n",
+ smp_processor_id(),
+ data->currfid, data->currvid, reqvid, data->rvo);
+
+ if ((savefid < LO_FID_TABLE_TOP) && (reqfid < LO_FID_TABLE_TOP))
+ rvomult = 2;
+ rvosteps *= rvomult;
+ rdmsr(MSR_FIDVID_STATUS, lo, maxvid);
+ maxvid = 0x1f & (maxvid >> 16);
+ pr_debug("ph1 maxvid=0x%x\n", maxvid);
+ if (reqvid < maxvid) /* lower numbers are higher voltages */
+ reqvid = maxvid;
+
+ while (data->currvid > reqvid) {
+ pr_debug("ph1: curr 0x%x, req vid 0x%x\n",
+ data->currvid, reqvid);
+ if (decrease_vid_code_by_step(data, reqvid, data->vidmvs))
+ return 1;
+ }
+
+ while ((rvosteps > 0) &&
+ ((rvomult * data->rvo + data->currvid) > reqvid)) {
+ if (data->currvid == maxvid) {
+ rvosteps = 0;
+ } else {
+ pr_debug("ph1: changing vid for rvo, req 0x%x\n",
+ data->currvid - 1);
+ if (decrease_vid_code_by_step(data, data->currvid-1, 1))
+ return 1;
+ rvosteps--;
+ }
+ }
+
+ if (query_current_values_with_pending_wait(data))
+ return 1;
+
+ if (savefid != data->currfid) {
+ printk(KERN_ERR PFX "ph1 err, currfid changed 0x%x\n",
+ data->currfid);
+ return 1;
+ }
+
+ pr_debug("ph1 complete, currfid 0x%x, currvid 0x%x\n",
+ data->currfid, data->currvid);
+
+ return 0;
+}
+
+/* Phase 2 - core frequency transition */
+static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid)
+{
+ u32 vcoreqfid, vcocurrfid, vcofiddiff;
+ u32 fid_interval, savevid = data->currvid;
+
+ if (data->currfid == reqfid) {
+ printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n",
+ data->currfid);
+ return 0;
+ }
+
+ pr_debug("ph2 (cpu%d): starting, currfid 0x%x, currvid 0x%x, "
+ "reqfid 0x%x\n",
+ smp_processor_id(),
+ data->currfid, data->currvid, reqfid);
+
+ vcoreqfid = convert_fid_to_vco_fid(reqfid);
+ vcocurrfid = convert_fid_to_vco_fid(data->currfid);
+ vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
+ : vcoreqfid - vcocurrfid;
+
+ if ((reqfid <= LO_FID_TABLE_TOP) && (data->currfid <= LO_FID_TABLE_TOP))
+ vcofiddiff = 0;
+
+ while (vcofiddiff > 2) {
+ (data->currfid & 1) ? (fid_interval = 1) : (fid_interval = 2);
+
+ if (reqfid > data->currfid) {
+ if (data->currfid > LO_FID_TABLE_TOP) {
+ if (write_new_fid(data,
+ data->currfid + fid_interval))
+ return 1;
+ } else {
+ if (write_new_fid
+ (data,
+ 2 + convert_fid_to_vco_fid(data->currfid)))
+ return 1;
+ }
+ } else {
+ if (write_new_fid(data, data->currfid - fid_interval))
+ return 1;
+ }
+
+ vcocurrfid = convert_fid_to_vco_fid(data->currfid);
+ vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
+ : vcoreqfid - vcocurrfid;
+ }
+
+ if (write_new_fid(data, reqfid))
+ return 1;
+
+ if (query_current_values_with_pending_wait(data))
+ return 1;
+
+ if (data->currfid != reqfid) {
+ printk(KERN_ERR PFX
+ "ph2: mismatch, failed fid transition, "
+ "curr 0x%x, req 0x%x\n",
+ data->currfid, reqfid);
+ return 1;
+ }
+
+ if (savevid != data->currvid) {
+ printk(KERN_ERR PFX "ph2: vid changed, save 0x%x, curr 0x%x\n",
+ savevid, data->currvid);
+ return 1;
+ }
+
+ pr_debug("ph2 complete, currfid 0x%x, currvid 0x%x\n",
+ data->currfid, data->currvid);
+
+ return 0;
+}
+
+/* Phase 3 - core voltage transition flow ... jump to the final vid. */
+static int core_voltage_post_transition(struct powernow_k8_data *data,
+ u32 reqvid)
+{
+ u32 savefid = data->currfid;
+ u32 savereqvid = reqvid;
+
+ pr_debug("ph3 (cpu%d): starting, currfid 0x%x, currvid 0x%x\n",
+ smp_processor_id(),
+ data->currfid, data->currvid);
+
+ if (reqvid != data->currvid) {
+ if (write_new_vid(data, reqvid))
+ return 1;
+
+ if (savefid != data->currfid) {
+ printk(KERN_ERR PFX
+ "ph3: bad fid change, save 0x%x, curr 0x%x\n",
+ savefid, data->currfid);
+ return 1;
+ }
+
+ if (data->currvid != reqvid) {
+ printk(KERN_ERR PFX
+ "ph3: failed vid transition\n, "
+ "req 0x%x, curr 0x%x",
+ reqvid, data->currvid);
+ return 1;
+ }
+ }
+
+ if (query_current_values_with_pending_wait(data))
+ return 1;
+
+ if (savereqvid != data->currvid) {
+ pr_debug("ph3 failed, currvid 0x%x\n", data->currvid);
+ return 1;
+ }
+
+ if (savefid != data->currfid) {
+ pr_debug("ph3 failed, currfid changed 0x%x\n",
+ data->currfid);
+ return 1;
+ }
+
+ pr_debug("ph3 complete, currfid 0x%x, currvid 0x%x\n",
+ data->currfid, data->currvid);
+
+ return 0;
+}
+
+static void check_supported_cpu(void *_rc)
+{
+ u32 eax, ebx, ecx, edx;
+ int *rc = _rc;
+
+ *rc = -ENODEV;
+
+ if (__this_cpu_read(cpu_info.x86_vendor) != X86_VENDOR_AMD)
+ return;
+
+ eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
+ if (((eax & CPUID_XFAM) != CPUID_XFAM_K8) &&
+ ((eax & CPUID_XFAM) < CPUID_XFAM_10H))
+ return;
+
+ if ((eax & CPUID_XFAM) == CPUID_XFAM_K8) {
+ if (((eax & CPUID_USE_XFAM_XMOD) != CPUID_USE_XFAM_XMOD) ||
+ ((eax & CPUID_XMOD) > CPUID_XMOD_REV_MASK)) {
+ printk(KERN_INFO PFX
+ "Processor cpuid %x not supported\n", eax);
+ return;
+ }
+
+ eax = cpuid_eax(CPUID_GET_MAX_CAPABILITIES);
+ if (eax < CPUID_FREQ_VOLT_CAPABILITIES) {
+ printk(KERN_INFO PFX
+ "No frequency change capabilities detected\n");
+ return;
+ }
+
+ cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
+ if ((edx & P_STATE_TRANSITION_CAPABLE)
+ != P_STATE_TRANSITION_CAPABLE) {
+ printk(KERN_INFO PFX
+ "Power state transitions not supported\n");
+ return;
+ }
+ } else { /* must be a HW Pstate capable processor */
+ cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
+ if ((edx & USE_HW_PSTATE) == USE_HW_PSTATE)
+ cpu_family = CPU_HW_PSTATE;
+ else
+ return;
+ }
+
+ *rc = 0;
+}
+
+static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst,
+ u8 maxvid)
+{
+ unsigned int j;
+ u8 lastfid = 0xff;
+
+ for (j = 0; j < data->numps; j++) {
+ if (pst[j].vid > LEAST_VID) {
+ printk(KERN_ERR FW_BUG PFX "vid %d invalid : 0x%x\n",
+ j, pst[j].vid);
+ return -EINVAL;
+ }
+ if (pst[j].vid < data->rvo) {
+ /* vid + rvo >= 0 */
+ printk(KERN_ERR FW_BUG PFX "0 vid exceeded with pstate"
+ " %d\n", j);
+ return -ENODEV;
+ }
+ if (pst[j].vid < maxvid + data->rvo) {
+ /* vid + rvo >= maxvid */
+ printk(KERN_ERR FW_BUG PFX "maxvid exceeded with pstate"
+ " %d\n", j);
+ return -ENODEV;
+ }
+ if (pst[j].fid > MAX_FID) {
+ printk(KERN_ERR FW_BUG PFX "maxfid exceeded with pstate"
+ " %d\n", j);
+ return -ENODEV;
+ }
+ if (j && (pst[j].fid < HI_FID_TABLE_BOTTOM)) {
+ /* Only first fid is allowed to be in "low" range */
+ printk(KERN_ERR FW_BUG PFX "two low fids - %d : "
+ "0x%x\n", j, pst[j].fid);
+ return -EINVAL;
+ }
+ if (pst[j].fid < lastfid)
+ lastfid = pst[j].fid;
+ }
+ if (lastfid & 1) {
+ printk(KERN_ERR FW_BUG PFX "lastfid invalid\n");
+ return -EINVAL;
+ }
+ if (lastfid > LO_FID_TABLE_TOP)
+ printk(KERN_INFO FW_BUG PFX
+ "first fid not from lo freq table\n");
+
+ return 0;
+}
+
+static void invalidate_entry(struct cpufreq_frequency_table *powernow_table,
+ unsigned int entry)
+{
+ powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID;
+}
+
+static void print_basics(struct powernow_k8_data *data)
+{
+ int j;
+ for (j = 0; j < data->numps; j++) {
+ if (data->powernow_table[j].frequency !=
+ CPUFREQ_ENTRY_INVALID) {
+ if (cpu_family == CPU_HW_PSTATE) {
+ printk(KERN_INFO PFX
+ " %d : pstate %d (%d MHz)\n", j,
+ data->powernow_table[j].index,
+ data->powernow_table[j].frequency/1000);
+ } else {
+ printk(KERN_INFO PFX
+ "fid 0x%x (%d MHz), vid 0x%x\n",
+ data->powernow_table[j].index & 0xff,
+ data->powernow_table[j].frequency/1000,
+ data->powernow_table[j].index >> 8);
+ }
+ }
+ }
+ if (data->batps)
+ printk(KERN_INFO PFX "Only %d pstates on battery\n",
+ data->batps);
+}
+
+static u32 freq_from_fid_did(u32 fid, u32 did)
+{
+ u32 mhz = 0;
+
+ if (boot_cpu_data.x86 == 0x10)
+ mhz = (100 * (fid + 0x10)) >> did;
+ else if (boot_cpu_data.x86 == 0x11)
+ mhz = (100 * (fid + 8)) >> did;
+ else
+ BUG();
+
+ return mhz * 1000;
+}
+
+static int fill_powernow_table(struct powernow_k8_data *data,
+ struct pst_s *pst, u8 maxvid)
+{
+ struct cpufreq_frequency_table *powernow_table;
+ unsigned int j;
+
+ if (data->batps) {
+ /* use ACPI support to get full speed on mains power */
+ printk(KERN_WARNING PFX
+ "Only %d pstates usable (use ACPI driver for full "
+ "range\n", data->batps);
+ data->numps = data->batps;
+ }
+
+ for (j = 1; j < data->numps; j++) {
+ if (pst[j-1].fid >= pst[j].fid) {
+ printk(KERN_ERR PFX "PST out of sequence\n");
+ return -EINVAL;
+ }
+ }
+
+ if (data->numps < 2) {
+ printk(KERN_ERR PFX "no p states to transition\n");
+ return -ENODEV;
+ }
+
+ if (check_pst_table(data, pst, maxvid))
+ return -EINVAL;
+
+ powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table)
+ * (data->numps + 1)), GFP_KERNEL);
+ if (!powernow_table) {
+ printk(KERN_ERR PFX "powernow_table memory alloc failure\n");
+ return -ENOMEM;
+ }
+
+ for (j = 0; j < data->numps; j++) {
+ int freq;
+ powernow_table[j].index = pst[j].fid; /* lower 8 bits */
+ powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */
+ freq = find_khz_freq_from_fid(pst[j].fid);
+ powernow_table[j].frequency = freq;
+ }
+ powernow_table[data->numps].frequency = CPUFREQ_TABLE_END;
+ powernow_table[data->numps].index = 0;
+
+ if (query_current_values_with_pending_wait(data)) {
+ kfree(powernow_table);
+ return -EIO;
+ }
+
+ pr_debug("cfid 0x%x, cvid 0x%x\n", data->currfid, data->currvid);
+ data->powernow_table = powernow_table;
+ if (cpumask_first(cpu_core_mask(data->cpu)) == data->cpu)
+ print_basics(data);
+
+ for (j = 0; j < data->numps; j++)
+ if ((pst[j].fid == data->currfid) &&
+ (pst[j].vid == data->currvid))
+ return 0;
+
+ pr_debug("currfid/vid do not match PST, ignoring\n");
+ return 0;
+}
+
+/* Find and validate the PSB/PST table in BIOS. */
+static int find_psb_table(struct powernow_k8_data *data)
+{
+ struct psb_s *psb;
+ unsigned int i;
+ u32 mvs;
+ u8 maxvid;
+ u32 cpst = 0;
+ u32 thiscpuid;
+
+ for (i = 0xc0000; i < 0xffff0; i += 0x10) {
+ /* Scan BIOS looking for the signature. */
+ /* It can not be at ffff0 - it is too big. */
+
+ psb = phys_to_virt(i);
+ if (memcmp(psb, PSB_ID_STRING, PSB_ID_STRING_LEN) != 0)
+ continue;
+
+ pr_debug("found PSB header at 0x%p\n", psb);
+
+ pr_debug("table vers: 0x%x\n", psb->tableversion);
+ if (psb->tableversion != PSB_VERSION_1_4) {
+ printk(KERN_ERR FW_BUG PFX "PSB table is not v1.4\n");
+ return -ENODEV;
+ }
+
+ pr_debug("flags: 0x%x\n", psb->flags1);
+ if (psb->flags1) {
+ printk(KERN_ERR FW_BUG PFX "unknown flags\n");
+ return -ENODEV;
+ }
+
+ data->vstable = psb->vstable;
+ pr_debug("voltage stabilization time: %d(*20us)\n",
+ data->vstable);
+
+ pr_debug("flags2: 0x%x\n", psb->flags2);
+ data->rvo = psb->flags2 & 3;
+ data->irt = ((psb->flags2) >> 2) & 3;
+ mvs = ((psb->flags2) >> 4) & 3;
+ data->vidmvs = 1 << mvs;
+ data->batps = ((psb->flags2) >> 6) & 3;
+
+ pr_debug("ramp voltage offset: %d\n", data->rvo);
+ pr_debug("isochronous relief time: %d\n", data->irt);
+ pr_debug("maximum voltage step: %d - 0x%x\n", mvs, data->vidmvs);
+
+ pr_debug("numpst: 0x%x\n", psb->num_tables);
+ cpst = psb->num_tables;
+ if ((psb->cpuid == 0x00000fc0) ||
+ (psb->cpuid == 0x00000fe0)) {
+ thiscpuid = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
+ if ((thiscpuid == 0x00000fc0) ||
+ (thiscpuid == 0x00000fe0))
+ cpst = 1;
+ }
+ if (cpst != 1) {
+ printk(KERN_ERR FW_BUG PFX "numpst must be 1\n");
+ return -ENODEV;
+ }
+
+ data->plllock = psb->plllocktime;
+ pr_debug("plllocktime: 0x%x (units 1us)\n", psb->plllocktime);
+ pr_debug("maxfid: 0x%x\n", psb->maxfid);
+ pr_debug("maxvid: 0x%x\n", psb->maxvid);
+ maxvid = psb->maxvid;
+
+ data->numps = psb->numps;
+ pr_debug("numpstates: 0x%x\n", data->numps);
+ return fill_powernow_table(data,
+ (struct pst_s *)(psb+1), maxvid);
+ }
+ /*
+ * If you see this message, complain to BIOS manufacturer. If
+ * he tells you "we do not support Linux" or some similar
+ * nonsense, remember that Windows 2000 uses the same legacy
+ * mechanism that the old Linux PSB driver uses. Tell them it
+ * is broken with Windows 2000.
+ *
+ * The reference to the AMD documentation is chapter 9 in the
+ * BIOS and Kernel Developer's Guide, which is available on
+ * www.amd.com
+ */
+ printk(KERN_ERR FW_BUG PFX "No PSB or ACPI _PSS objects\n");
+ printk(KERN_ERR PFX "Make sure that your BIOS is up to date"
+ " and Cool'N'Quiet support is enabled in BIOS setup\n");
+ return -ENODEV;
+}
+
+static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data,
+ unsigned int index)
+{
+ u64 control;
+
+ if (!data->acpi_data.state_count || (cpu_family == CPU_HW_PSTATE))
+ return;
+
+ control = data->acpi_data.states[index].control;
+ data->irt = (control >> IRT_SHIFT) & IRT_MASK;
+ data->rvo = (control >> RVO_SHIFT) & RVO_MASK;
+ data->exttype = (control >> EXT_TYPE_SHIFT) & EXT_TYPE_MASK;
+ data->plllock = (control >> PLL_L_SHIFT) & PLL_L_MASK;
+ data->vidmvs = 1 << ((control >> MVS_SHIFT) & MVS_MASK);
+ data->vstable = (control >> VST_SHIFT) & VST_MASK;
+}
+
+static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
+{
+ struct cpufreq_frequency_table *powernow_table;
+ int ret_val = -ENODEV;
+ u64 control, status;
+
+ if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) {
+ pr_debug("register performance failed: bad ACPI data\n");
+ return -EIO;
+ }
+
+ /* verify the data contained in the ACPI structures */
+ if (data->acpi_data.state_count <= 1) {
+ pr_debug("No ACPI P-States\n");
+ goto err_out;
+ }
+
+ control = data->acpi_data.control_register.space_id;
+ status = data->acpi_data.status_register.space_id;
+
+ if ((control != ACPI_ADR_SPACE_FIXED_HARDWARE) ||
+ (status != ACPI_ADR_SPACE_FIXED_HARDWARE)) {
+ pr_debug("Invalid control/status registers (%llx - %llx)\n",
+ control, status);
+ goto err_out;
+ }
+
+ /* fill in data->powernow_table */
+ powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table)
+ * (data->acpi_data.state_count + 1)), GFP_KERNEL);
+ if (!powernow_table) {
+ pr_debug("powernow_table memory alloc failure\n");
+ goto err_out;
+ }
+
+ /* fill in data */
+ data->numps = data->acpi_data.state_count;
+ powernow_k8_acpi_pst_values(data, 0);
+
+ if (cpu_family == CPU_HW_PSTATE)
+ ret_val = fill_powernow_table_pstate(data, powernow_table);
+ else
+ ret_val = fill_powernow_table_fidvid(data, powernow_table);
+ if (ret_val)
+ goto err_out_mem;
+
+ powernow_table[data->acpi_data.state_count].frequency =
+ CPUFREQ_TABLE_END;
+ powernow_table[data->acpi_data.state_count].index = 0;
+ data->powernow_table = powernow_table;
+
+ if (cpumask_first(cpu_core_mask(data->cpu)) == data->cpu)
+ print_basics(data);
+
+ /* notify BIOS that we exist */
+ acpi_processor_notify_smm(THIS_MODULE);
+
+ if (!zalloc_cpumask_var(&data->acpi_data.shared_cpu_map, GFP_KERNEL)) {
+ printk(KERN_ERR PFX
+ "unable to alloc powernow_k8_data cpumask\n");
+ ret_val = -ENOMEM;
+ goto err_out_mem;
+ }
+
+ return 0;
+
+err_out_mem:
+ kfree(powernow_table);
+
+err_out:
+ acpi_processor_unregister_performance(&data->acpi_data, data->cpu);
+
+ /* data->acpi_data.state_count informs us at ->exit()
+ * whether ACPI was used */
+ data->acpi_data.state_count = 0;
+
+ return ret_val;
+}
+
+static int fill_powernow_table_pstate(struct powernow_k8_data *data,
+ struct cpufreq_frequency_table *powernow_table)
+{
+ int i;
+ u32 hi = 0, lo = 0;
+ rdmsr(MSR_PSTATE_CUR_LIMIT, lo, hi);
+ data->max_hw_pstate = (lo & HW_PSTATE_MAX_MASK) >> HW_PSTATE_MAX_SHIFT;
+
+ for (i = 0; i < data->acpi_data.state_count; i++) {
+ u32 index;
+
+ index = data->acpi_data.states[i].control & HW_PSTATE_MASK;
+ if (index > data->max_hw_pstate) {
+ printk(KERN_ERR PFX "invalid pstate %d - "
+ "bad value %d.\n", i, index);
+ printk(KERN_ERR PFX "Please report to BIOS "
+ "manufacturer\n");
+ invalidate_entry(powernow_table, i);
+ continue;
+ }
+ rdmsr(MSR_PSTATE_DEF_BASE + index, lo, hi);
+ if (!(hi & HW_PSTATE_VALID_MASK)) {
+ pr_debug("invalid pstate %d, ignoring\n", index);
+ invalidate_entry(powernow_table, i);
+ continue;
+ }
+
+ powernow_table[i].index = index;
+
+ /* Frequency may be rounded for these */
+ if ((boot_cpu_data.x86 == 0x10 && boot_cpu_data.x86_model < 10)
+ || boot_cpu_data.x86 == 0x11) {
+ powernow_table[i].frequency =
+ freq_from_fid_did(lo & 0x3f, (lo >> 6) & 7);
+ } else
+ powernow_table[i].frequency =
+ data->acpi_data.states[i].core_frequency * 1000;
+ }
+ return 0;
+}
+
+static int fill_powernow_table_fidvid(struct powernow_k8_data *data,
+ struct cpufreq_frequency_table *powernow_table)
+{
+ int i;
+
+ for (i = 0; i < data->acpi_data.state_count; i++) {
+ u32 fid;
+ u32 vid;
+ u32 freq, index;
+ u64 status, control;
+
+ if (data->exttype) {
+ status = data->acpi_data.states[i].status;
+ fid = status & EXT_FID_MASK;
+ vid = (status >> VID_SHIFT) & EXT_VID_MASK;
+ } else {
+ control = data->acpi_data.states[i].control;
+ fid = control & FID_MASK;
+ vid = (control >> VID_SHIFT) & VID_MASK;
+ }
+
+ pr_debug(" %d : fid 0x%x, vid 0x%x\n", i, fid, vid);
+
+ index = fid | (vid<<8);
+ powernow_table[i].index = index;
+
+ freq = find_khz_freq_from_fid(fid);
+ powernow_table[i].frequency = freq;
+
+ /* verify frequency is OK */
+ if ((freq > (MAX_FREQ * 1000)) || (freq < (MIN_FREQ * 1000))) {
+ pr_debug("invalid freq %u kHz, ignoring\n", freq);
+ invalidate_entry(powernow_table, i);
+ continue;
+ }
+
+ /* verify voltage is OK -
+ * BIOSs are using "off" to indicate invalid */
+ if (vid == VID_OFF) {
+ pr_debug("invalid vid %u, ignoring\n", vid);
+ invalidate_entry(powernow_table, i);
+ continue;
+ }
+
+ if (freq != (data->acpi_data.states[i].core_frequency * 1000)) {
+ printk(KERN_INFO PFX "invalid freq entries "
+ "%u kHz vs. %u kHz\n", freq,
+ (unsigned int)
+ (data->acpi_data.states[i].core_frequency
+ * 1000));
+ invalidate_entry(powernow_table, i);
+ continue;
+ }
+ }
+ return 0;
+}
+
+static void powernow_k8_cpu_exit_acpi(struct powernow_k8_data *data)
+{
+ if (data->acpi_data.state_count)
+ acpi_processor_unregister_performance(&data->acpi_data,
+ data->cpu);
+ free_cpumask_var(data->acpi_data.shared_cpu_map);
+}
+
+static int get_transition_latency(struct powernow_k8_data *data)
+{
+ int max_latency = 0;
+ int i;
+ for (i = 0; i < data->acpi_data.state_count; i++) {
+ int cur_latency = data->acpi_data.states[i].transition_latency
+ + data->acpi_data.states[i].bus_master_latency;
+ if (cur_latency > max_latency)
+ max_latency = cur_latency;
+ }
+ if (max_latency == 0) {
+ /*
+ * Fam 11h and later may return 0 as transition latency. This
+ * is intended and means "very fast". While cpufreq core and
+ * governors currently can handle that gracefully, better set it
+ * to 1 to avoid problems in the future.
+ */
+ if (boot_cpu_data.x86 < 0x11)
+ printk(KERN_ERR FW_WARN PFX "Invalid zero transition "
+ "latency\n");
+ max_latency = 1;
+ }
+ /* value in usecs, needs to be in nanoseconds */
+ return 1000 * max_latency;
+}
+
+/* Take a frequency, and issue the fid/vid transition command */
+static int transition_frequency_fidvid(struct powernow_k8_data *data,
+ unsigned int index)
+{
+ u32 fid = 0;
+ u32 vid = 0;
+ int res, i;
+ struct cpufreq_freqs freqs;
+
+ pr_debug("cpu %d transition to index %u\n", smp_processor_id(), index);
+
+ /* fid/vid correctness check for k8 */
+ /* fid are the lower 8 bits of the index we stored into
+ * the cpufreq frequency table in find_psb_table, vid
+ * are the upper 8 bits.
+ */
+ fid = data->powernow_table[index].index & 0xFF;
+ vid = (data->powernow_table[index].index & 0xFF00) >> 8;
+
+ pr_debug("table matched fid 0x%x, giving vid 0x%x\n", fid, vid);
+
+ if (query_current_values_with_pending_wait(data))
+ return 1;
+
+ if ((data->currvid == vid) && (data->currfid == fid)) {
+ pr_debug("target matches current values (fid 0x%x, vid 0x%x)\n",
+ fid, vid);
+ return 0;
+ }
+
+ pr_debug("cpu %d, changing to fid 0x%x, vid 0x%x\n",
+ smp_processor_id(), fid, vid);
+ freqs.old = find_khz_freq_from_fid(data->currfid);
+ freqs.new = find_khz_freq_from_fid(fid);
+
+ for_each_cpu(i, data->available_cores) {
+ freqs.cpu = i;
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ }
+
+ res = transition_fid_vid(data, fid, vid);
+ freqs.new = find_khz_freq_from_fid(data->currfid);
+
+ for_each_cpu(i, data->available_cores) {
+ freqs.cpu = i;
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
+ return res;
+}
+
+/* Take a frequency, and issue the hardware pstate transition command */
+static int transition_frequency_pstate(struct powernow_k8_data *data,
+ unsigned int index)
+{
+ u32 pstate = 0;
+ int res, i;
+ struct cpufreq_freqs freqs;
+
+ pr_debug("cpu %d transition to index %u\n", smp_processor_id(), index);
+
+ /* get MSR index for hardware pstate transition */
+ pstate = index & HW_PSTATE_MASK;
+ if (pstate > data->max_hw_pstate)
+ return 0;
+ freqs.old = find_khz_freq_from_pstate(data->powernow_table,
+ data->currpstate);
+ freqs.new = find_khz_freq_from_pstate(data->powernow_table, pstate);
+
+ for_each_cpu(i, data->available_cores) {
+ freqs.cpu = i;
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ }
+
+ res = transition_pstate(data, pstate);
+ freqs.new = find_khz_freq_from_pstate(data->powernow_table, pstate);
+
+ for_each_cpu(i, data->available_cores) {
+ freqs.cpu = i;
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
+ return res;
+}
+
+/* Driver entry point to switch to the target frequency */
+static int powernowk8_target(struct cpufreq_policy *pol,
+ unsigned targfreq, unsigned relation)
+{
+ cpumask_var_t oldmask;
+ struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu);
+ u32 checkfid;
+ u32 checkvid;
+ unsigned int newstate;
+ int ret = -EIO;
+
+ if (!data)
+ return -EINVAL;
+
+ 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;
+ }
+
+ 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;
+
+ if (cpu_family != CPU_HW_PSTATE) {
+ pr_debug("targ: curr fid 0x%x, vid 0x%x\n",
+ data->currfid, data->currvid);
+
+ if ((checkvid != data->currvid) ||
+ (checkfid != data->currfid)) {
+ printk(KERN_INFO PFX
+ "error - out of sync, fix 0x%x 0x%x, "
+ "vid 0x%x 0x%x\n",
+ checkfid, data->currfid,
+ checkvid, data->currvid);
+ }
+ }
+
+ if (cpufreq_frequency_table_target(pol, data->powernow_table,
+ targfreq, relation, &newstate))
+ goto err_out;
+
+ mutex_lock(&fidvid_mutex);
+
+ powernow_k8_acpi_pst_values(data, newstate);
+
+ if (cpu_family == CPU_HW_PSTATE)
+ ret = transition_frequency_pstate(data, newstate);
+ else
+ 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;
+ }
+ mutex_unlock(&fidvid_mutex);
+
+ if (cpu_family == CPU_HW_PSTATE)
+ pol->cur = find_khz_freq_from_pstate(data->powernow_table,
+ newstate);
+ 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;
+}
+
+/* Driver entry point to verify the policy and range of frequencies */
+static int powernowk8_verify(struct cpufreq_policy *pol)
+{
+ struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu);
+
+ if (!data)
+ return -EINVAL;
+
+ return cpufreq_frequency_table_verify(pol, data->powernow_table);
+}
+
+struct init_on_cpu {
+ struct powernow_k8_data *data;
+ int rc;
+};
+
+static void __cpuinit powernowk8_cpu_init_on_cpu(void *_init_on_cpu)
+{
+ struct init_on_cpu *init_on_cpu = _init_on_cpu;
+
+ if (pending_bit_stuck()) {
+ printk(KERN_ERR PFX "failing init, change pending bit set\n");
+ init_on_cpu->rc = -ENODEV;
+ return;
+ }
+
+ if (query_current_values_with_pending_wait(init_on_cpu->data)) {
+ init_on_cpu->rc = -ENODEV;
+ return;
+ }
+
+ if (cpu_family == CPU_OPTERON)
+ fidvid_msr_init();
+
+ init_on_cpu->rc = 0;
+}
+
+/* per CPU init entry point to the driver */
+static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
+{
+ static const char ACPI_PSS_BIOS_BUG_MSG[] =
+ KERN_ERR FW_BUG PFX "No compatible ACPI _PSS objects found.\n"
+ FW_BUG PFX "Try again with latest BIOS.\n";
+ struct powernow_k8_data *data;
+ struct init_on_cpu init_on_cpu;
+ int rc;
+ struct cpuinfo_x86 *c = &cpu_data(pol->cpu);
+
+ if (!cpu_online(pol->cpu))
+ return -ENODEV;
+
+ smp_call_function_single(pol->cpu, check_supported_cpu, &rc, 1);
+ if (rc)
+ return -ENODEV;
+
+ data = kzalloc(sizeof(struct powernow_k8_data), GFP_KERNEL);
+ if (!data) {
+ printk(KERN_ERR PFX "unable to alloc powernow_k8_data");
+ return -ENOMEM;
+ }
+
+ data->cpu = pol->cpu;
+ data->currpstate = HW_PSTATE_INVALID;
+
+ if (powernow_k8_cpu_init_acpi(data)) {
+ /*
+ * Use the PSB BIOS structure. This is only available on
+ * an UP version, and is deprecated by AMD.
+ */
+ if (num_online_cpus() != 1) {
+ printk_once(ACPI_PSS_BIOS_BUG_MSG);
+ goto err_out;
+ }
+ if (pol->cpu != 0) {
+ printk(KERN_ERR FW_BUG PFX "No ACPI _PSS objects for "
+ "CPU other than CPU0. Complain to your BIOS "
+ "vendor.\n");
+ goto err_out;
+ }
+ rc = find_psb_table(data);
+ if (rc)
+ goto err_out;
+
+ /* Take a crude guess here.
+ * That guess was in microseconds, so multiply with 1000 */
+ pol->cpuinfo.transition_latency = (
+ ((data->rvo + 8) * data->vstable * VST_UNITS_20US) +
+ ((1 << data->irt) * 30)) * 1000;
+ } else /* ACPI _PSS objects available */
+ pol->cpuinfo.transition_latency = get_transition_latency(data);
+
+ /* only run on specific CPU from here on */
+ init_on_cpu.data = data;
+ smp_call_function_single(data->cpu, powernowk8_cpu_init_on_cpu,
+ &init_on_cpu, 1);
+ rc = init_on_cpu.rc;
+ if (rc != 0)
+ goto err_out_exit_acpi;
+
+ if (cpu_family == CPU_HW_PSTATE)
+ cpumask_copy(pol->cpus, cpumask_of(pol->cpu));
+ else
+ cpumask_copy(pol->cpus, cpu_core_mask(pol->cpu));
+ data->available_cores = pol->cpus;
+
+ if (cpu_family == CPU_HW_PSTATE)
+ pol->cur = find_khz_freq_from_pstate(data->powernow_table,
+ data->currpstate);
+ else
+ pol->cur = find_khz_freq_from_fid(data->currfid);
+ pr_debug("policy current frequency %d kHz\n", pol->cur);
+
+ /* min/max the cpu is capable of */
+ if (cpufreq_frequency_table_cpuinfo(pol, data->powernow_table)) {
+ printk(KERN_ERR FW_BUG PFX "invalid powernow_table\n");
+ powernow_k8_cpu_exit_acpi(data);
+ kfree(data->powernow_table);
+ kfree(data);
+ return -EINVAL;
+ }
+
+ /* Check for APERF/MPERF support in hardware */
+ if (cpu_has(c, X86_FEATURE_APERFMPERF))
+ cpufreq_amd64_driver.getavg = cpufreq_get_measured_perf;
+
+ cpufreq_frequency_table_get_attr(data->powernow_table, pol->cpu);
+
+ if (cpu_family == CPU_HW_PSTATE)
+ pr_debug("cpu_init done, current pstate 0x%x\n",
+ data->currpstate);
+ else
+ pr_debug("cpu_init done, current fid 0x%x, vid 0x%x\n",
+ data->currfid, data->currvid);
+
+ per_cpu(powernow_data, pol->cpu) = data;
+
+ return 0;
+
+err_out_exit_acpi:
+ powernow_k8_cpu_exit_acpi(data);
+
+err_out:
+ kfree(data);
+ return -ENODEV;
+}
+
+static int __devexit powernowk8_cpu_exit(struct cpufreq_policy *pol)
+{
+ struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu);
+
+ if (!data)
+ return -EINVAL;
+
+ powernow_k8_cpu_exit_acpi(data);
+
+ cpufreq_frequency_table_put_attr(pol->cpu);
+
+ kfree(data->powernow_table);
+ kfree(data);
+ per_cpu(powernow_data, pol->cpu) = NULL;
+
+ return 0;
+}
+
+static void query_values_on_cpu(void *_err)
+{
+ int *err = _err;
+ struct powernow_k8_data *data = __this_cpu_read(powernow_data);
+
+ *err = query_current_values_with_pending_wait(data);
+}
+
+static unsigned int powernowk8_get(unsigned int cpu)
+{
+ struct powernow_k8_data *data = per_cpu(powernow_data, cpu);
+ unsigned int khz = 0;
+ int err;
+
+ if (!data)
+ return 0;
+
+ smp_call_function_single(cpu, query_values_on_cpu, &err, true);
+ if (err)
+ goto out;
+
+ if (cpu_family == CPU_HW_PSTATE)
+ khz = find_khz_freq_from_pstate(data->powernow_table,
+ data->currpstate);
+ else
+ khz = find_khz_freq_from_fid(data->currfid);
+
+
+out:
+ return khz;
+}
+
+static void _cpb_toggle_msrs(bool t)
+{
+ int cpu;
+
+ get_online_cpus();
+
+ rdmsr_on_cpus(cpu_online_mask, MSR_K7_HWCR, msrs);
+
+ for_each_cpu(cpu, cpu_online_mask) {
+ struct msr *reg = per_cpu_ptr(msrs, cpu);
+ if (t)
+ reg->l &= ~BIT(25);
+ else
+ reg->l |= BIT(25);
+ }
+ wrmsr_on_cpus(cpu_online_mask, MSR_K7_HWCR, msrs);
+
+ put_online_cpus();
+}
+
+/*
+ * Switch on/off core performance boosting.
+ *
+ * 0=disable
+ * 1=enable.
+ */
+static void cpb_toggle(bool t)
+{
+ if (!cpb_capable)
+ return;
+
+ if (t && !cpb_enabled) {
+ cpb_enabled = true;
+ _cpb_toggle_msrs(t);
+ printk(KERN_INFO PFX "Core Boosting enabled.\n");
+ } else if (!t && cpb_enabled) {
+ cpb_enabled = false;
+ _cpb_toggle_msrs(t);
+ printk(KERN_INFO PFX "Core Boosting disabled.\n");
+ }
+}
+
+static ssize_t store_cpb(struct cpufreq_policy *policy, const char *buf,
+ size_t count)
+{
+ int ret = -EINVAL;
+ unsigned long val = 0;
+
+ ret = strict_strtoul(buf, 10, &val);
+ if (!ret && (val == 0 || val == 1) && cpb_capable)
+ cpb_toggle(val);
+ else
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t show_cpb(struct cpufreq_policy *policy, char *buf)
+{
+ return sprintf(buf, "%u\n", cpb_enabled);
+}
+
+#define define_one_rw(_name) \
+static struct freq_attr _name = \
+__ATTR(_name, 0644, show_##_name, store_##_name)
+
+define_one_rw(cpb);
+
+static struct freq_attr *powernow_k8_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ &cpb,
+ NULL,
+};
+
+static struct cpufreq_driver cpufreq_amd64_driver = {
+ .verify = powernowk8_verify,
+ .target = powernowk8_target,
+ .bios_limit = acpi_processor_get_bios_limit,
+ .init = powernowk8_cpu_init,
+ .exit = __devexit_p(powernowk8_cpu_exit),
+ .get = powernowk8_get,
+ .name = "powernow-k8",
+ .owner = THIS_MODULE,
+ .attr = powernow_k8_attr,
+};
+
+/*
+ * Clear the boost-disable flag on the CPU_DOWN path so that this cpu
+ * cannot block the remaining ones from boosting. On the CPU_UP path we
+ * simply keep the boost-disable flag in sync with the current global
+ * state.
+ */
+static int cpb_notify(struct notifier_block *nb, unsigned long action,
+ void *hcpu)
+{
+ unsigned cpu = (long)hcpu;
+ u32 lo, hi;
+
+ switch (action) {
+ case CPU_UP_PREPARE:
+ case CPU_UP_PREPARE_FROZEN:
+
+ if (!cpb_enabled) {
+ rdmsr_on_cpu(cpu, MSR_K7_HWCR, &lo, &hi);
+ lo |= BIT(25);
+ wrmsr_on_cpu(cpu, MSR_K7_HWCR, lo, hi);
+ }
+ break;
+
+ case CPU_DOWN_PREPARE:
+ case CPU_DOWN_PREPARE_FROZEN:
+ rdmsr_on_cpu(cpu, MSR_K7_HWCR, &lo, &hi);
+ lo &= ~BIT(25);
+ wrmsr_on_cpu(cpu, MSR_K7_HWCR, lo, hi);
+ break;
+
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block cpb_nb = {
+ .notifier_call = cpb_notify,
+};
+
+/* driver entry point for init */
+static int __cpuinit powernowk8_init(void)
+{
+ unsigned int i, supported_cpus = 0, cpu;
+ int rv;
+
+ for_each_online_cpu(i) {
+ int rc;
+ smp_call_function_single(i, check_supported_cpu, &rc, 1);
+ if (rc == 0)
+ supported_cpus++;
+ }
+
+ if (supported_cpus != num_online_cpus())
+ return -ENODEV;
+
+ printk(KERN_INFO PFX "Found %d %s (%d cpu cores) (" VERSION ")\n",
+ num_online_nodes(), boot_cpu_data.x86_model_id, supported_cpus);
+
+ if (boot_cpu_has(X86_FEATURE_CPB)) {
+
+ cpb_capable = true;
+
+ msrs = msrs_alloc();
+ if (!msrs) {
+ printk(KERN_ERR "%s: Error allocating msrs!\n", __func__);
+ return -ENOMEM;
+ }
+
+ register_cpu_notifier(&cpb_nb);
+
+ rdmsr_on_cpus(cpu_online_mask, MSR_K7_HWCR, msrs);
+
+ for_each_cpu(cpu, cpu_online_mask) {
+ struct msr *reg = per_cpu_ptr(msrs, cpu);
+ cpb_enabled |= !(!!(reg->l & BIT(25)));
+ }
+
+ printk(KERN_INFO PFX "Core Performance Boosting: %s.\n",
+ (cpb_enabled ? "on" : "off"));
+ }
+
+ rv = cpufreq_register_driver(&cpufreq_amd64_driver);
+ if (rv < 0 && boot_cpu_has(X86_FEATURE_CPB)) {
+ unregister_cpu_notifier(&cpb_nb);
+ msrs_free(msrs);
+ msrs = NULL;
+ }
+ return rv;
+}
+
+/* driver entry point for term */
+static void __exit powernowk8_exit(void)
+{
+ pr_debug("exit\n");
+
+ if (boot_cpu_has(X86_FEATURE_CPB)) {
+ msrs_free(msrs);
+ msrs = NULL;
+
+ unregister_cpu_notifier(&cpb_nb);
+ }
+
+ cpufreq_unregister_driver(&cpufreq_amd64_driver);
+}
+
+MODULE_AUTHOR("Paul Devriendt <paul.devriendt@amd.com> and "
+ "Mark Langsdorf <mark.langsdorf@amd.com>");
+MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver.");
+MODULE_LICENSE("GPL");
+
+late_initcall(powernowk8_init);
+module_exit(powernowk8_exit);
diff --git a/drivers/cpufreq/powernow-k8.h b/drivers/cpufreq/powernow-k8.h
new file mode 100644
index 00000000000..3744d26cdc2
--- /dev/null
+++ b/drivers/cpufreq/powernow-k8.h
@@ -0,0 +1,222 @@
+/*
+ * (c) 2003-2006 Advanced Micro Devices, Inc.
+ * Your use of this code is subject to the terms and conditions of the
+ * GNU general public license version 2. See "COPYING" or
+ * http://www.gnu.org/licenses/gpl.html
+ */
+
+enum pstate {
+ HW_PSTATE_INVALID = 0xff,
+ HW_PSTATE_0 = 0,
+ HW_PSTATE_1 = 1,
+ HW_PSTATE_2 = 2,
+ HW_PSTATE_3 = 3,
+ HW_PSTATE_4 = 4,
+ HW_PSTATE_5 = 5,
+ HW_PSTATE_6 = 6,
+ HW_PSTATE_7 = 7,
+};
+
+struct powernow_k8_data {
+ unsigned int cpu;
+
+ u32 numps; /* number of p-states */
+ u32 batps; /* number of p-states supported on battery */
+ u32 max_hw_pstate; /* maximum legal hardware pstate */
+
+ /* these values are constant when the PSB is used to determine
+ * vid/fid pairings, but are modified during the ->target() call
+ * when ACPI is used */
+ u32 rvo; /* ramp voltage offset */
+ u32 irt; /* isochronous relief time */
+ u32 vidmvs; /* usable value calculated from mvs */
+ u32 vstable; /* voltage stabilization time, units 20 us */
+ u32 plllock; /* pll lock time, units 1 us */
+ u32 exttype; /* extended interface = 1 */
+
+ /* keep track of the current fid / vid or pstate */
+ u32 currvid;
+ u32 currfid;
+ enum pstate currpstate;
+
+ /* the powernow_table includes all frequency and vid/fid pairings:
+ * fid are the lower 8 bits of the index, vid are the upper 8 bits.
+ * frequency is in kHz */
+ struct cpufreq_frequency_table *powernow_table;
+
+ /* the acpi table needs to be kept. it's only available if ACPI was
+ * used to determine valid frequency/vid/fid states */
+ struct acpi_processor_performance acpi_data;
+
+ /* we need to keep track of associated cores, but let cpufreq
+ * handle hotplug events - so just point at cpufreq pol->cpus
+ * structure */
+ struct cpumask *available_cores;
+};
+
+/* processor's cpuid instruction support */
+#define CPUID_PROCESSOR_SIGNATURE 1 /* function 1 */
+#define CPUID_XFAM 0x0ff00000 /* extended family */
+#define CPUID_XFAM_K8 0
+#define CPUID_XMOD 0x000f0000 /* extended model */
+#define CPUID_XMOD_REV_MASK 0x000c0000
+#define CPUID_XFAM_10H 0x00100000 /* family 0x10 */
+#define CPUID_USE_XFAM_XMOD 0x00000f00
+#define CPUID_GET_MAX_CAPABILITIES 0x80000000
+#define CPUID_FREQ_VOLT_CAPABILITIES 0x80000007
+#define P_STATE_TRANSITION_CAPABLE 6
+
+/* Model Specific Registers for p-state transitions. MSRs are 64-bit. For */
+/* writes (wrmsr - opcode 0f 30), the register number is placed in ecx, and */
+/* the value to write is placed in edx:eax. For reads (rdmsr - opcode 0f 32), */
+/* the register number is placed in ecx, and the data is returned in edx:eax. */
+
+#define MSR_FIDVID_CTL 0xc0010041
+#define MSR_FIDVID_STATUS 0xc0010042
+
+/* Field definitions within the FID VID Low Control MSR : */
+#define MSR_C_LO_INIT_FID_VID 0x00010000
+#define MSR_C_LO_NEW_VID 0x00003f00
+#define MSR_C_LO_NEW_FID 0x0000003f
+#define MSR_C_LO_VID_SHIFT 8
+
+/* Field definitions within the FID VID High Control MSR : */
+#define MSR_C_HI_STP_GNT_TO 0x000fffff
+
+/* Field definitions within the FID VID Low Status MSR : */
+#define MSR_S_LO_CHANGE_PENDING 0x80000000 /* cleared when completed */
+#define MSR_S_LO_MAX_RAMP_VID 0x3f000000
+#define MSR_S_LO_MAX_FID 0x003f0000
+#define MSR_S_LO_START_FID 0x00003f00
+#define MSR_S_LO_CURRENT_FID 0x0000003f
+
+/* Field definitions within the FID VID High Status MSR : */
+#define MSR_S_HI_MIN_WORKING_VID 0x3f000000
+#define MSR_S_HI_MAX_WORKING_VID 0x003f0000
+#define MSR_S_HI_START_VID 0x00003f00
+#define MSR_S_HI_CURRENT_VID 0x0000003f
+#define MSR_C_HI_STP_GNT_BENIGN 0x00000001
+
+
+/* Hardware Pstate _PSS and MSR definitions */
+#define USE_HW_PSTATE 0x00000080
+#define HW_PSTATE_MASK 0x00000007
+#define HW_PSTATE_VALID_MASK 0x80000000
+#define HW_PSTATE_MAX_MASK 0x000000f0
+#define HW_PSTATE_MAX_SHIFT 4
+#define MSR_PSTATE_DEF_BASE 0xc0010064 /* base of Pstate MSRs */
+#define MSR_PSTATE_STATUS 0xc0010063 /* Pstate Status MSR */
+#define MSR_PSTATE_CTRL 0xc0010062 /* Pstate control MSR */
+#define MSR_PSTATE_CUR_LIMIT 0xc0010061 /* pstate current limit MSR */
+
+/* define the two driver architectures */
+#define CPU_OPTERON 0
+#define CPU_HW_PSTATE 1
+
+
+/*
+ * There are restrictions frequencies have to follow:
+ * - only 1 entry in the low fid table ( <=1.4GHz )
+ * - lowest entry in the high fid table must be >= 2 * the entry in the
+ * low fid table
+ * - lowest entry in the high fid table must be a <= 200MHz + 2 * the entry
+ * in the low fid table
+ * - the parts can only step at <= 200 MHz intervals, odd fid values are
+ * supported in revision G and later revisions.
+ * - lowest frequency must be >= interprocessor hypertransport link speed
+ * (only applies to MP systems obviously)
+ */
+
+/* fids (frequency identifiers) are arranged in 2 tables - lo and hi */
+#define LO_FID_TABLE_TOP 7 /* fid values marking the boundary */
+#define HI_FID_TABLE_BOTTOM 8 /* between the low and high tables */
+
+#define LO_VCOFREQ_TABLE_TOP 1400 /* corresponding vco frequency values */
+#define HI_VCOFREQ_TABLE_BOTTOM 1600
+
+#define MIN_FREQ_RESOLUTION 200 /* fids jump by 2 matching freq jumps by 200 */
+
+#define MAX_FID 0x2a /* Spec only gives FID values as far as 5 GHz */
+#define LEAST_VID 0x3e /* Lowest (numerically highest) useful vid value */
+
+#define MIN_FREQ 800 /* Min and max freqs, per spec */
+#define MAX_FREQ 5000
+
+#define INVALID_FID_MASK 0xffffffc0 /* not a valid fid if these bits are set */
+#define INVALID_VID_MASK 0xffffffc0 /* not a valid vid if these bits are set */
+
+#define VID_OFF 0x3f
+
+#define STOP_GRANT_5NS 1 /* min poss memory access latency for voltage change */
+
+#define PLL_LOCK_CONVERSION (1000/5) /* ms to ns, then divide by clock period */
+
+#define MAXIMUM_VID_STEPS 1 /* Current cpus only allow a single step of 25mV */
+#define VST_UNITS_20US 20 /* Voltage Stabilization Time is in units of 20us */
+
+/*
+ * Most values of interest are encoded in a single field of the _PSS
+ * entries: the "control" value.
+ */
+
+#define IRT_SHIFT 30
+#define RVO_SHIFT 28
+#define EXT_TYPE_SHIFT 27
+#define PLL_L_SHIFT 20
+#define MVS_SHIFT 18
+#define VST_SHIFT 11
+#define VID_SHIFT 6
+#define IRT_MASK 3
+#define RVO_MASK 3
+#define EXT_TYPE_MASK 1
+#define PLL_L_MASK 0x7f
+#define MVS_MASK 3
+#define VST_MASK 0x7f
+#define VID_MASK 0x1f
+#define FID_MASK 0x1f
+#define EXT_VID_MASK 0x3f
+#define EXT_FID_MASK 0x3f
+
+
+/*
+ * Version 1.4 of the PSB table. This table is constructed by BIOS and is
+ * to tell the OS's power management driver which VIDs and FIDs are
+ * supported by this particular processor.
+ * If the data in the PSB / PST is wrong, then this driver will program the
+ * wrong values into hardware, which is very likely to lead to a crash.
+ */
+
+#define PSB_ID_STRING "AMDK7PNOW!"
+#define PSB_ID_STRING_LEN 10
+
+#define PSB_VERSION_1_4 0x14
+
+struct psb_s {
+ u8 signature[10];
+ u8 tableversion;
+ u8 flags1;
+ u16 vstable;
+ u8 flags2;
+ u8 num_tables;
+ u32 cpuid;
+ u8 plllocktime;
+ u8 maxfid;
+ u8 maxvid;
+ u8 numps;
+};
+
+/* Pairs of fid/vid values are appended to the version 1.4 PSB table. */
+struct pst_s {
+ u8 fid;
+ u8 vid;
+};
+
+static int core_voltage_pre_transition(struct powernow_k8_data *data,
+ u32 reqvid, u32 regfid);
+static int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid);
+static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid);
+
+static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index);
+
+static int fill_powernow_table_pstate(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table);
+static int fill_powernow_table_fidvid(struct powernow_k8_data *data, struct cpufreq_frequency_table *powernow_table);
diff --git a/drivers/cpufreq/sc520_freq.c b/drivers/cpufreq/sc520_freq.c
new file mode 100644
index 00000000000..1e205e6b172
--- /dev/null
+++ b/drivers/cpufreq/sc520_freq.c
@@ -0,0 +1,192 @@
+/*
+ * sc520_freq.c: cpufreq driver for the AMD Elan sc520
+ *
+ * Copyright (C) 2005 Sean Young <sean@mess.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Based on elanfreq.c
+ *
+ * 2005-03-30: - initial revision
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/delay.h>
+#include <linux/cpufreq.h>
+#include <linux/timex.h>
+#include <linux/io.h>
+
+#include <asm/msr.h>
+
+#define MMCR_BASE 0xfffef000 /* The default base address */
+#define OFFS_CPUCTL 0x2 /* CPU Control Register */
+
+static __u8 __iomem *cpuctl;
+
+#define PFX "sc520_freq: "
+
+static struct cpufreq_frequency_table sc520_freq_table[] = {
+ {0x01, 100000},
+ {0x02, 133000},
+ {0, CPUFREQ_TABLE_END},
+};
+
+static unsigned int sc520_freq_get_cpu_frequency(unsigned int cpu)
+{
+ u8 clockspeed_reg = *cpuctl;
+
+ switch (clockspeed_reg & 0x03) {
+ default:
+ printk(KERN_ERR PFX "error: cpuctl register has unexpected "
+ "value %02x\n", clockspeed_reg);
+ case 0x01:
+ return 100000;
+ case 0x02:
+ return 133000;
+ }
+}
+
+static void sc520_freq_set_cpu_state(unsigned int state)
+{
+
+ struct cpufreq_freqs freqs;
+ u8 clockspeed_reg;
+
+ freqs.old = sc520_freq_get_cpu_frequency(0);
+ freqs.new = sc520_freq_table[state].frequency;
+ freqs.cpu = 0; /* AMD Elan is UP */
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ pr_debug("attempting to set frequency to %i kHz\n",
+ sc520_freq_table[state].frequency);
+
+ local_irq_disable();
+
+ clockspeed_reg = *cpuctl & ~0x03;
+ *cpuctl = clockspeed_reg | sc520_freq_table[state].index;
+
+ local_irq_enable();
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+};
+
+static int sc520_freq_verify(struct cpufreq_policy *policy)
+{
+ return cpufreq_frequency_table_verify(policy, &sc520_freq_table[0]);
+}
+
+static int sc520_freq_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ unsigned int newstate = 0;
+
+ if (cpufreq_frequency_table_target(policy, sc520_freq_table,
+ target_freq, relation, &newstate))
+ return -EINVAL;
+
+ sc520_freq_set_cpu_state(newstate);
+
+ return 0;
+}
+
+
+/*
+ * Module init and exit code
+ */
+
+static int sc520_freq_cpu_init(struct cpufreq_policy *policy)
+{
+ struct cpuinfo_x86 *c = &cpu_data(0);
+ int result;
+
+ /* capability check */
+ if (c->x86_vendor != X86_VENDOR_AMD ||
+ c->x86 != 4 || c->x86_model != 9)
+ return -ENODEV;
+
+ /* cpuinfo and default policy values */
+ policy->cpuinfo.transition_latency = 1000000; /* 1ms */
+ policy->cur = sc520_freq_get_cpu_frequency(0);
+
+ result = cpufreq_frequency_table_cpuinfo(policy, sc520_freq_table);
+ if (result)
+ return result;
+
+ cpufreq_frequency_table_get_attr(sc520_freq_table, policy->cpu);
+
+ return 0;
+}
+
+
+static int sc520_freq_cpu_exit(struct cpufreq_policy *policy)
+{
+ cpufreq_frequency_table_put_attr(policy->cpu);
+ return 0;
+}
+
+
+static struct freq_attr *sc520_freq_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+
+static struct cpufreq_driver sc520_freq_driver = {
+ .get = sc520_freq_get_cpu_frequency,
+ .verify = sc520_freq_verify,
+ .target = sc520_freq_target,
+ .init = sc520_freq_cpu_init,
+ .exit = sc520_freq_cpu_exit,
+ .name = "sc520_freq",
+ .owner = THIS_MODULE,
+ .attr = sc520_freq_attr,
+};
+
+
+static int __init sc520_freq_init(void)
+{
+ struct cpuinfo_x86 *c = &cpu_data(0);
+ int err;
+
+ /* Test if we have the right hardware */
+ if (c->x86_vendor != X86_VENDOR_AMD ||
+ c->x86 != 4 || c->x86_model != 9) {
+ pr_debug("no Elan SC520 processor found!\n");
+ return -ENODEV;
+ }
+ cpuctl = ioremap((unsigned long)(MMCR_BASE + OFFS_CPUCTL), 1);
+ if (!cpuctl) {
+ printk(KERN_ERR "sc520_freq: error: failed to remap memory\n");
+ return -ENOMEM;
+ }
+
+ err = cpufreq_register_driver(&sc520_freq_driver);
+ if (err)
+ iounmap(cpuctl);
+
+ return err;
+}
+
+
+static void __exit sc520_freq_exit(void)
+{
+ cpufreq_unregister_driver(&sc520_freq_driver);
+ iounmap(cpuctl);
+}
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_DESCRIPTION("cpufreq driver for AMD's Elan sc520 CPU");
+
+module_init(sc520_freq_init);
+module_exit(sc520_freq_exit);
+
diff --git a/drivers/cpufreq/speedstep-centrino.c b/drivers/cpufreq/speedstep-centrino.c
new file mode 100644
index 00000000000..6ea3455def2
--- /dev/null
+++ b/drivers/cpufreq/speedstep-centrino.c
@@ -0,0 +1,633 @@
+/*
+ * cpufreq driver for Enhanced SpeedStep, as found in Intel's Pentium
+ * M (part of the Centrino chipset).
+ *
+ * Since the original Pentium M, most new Intel CPUs support Enhanced
+ * SpeedStep.
+ *
+ * Despite the "SpeedStep" in the name, this is almost entirely unlike
+ * traditional SpeedStep.
+ *
+ * Modelled on speedstep.c
+ *
+ * Copyright (C) 2003 Jeremy Fitzhardinge <jeremy@goop.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/sched.h> /* current */
+#include <linux/delay.h>
+#include <linux/compiler.h>
+#include <linux/gfp.h>
+
+#include <asm/msr.h>
+#include <asm/processor.h>
+#include <asm/cpufeature.h>
+
+#define PFX "speedstep-centrino: "
+#define MAINTAINER "cpufreq@vger.kernel.org"
+
+#define INTEL_MSR_RANGE (0xffff)
+
+struct cpu_id
+{
+ __u8 x86; /* CPU family */
+ __u8 x86_model; /* model */
+ __u8 x86_mask; /* stepping */
+};
+
+enum {
+ CPU_BANIAS,
+ CPU_DOTHAN_A1,
+ CPU_DOTHAN_A2,
+ CPU_DOTHAN_B0,
+ CPU_MP4HT_D0,
+ CPU_MP4HT_E0,
+};
+
+static const struct cpu_id cpu_ids[] = {
+ [CPU_BANIAS] = { 6, 9, 5 },
+ [CPU_DOTHAN_A1] = { 6, 13, 1 },
+ [CPU_DOTHAN_A2] = { 6, 13, 2 },
+ [CPU_DOTHAN_B0] = { 6, 13, 6 },
+ [CPU_MP4HT_D0] = {15, 3, 4 },
+ [CPU_MP4HT_E0] = {15, 4, 1 },
+};
+#define N_IDS ARRAY_SIZE(cpu_ids)
+
+struct cpu_model
+{
+ const struct cpu_id *cpu_id;
+ const char *model_name;
+ unsigned max_freq; /* max clock in kHz */
+
+ struct cpufreq_frequency_table *op_points; /* clock/voltage pairs */
+};
+static int centrino_verify_cpu_id(const struct cpuinfo_x86 *c,
+ const struct cpu_id *x);
+
+/* Operating points for current CPU */
+static DEFINE_PER_CPU(struct cpu_model *, centrino_model);
+static DEFINE_PER_CPU(const struct cpu_id *, centrino_cpu);
+
+static struct cpufreq_driver centrino_driver;
+
+#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE
+
+/* Computes the correct form for IA32_PERF_CTL MSR for a particular
+ frequency/voltage operating point; frequency in MHz, volts in mV.
+ This is stored as "index" in the structure. */
+#define OP(mhz, mv) \
+ { \
+ .frequency = (mhz) * 1000, \
+ .index = (((mhz)/100) << 8) | ((mv - 700) / 16) \
+ }
+
+/*
+ * These voltage tables were derived from the Intel Pentium M
+ * datasheet, document 25261202.pdf, Table 5. I have verified they
+ * are consistent with my IBM ThinkPad X31, which has a 1.3GHz Pentium
+ * M.
+ */
+
+/* Ultra Low Voltage Intel Pentium M processor 900MHz (Banias) */
+static struct cpufreq_frequency_table banias_900[] =
+{
+ OP(600, 844),
+ OP(800, 988),
+ OP(900, 1004),
+ { .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Ultra Low Voltage Intel Pentium M processor 1000MHz (Banias) */
+static struct cpufreq_frequency_table banias_1000[] =
+{
+ OP(600, 844),
+ OP(800, 972),
+ OP(900, 988),
+ OP(1000, 1004),
+ { .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Low Voltage Intel Pentium M processor 1.10GHz (Banias) */
+static struct cpufreq_frequency_table banias_1100[] =
+{
+ OP( 600, 956),
+ OP( 800, 1020),
+ OP( 900, 1100),
+ OP(1000, 1164),
+ OP(1100, 1180),
+ { .frequency = CPUFREQ_TABLE_END }
+};
+
+
+/* Low Voltage Intel Pentium M processor 1.20GHz (Banias) */
+static struct cpufreq_frequency_table banias_1200[] =
+{
+ OP( 600, 956),
+ OP( 800, 1004),
+ OP( 900, 1020),
+ OP(1000, 1100),
+ OP(1100, 1164),
+ OP(1200, 1180),
+ { .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Intel Pentium M processor 1.30GHz (Banias) */
+static struct cpufreq_frequency_table banias_1300[] =
+{
+ OP( 600, 956),
+ OP( 800, 1260),
+ OP(1000, 1292),
+ OP(1200, 1356),
+ OP(1300, 1388),
+ { .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Intel Pentium M processor 1.40GHz (Banias) */
+static struct cpufreq_frequency_table banias_1400[] =
+{
+ OP( 600, 956),
+ OP( 800, 1180),
+ OP(1000, 1308),
+ OP(1200, 1436),
+ OP(1400, 1484),
+ { .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Intel Pentium M processor 1.50GHz (Banias) */
+static struct cpufreq_frequency_table banias_1500[] =
+{
+ OP( 600, 956),
+ OP( 800, 1116),
+ OP(1000, 1228),
+ OP(1200, 1356),
+ OP(1400, 1452),
+ OP(1500, 1484),
+ { .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Intel Pentium M processor 1.60GHz (Banias) */
+static struct cpufreq_frequency_table banias_1600[] =
+{
+ OP( 600, 956),
+ OP( 800, 1036),
+ OP(1000, 1164),
+ OP(1200, 1276),
+ OP(1400, 1420),
+ OP(1600, 1484),
+ { .frequency = CPUFREQ_TABLE_END }
+};
+
+/* Intel Pentium M processor 1.70GHz (Banias) */
+static struct cpufreq_frequency_table banias_1700[] =
+{
+ OP( 600, 956),
+ OP( 800, 1004),
+ OP(1000, 1116),
+ OP(1200, 1228),
+ OP(1400, 1308),
+ OP(1700, 1484),
+ { .frequency = CPUFREQ_TABLE_END }
+};
+#undef OP
+
+#define _BANIAS(cpuid, max, name) \
+{ .cpu_id = cpuid, \
+ .model_name = "Intel(R) Pentium(R) M processor " name "MHz", \
+ .max_freq = (max)*1000, \
+ .op_points = banias_##max, \
+}
+#define BANIAS(max) _BANIAS(&cpu_ids[CPU_BANIAS], max, #max)
+
+/* CPU models, their operating frequency range, and freq/voltage
+ operating points */
+static struct cpu_model models[] =
+{
+ _BANIAS(&cpu_ids[CPU_BANIAS], 900, " 900"),
+ BANIAS(1000),
+ BANIAS(1100),
+ BANIAS(1200),
+ BANIAS(1300),
+ BANIAS(1400),
+ BANIAS(1500),
+ BANIAS(1600),
+ BANIAS(1700),
+
+ /* NULL model_name is a wildcard */
+ { &cpu_ids[CPU_DOTHAN_A1], NULL, 0, NULL },
+ { &cpu_ids[CPU_DOTHAN_A2], NULL, 0, NULL },
+ { &cpu_ids[CPU_DOTHAN_B0], NULL, 0, NULL },
+ { &cpu_ids[CPU_MP4HT_D0], NULL, 0, NULL },
+ { &cpu_ids[CPU_MP4HT_E0], NULL, 0, NULL },
+
+ { NULL, }
+};
+#undef _BANIAS
+#undef BANIAS
+
+static int centrino_cpu_init_table(struct cpufreq_policy *policy)
+{
+ struct cpuinfo_x86 *cpu = &cpu_data(policy->cpu);
+ struct cpu_model *model;
+
+ for(model = models; model->cpu_id != NULL; model++)
+ if (centrino_verify_cpu_id(cpu, model->cpu_id) &&
+ (model->model_name == NULL ||
+ strcmp(cpu->x86_model_id, model->model_name) == 0))
+ break;
+
+ if (model->cpu_id == NULL) {
+ /* No match at all */
+ pr_debug("no support for CPU model \"%s\": "
+ "send /proc/cpuinfo to " MAINTAINER "\n",
+ cpu->x86_model_id);
+ return -ENOENT;
+ }
+
+ if (model->op_points == NULL) {
+ /* Matched a non-match */
+ pr_debug("no table support for CPU model \"%s\"\n",
+ cpu->x86_model_id);
+ pr_debug("try using the acpi-cpufreq driver\n");
+ return -ENOENT;
+ }
+
+ per_cpu(centrino_model, policy->cpu) = model;
+
+ pr_debug("found \"%s\": max frequency: %dkHz\n",
+ model->model_name, model->max_freq);
+
+ return 0;
+}
+
+#else
+static inline int centrino_cpu_init_table(struct cpufreq_policy *policy)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE */
+
+static int centrino_verify_cpu_id(const struct cpuinfo_x86 *c,
+ const struct cpu_id *x)
+{
+ if ((c->x86 == x->x86) &&
+ (c->x86_model == x->x86_model) &&
+ (c->x86_mask == x->x86_mask))
+ return 1;
+ return 0;
+}
+
+/* To be called only after centrino_model is initialized */
+static unsigned extract_clock(unsigned msr, unsigned int cpu, int failsafe)
+{
+ int i;
+
+ /*
+ * Extract clock in kHz from PERF_CTL value
+ * for centrino, as some DSDTs are buggy.
+ * Ideally, this can be done using the acpi_data structure.
+ */
+ if ((per_cpu(centrino_cpu, cpu) == &cpu_ids[CPU_BANIAS]) ||
+ (per_cpu(centrino_cpu, cpu) == &cpu_ids[CPU_DOTHAN_A1]) ||
+ (per_cpu(centrino_cpu, cpu) == &cpu_ids[CPU_DOTHAN_B0])) {
+ msr = (msr >> 8) & 0xff;
+ return msr * 100000;
+ }
+
+ if ((!per_cpu(centrino_model, cpu)) ||
+ (!per_cpu(centrino_model, cpu)->op_points))
+ return 0;
+
+ msr &= 0xffff;
+ for (i = 0;
+ per_cpu(centrino_model, cpu)->op_points[i].frequency
+ != CPUFREQ_TABLE_END;
+ i++) {
+ if (msr == per_cpu(centrino_model, cpu)->op_points[i].index)
+ return per_cpu(centrino_model, cpu)->
+ op_points[i].frequency;
+ }
+ if (failsafe)
+ return per_cpu(centrino_model, cpu)->op_points[i-1].frequency;
+ else
+ return 0;
+}
+
+/* Return the current CPU frequency in kHz */
+static unsigned int get_cur_freq(unsigned int cpu)
+{
+ unsigned l, h;
+ unsigned clock_freq;
+
+ rdmsr_on_cpu(cpu, MSR_IA32_PERF_STATUS, &l, &h);
+ clock_freq = extract_clock(l, cpu, 0);
+
+ if (unlikely(clock_freq == 0)) {
+ /*
+ * On some CPUs, we can see transient MSR values (which are
+ * not present in _PSS), while CPU is doing some automatic
+ * P-state transition (like TM2). Get the last freq set
+ * in PERF_CTL.
+ */
+ rdmsr_on_cpu(cpu, MSR_IA32_PERF_CTL, &l, &h);
+ clock_freq = extract_clock(l, cpu, 1);
+ }
+ return clock_freq;
+}
+
+
+static int centrino_cpu_init(struct cpufreq_policy *policy)
+{
+ struct cpuinfo_x86 *cpu = &cpu_data(policy->cpu);
+ unsigned freq;
+ unsigned l, h;
+ int ret;
+ int i;
+
+ /* Only Intel makes Enhanced Speedstep-capable CPUs */
+ if (cpu->x86_vendor != X86_VENDOR_INTEL ||
+ !cpu_has(cpu, X86_FEATURE_EST))
+ return -ENODEV;
+
+ if (cpu_has(cpu, X86_FEATURE_CONSTANT_TSC))
+ centrino_driver.flags |= CPUFREQ_CONST_LOOPS;
+
+ if (policy->cpu != 0)
+ return -ENODEV;
+
+ for (i = 0; i < N_IDS; i++)
+ if (centrino_verify_cpu_id(cpu, &cpu_ids[i]))
+ break;
+
+ if (i != N_IDS)
+ per_cpu(centrino_cpu, policy->cpu) = &cpu_ids[i];
+
+ if (!per_cpu(centrino_cpu, policy->cpu)) {
+ pr_debug("found unsupported CPU with "
+ "Enhanced SpeedStep: send /proc/cpuinfo to "
+ MAINTAINER "\n");
+ return -ENODEV;
+ }
+
+ if (centrino_cpu_init_table(policy)) {
+ return -ENODEV;
+ }
+
+ /* Check to see if Enhanced SpeedStep is enabled, and try to
+ enable it if not. */
+ rdmsr(MSR_IA32_MISC_ENABLE, l, h);
+
+ if (!(l & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {
+ l |= MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP;
+ pr_debug("trying to enable Enhanced SpeedStep (%x)\n", l);
+ wrmsr(MSR_IA32_MISC_ENABLE, l, h);
+
+ /* check to see if it stuck */
+ rdmsr(MSR_IA32_MISC_ENABLE, l, h);
+ if (!(l & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {
+ printk(KERN_INFO PFX
+ "couldn't enable Enhanced SpeedStep\n");
+ return -ENODEV;
+ }
+ }
+
+ freq = get_cur_freq(policy->cpu);
+ policy->cpuinfo.transition_latency = 10000;
+ /* 10uS transition latency */
+ policy->cur = freq;
+
+ pr_debug("centrino_cpu_init: cur=%dkHz\n", policy->cur);
+
+ ret = cpufreq_frequency_table_cpuinfo(policy,
+ per_cpu(centrino_model, policy->cpu)->op_points);
+ if (ret)
+ return (ret);
+
+ cpufreq_frequency_table_get_attr(
+ per_cpu(centrino_model, policy->cpu)->op_points, policy->cpu);
+
+ return 0;
+}
+
+static int centrino_cpu_exit(struct cpufreq_policy *policy)
+{
+ unsigned int cpu = policy->cpu;
+
+ if (!per_cpu(centrino_model, cpu))
+ return -ENODEV;
+
+ cpufreq_frequency_table_put_attr(cpu);
+
+ per_cpu(centrino_model, cpu) = NULL;
+
+ return 0;
+}
+
+/**
+ * centrino_verify - verifies a new CPUFreq policy
+ * @policy: new policy
+ *
+ * Limit must be within this model's frequency range at least one
+ * border included.
+ */
+static int centrino_verify (struct cpufreq_policy *policy)
+{
+ return cpufreq_frequency_table_verify(policy,
+ per_cpu(centrino_model, policy->cpu)->op_points);
+}
+
+/**
+ * centrino_setpolicy - set a new CPUFreq policy
+ * @policy: new policy
+ * @target_freq: the target frequency
+ * @relation: how that frequency relates to achieved frequency
+ * (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
+ *
+ * Sets a new CPUFreq policy.
+ */
+static int centrino_target (struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ unsigned int newstate = 0;
+ unsigned int msr, oldmsr = 0, h = 0, cpu = policy->cpu;
+ struct cpufreq_freqs freqs;
+ int retval = 0;
+ unsigned int j, k, first_cpu, tmp;
+ cpumask_var_t covered_cpus;
+
+ if (unlikely(!zalloc_cpumask_var(&covered_cpus, GFP_KERNEL)))
+ return -ENOMEM;
+
+ if (unlikely(per_cpu(centrino_model, cpu) == NULL)) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ if (unlikely(cpufreq_frequency_table_target(policy,
+ per_cpu(centrino_model, cpu)->op_points,
+ target_freq,
+ relation,
+ &newstate))) {
+ retval = -EINVAL;
+ goto out;
+ }
+
+ first_cpu = 1;
+ for_each_cpu(j, policy->cpus) {
+ int good_cpu;
+
+ /* cpufreq holds the hotplug lock, so we are safe here */
+ if (!cpu_online(j))
+ continue;
+
+ /*
+ * Support for SMP systems.
+ * Make sure we are running on CPU that wants to change freq
+ */
+ if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY)
+ good_cpu = cpumask_any_and(policy->cpus,
+ cpu_online_mask);
+ else
+ good_cpu = j;
+
+ if (good_cpu >= nr_cpu_ids) {
+ pr_debug("couldn't limit to CPUs in this domain\n");
+ retval = -EAGAIN;
+ if (first_cpu) {
+ /* We haven't started the transition yet. */
+ goto out;
+ }
+ break;
+ }
+
+ msr = per_cpu(centrino_model, cpu)->op_points[newstate].index;
+
+ if (first_cpu) {
+ rdmsr_on_cpu(good_cpu, MSR_IA32_PERF_CTL, &oldmsr, &h);
+ if (msr == (oldmsr & 0xffff)) {
+ pr_debug("no change needed - msr was and needs "
+ "to be %x\n", oldmsr);
+ retval = 0;
+ goto out;
+ }
+
+ freqs.old = extract_clock(oldmsr, cpu, 0);
+ freqs.new = extract_clock(msr, cpu, 0);
+
+ pr_debug("target=%dkHz old=%d new=%d msr=%04x\n",
+ target_freq, freqs.old, freqs.new, msr);
+
+ for_each_cpu(k, policy->cpus) {
+ if (!cpu_online(k))
+ continue;
+ freqs.cpu = k;
+ cpufreq_notify_transition(&freqs,
+ CPUFREQ_PRECHANGE);
+ }
+
+ first_cpu = 0;
+ /* all but 16 LSB are reserved, treat them with care */
+ oldmsr &= ~0xffff;
+ msr &= 0xffff;
+ oldmsr |= msr;
+ }
+
+ wrmsr_on_cpu(good_cpu, MSR_IA32_PERF_CTL, oldmsr, h);
+ if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY)
+ break;
+
+ cpumask_set_cpu(j, covered_cpus);
+ }
+
+ for_each_cpu(k, policy->cpus) {
+ if (!cpu_online(k))
+ continue;
+ freqs.cpu = k;
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
+
+ if (unlikely(retval)) {
+ /*
+ * We have failed halfway through the frequency change.
+ * We have sent callbacks to policy->cpus and
+ * MSRs have already been written on coverd_cpus.
+ * Best effort undo..
+ */
+
+ for_each_cpu(j, covered_cpus)
+ wrmsr_on_cpu(j, MSR_IA32_PERF_CTL, oldmsr, h);
+
+ tmp = freqs.new;
+ freqs.new = freqs.old;
+ freqs.old = tmp;
+ for_each_cpu(j, policy->cpus) {
+ if (!cpu_online(j))
+ continue;
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
+ }
+ retval = 0;
+
+out:
+ free_cpumask_var(covered_cpus);
+ return retval;
+}
+
+static struct freq_attr* centrino_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+static struct cpufreq_driver centrino_driver = {
+ .name = "centrino", /* should be speedstep-centrino,
+ but there's a 16 char limit */
+ .init = centrino_cpu_init,
+ .exit = centrino_cpu_exit,
+ .verify = centrino_verify,
+ .target = centrino_target,
+ .get = get_cur_freq,
+ .attr = centrino_attr,
+ .owner = THIS_MODULE,
+};
+
+
+/**
+ * centrino_init - initializes the Enhanced SpeedStep CPUFreq driver
+ *
+ * Initializes the Enhanced SpeedStep support. Returns -ENODEV on
+ * unsupported devices, -ENOENT if there's no voltage table for this
+ * particular CPU model, -EINVAL on problems during initiatization,
+ * and zero on success.
+ *
+ * This is quite picky. Not only does the CPU have to advertise the
+ * "est" flag in the cpuid capability flags, we look for a specific
+ * CPU model and stepping, and we need to have the exact model name in
+ * our voltage tables. That is, be paranoid about not releasing
+ * someone's valuable magic smoke.
+ */
+static int __init centrino_init(void)
+{
+ struct cpuinfo_x86 *cpu = &cpu_data(0);
+
+ if (!cpu_has(cpu, X86_FEATURE_EST))
+ return -ENODEV;
+
+ return cpufreq_register_driver(&centrino_driver);
+}
+
+static void __exit centrino_exit(void)
+{
+ cpufreq_unregister_driver(&centrino_driver);
+}
+
+MODULE_AUTHOR ("Jeremy Fitzhardinge <jeremy@goop.org>");
+MODULE_DESCRIPTION ("Enhanced SpeedStep driver for Intel Pentium M processors.");
+MODULE_LICENSE ("GPL");
+
+late_initcall(centrino_init);
+module_exit(centrino_exit);
diff --git a/drivers/cpufreq/speedstep-ich.c b/drivers/cpufreq/speedstep-ich.c
new file mode 100644
index 00000000000..a748ce782fe
--- /dev/null
+++ b/drivers/cpufreq/speedstep-ich.c
@@ -0,0 +1,448 @@
+/*
+ * (C) 2001 Dave Jones, Arjan van de ven.
+ * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de>
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ * Based upon reverse engineered information, and on Intel documentation
+ * for chipsets ICH2-M and ICH3-M.
+ *
+ * Many thanks to Ducrot Bruno for finding and fixing the last
+ * "missing link" for ICH2-M/ICH3-M support, and to Thomas Winkler
+ * for extensive testing.
+ *
+ * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+
+/*********************************************************************
+ * SPEEDSTEP - DEFINITIONS *
+ *********************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+
+#include "speedstep-lib.h"
+
+
+/* speedstep_chipset:
+ * It is necessary to know which chipset is used. As accesses to
+ * this device occur at various places in this module, we need a
+ * static struct pci_dev * pointing to that device.
+ */
+static struct pci_dev *speedstep_chipset_dev;
+
+
+/* speedstep_processor
+ */
+static enum speedstep_processor speedstep_processor;
+
+static u32 pmbase;
+
+/*
+ * There are only two frequency states for each processor. Values
+ * are in kHz for the time being.
+ */
+static struct cpufreq_frequency_table speedstep_freqs[] = {
+ {SPEEDSTEP_HIGH, 0},
+ {SPEEDSTEP_LOW, 0},
+ {0, CPUFREQ_TABLE_END},
+};
+
+
+/**
+ * speedstep_find_register - read the PMBASE address
+ *
+ * Returns: -ENODEV if no register could be found
+ */
+static int speedstep_find_register(void)
+{
+ if (!speedstep_chipset_dev)
+ return -ENODEV;
+
+ /* get PMBASE */
+ pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase);
+ if (!(pmbase & 0x01)) {
+ printk(KERN_ERR "speedstep-ich: could not find speedstep register\n");
+ return -ENODEV;
+ }
+
+ pmbase &= 0xFFFFFFFE;
+ if (!pmbase) {
+ printk(KERN_ERR "speedstep-ich: could not find speedstep register\n");
+ return -ENODEV;
+ }
+
+ pr_debug("pmbase is 0x%x\n", pmbase);
+ return 0;
+}
+
+/**
+ * speedstep_set_state - set the SpeedStep state
+ * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
+ *
+ * Tries to change the SpeedStep state. Can be called from
+ * smp_call_function_single.
+ */
+static void speedstep_set_state(unsigned int state)
+{
+ u8 pm2_blk;
+ u8 value;
+ unsigned long flags;
+
+ if (state > 0x1)
+ return;
+
+ /* Disable IRQs */
+ local_irq_save(flags);
+
+ /* read state */
+ value = inb(pmbase + 0x50);
+
+ pr_debug("read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value);
+
+ /* write new state */
+ value &= 0xFE;
+ value |= state;
+
+ pr_debug("writing 0x%x to pmbase 0x%x + 0x50\n", value, pmbase);
+
+ /* Disable bus master arbitration */
+ pm2_blk = inb(pmbase + 0x20);
+ pm2_blk |= 0x01;
+ outb(pm2_blk, (pmbase + 0x20));
+
+ /* Actual transition */
+ outb(value, (pmbase + 0x50));
+
+ /* Restore bus master arbitration */
+ pm2_blk &= 0xfe;
+ outb(pm2_blk, (pmbase + 0x20));
+
+ /* check if transition was successful */
+ value = inb(pmbase + 0x50);
+
+ /* Enable IRQs */
+ local_irq_restore(flags);
+
+ pr_debug("read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value);
+
+ if (state == (value & 0x1))
+ pr_debug("change to %u MHz succeeded\n",
+ speedstep_get_frequency(speedstep_processor) / 1000);
+ else
+ printk(KERN_ERR "cpufreq: change failed - I/O error\n");
+
+ return;
+}
+
+/* Wrapper for smp_call_function_single. */
+static void _speedstep_set_state(void *_state)
+{
+ speedstep_set_state(*(unsigned int *)_state);
+}
+
+/**
+ * speedstep_activate - activate SpeedStep control in the chipset
+ *
+ * Tries to activate the SpeedStep status and control registers.
+ * Returns -EINVAL on an unsupported chipset, and zero on success.
+ */
+static int speedstep_activate(void)
+{
+ u16 value = 0;
+
+ if (!speedstep_chipset_dev)
+ return -EINVAL;
+
+ pci_read_config_word(speedstep_chipset_dev, 0x00A0, &value);
+ if (!(value & 0x08)) {
+ value |= 0x08;
+ pr_debug("activating SpeedStep (TM) registers\n");
+ pci_write_config_word(speedstep_chipset_dev, 0x00A0, value);
+ }
+
+ return 0;
+}
+
+
+/**
+ * speedstep_detect_chipset - detect the Southbridge which contains SpeedStep logic
+ *
+ * Detects ICH2-M, ICH3-M and ICH4-M so far. The pci_dev points to
+ * the LPC bridge / PM module which contains all power-management
+ * functions. Returns the SPEEDSTEP_CHIPSET_-number for the detected
+ * chipset, or zero on failure.
+ */
+static unsigned int speedstep_detect_chipset(void)
+{
+ speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82801DB_12,
+ PCI_ANY_ID, PCI_ANY_ID,
+ NULL);
+ if (speedstep_chipset_dev)
+ return 4; /* 4-M */
+
+ speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82801CA_12,
+ PCI_ANY_ID, PCI_ANY_ID,
+ NULL);
+ if (speedstep_chipset_dev)
+ return 3; /* 3-M */
+
+
+ speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82801BA_10,
+ PCI_ANY_ID, PCI_ANY_ID,
+ NULL);
+ if (speedstep_chipset_dev) {
+ /* speedstep.c causes lockups on Dell Inspirons 8000 and
+ * 8100 which use a pretty old revision of the 82815
+ * host brige. Abort on these systems.
+ */
+ static struct pci_dev *hostbridge;
+
+ hostbridge = pci_get_subsys(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82815_MC,
+ PCI_ANY_ID, PCI_ANY_ID,
+ NULL);
+
+ if (!hostbridge)
+ return 2; /* 2-M */
+
+ if (hostbridge->revision < 5) {
+ pr_debug("hostbridge does not support speedstep\n");
+ speedstep_chipset_dev = NULL;
+ pci_dev_put(hostbridge);
+ return 0;
+ }
+
+ pci_dev_put(hostbridge);
+ return 2; /* 2-M */
+ }
+
+ return 0;
+}
+
+static void get_freq_data(void *_speed)
+{
+ unsigned int *speed = _speed;
+
+ *speed = speedstep_get_frequency(speedstep_processor);
+}
+
+static unsigned int speedstep_get(unsigned int cpu)
+{
+ unsigned int speed;
+
+ /* You're supposed to ensure CPU is online. */
+ if (smp_call_function_single(cpu, get_freq_data, &speed, 1) != 0)
+ BUG();
+
+ pr_debug("detected %u kHz as current frequency\n", speed);
+ return speed;
+}
+
+/**
+ * speedstep_target - set a new CPUFreq policy
+ * @policy: new policy
+ * @target_freq: the target frequency
+ * @relation: how that frequency relates to achieved frequency
+ * (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
+ *
+ * Sets a new CPUFreq policy.
+ */
+static int speedstep_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ unsigned int newstate = 0, policy_cpu;
+ struct cpufreq_freqs freqs;
+ int i;
+
+ if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0],
+ target_freq, relation, &newstate))
+ return -EINVAL;
+
+ policy_cpu = cpumask_any_and(policy->cpus, cpu_online_mask);
+ freqs.old = speedstep_get(policy_cpu);
+ freqs.new = speedstep_freqs[newstate].frequency;
+ freqs.cpu = policy->cpu;
+
+ pr_debug("transiting from %u to %u kHz\n", freqs.old, freqs.new);
+
+ /* no transition necessary */
+ if (freqs.old == freqs.new)
+ return 0;
+
+ for_each_cpu(i, policy->cpus) {
+ freqs.cpu = i;
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ }
+
+ smp_call_function_single(policy_cpu, _speedstep_set_state, &newstate,
+ true);
+
+ for_each_cpu(i, policy->cpus) {
+ freqs.cpu = i;
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
+
+ return 0;
+}
+
+
+/**
+ * speedstep_verify - verifies a new CPUFreq policy
+ * @policy: new policy
+ *
+ * Limit must be within speedstep_low_freq and speedstep_high_freq, with
+ * at least one border included.
+ */
+static int speedstep_verify(struct cpufreq_policy *policy)
+{
+ return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
+}
+
+struct get_freqs {
+ struct cpufreq_policy *policy;
+ int ret;
+};
+
+static void get_freqs_on_cpu(void *_get_freqs)
+{
+ struct get_freqs *get_freqs = _get_freqs;
+
+ get_freqs->ret =
+ speedstep_get_freqs(speedstep_processor,
+ &speedstep_freqs[SPEEDSTEP_LOW].frequency,
+ &speedstep_freqs[SPEEDSTEP_HIGH].frequency,
+ &get_freqs->policy->cpuinfo.transition_latency,
+ &speedstep_set_state);
+}
+
+static int speedstep_cpu_init(struct cpufreq_policy *policy)
+{
+ int result;
+ unsigned int policy_cpu, speed;
+ struct get_freqs gf;
+
+ /* only run on CPU to be set, or on its sibling */
+#ifdef CONFIG_SMP
+ cpumask_copy(policy->cpus, cpu_sibling_mask(policy->cpu));
+#endif
+ policy_cpu = cpumask_any_and(policy->cpus, cpu_online_mask);
+
+ /* detect low and high frequency and transition latency */
+ gf.policy = policy;
+ smp_call_function_single(policy_cpu, get_freqs_on_cpu, &gf, 1);
+ if (gf.ret)
+ return gf.ret;
+
+ /* get current speed setting */
+ speed = speedstep_get(policy_cpu);
+ if (!speed)
+ return -EIO;
+
+ pr_debug("currently at %s speed setting - %i MHz\n",
+ (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency)
+ ? "low" : "high",
+ (speed / 1000));
+
+ /* cpuinfo and default policy values */
+ policy->cur = speed;
+
+ result = cpufreq_frequency_table_cpuinfo(policy, speedstep_freqs);
+ if (result)
+ return result;
+
+ cpufreq_frequency_table_get_attr(speedstep_freqs, policy->cpu);
+
+ return 0;
+}
+
+
+static int speedstep_cpu_exit(struct cpufreq_policy *policy)
+{
+ cpufreq_frequency_table_put_attr(policy->cpu);
+ return 0;
+}
+
+static struct freq_attr *speedstep_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+
+static struct cpufreq_driver speedstep_driver = {
+ .name = "speedstep-ich",
+ .verify = speedstep_verify,
+ .target = speedstep_target,
+ .init = speedstep_cpu_init,
+ .exit = speedstep_cpu_exit,
+ .get = speedstep_get,
+ .owner = THIS_MODULE,
+ .attr = speedstep_attr,
+};
+
+
+/**
+ * speedstep_init - initializes the SpeedStep CPUFreq driver
+ *
+ * Initializes the SpeedStep support. Returns -ENODEV on unsupported
+ * devices, -EINVAL on problems during initiatization, and zero on
+ * success.
+ */
+static int __init speedstep_init(void)
+{
+ /* detect processor */
+ speedstep_processor = speedstep_detect_processor();
+ if (!speedstep_processor) {
+ pr_debug("Intel(R) SpeedStep(TM) capable processor "
+ "not found\n");
+ return -ENODEV;
+ }
+
+ /* detect chipset */
+ if (!speedstep_detect_chipset()) {
+ pr_debug("Intel(R) SpeedStep(TM) for this chipset not "
+ "(yet) available.\n");
+ return -ENODEV;
+ }
+
+ /* activate speedstep support */
+ if (speedstep_activate()) {
+ pci_dev_put(speedstep_chipset_dev);
+ return -EINVAL;
+ }
+
+ if (speedstep_find_register())
+ return -ENODEV;
+
+ return cpufreq_register_driver(&speedstep_driver);
+}
+
+
+/**
+ * speedstep_exit - unregisters SpeedStep support
+ *
+ * Unregisters SpeedStep support.
+ */
+static void __exit speedstep_exit(void)
+{
+ pci_dev_put(speedstep_chipset_dev);
+ cpufreq_unregister_driver(&speedstep_driver);
+}
+
+
+MODULE_AUTHOR("Dave Jones <davej@redhat.com>, "
+ "Dominik Brodowski <linux@brodo.de>");
+MODULE_DESCRIPTION("Speedstep driver for Intel mobile processors on chipsets "
+ "with ICH-M southbridges.");
+MODULE_LICENSE("GPL");
+
+module_init(speedstep_init);
+module_exit(speedstep_exit);
diff --git a/drivers/cpufreq/speedstep-lib.c b/drivers/cpufreq/speedstep-lib.c
new file mode 100644
index 00000000000..8af2d2fd9d5
--- /dev/null
+++ b/drivers/cpufreq/speedstep-lib.c
@@ -0,0 +1,478 @@
+/*
+ * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de>
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ *
+ * Library for common functions for Intel SpeedStep v.1 and v.2 support
+ *
+ * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+
+#include <asm/msr.h>
+#include <asm/tsc.h>
+#include "speedstep-lib.h"
+
+#define PFX "speedstep-lib: "
+
+#ifdef CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK
+static int relaxed_check;
+#else
+#define relaxed_check 0
+#endif
+
+/*********************************************************************
+ * GET PROCESSOR CORE SPEED IN KHZ *
+ *********************************************************************/
+
+static unsigned int pentium3_get_frequency(enum speedstep_processor processor)
+{
+ /* See table 14 of p3_ds.pdf and table 22 of 29834003.pdf */
+ struct {
+ unsigned int ratio; /* Frequency Multiplier (x10) */
+ u8 bitmap; /* power on configuration bits
+ [27, 25:22] (in MSR 0x2a) */
+ } msr_decode_mult[] = {
+ { 30, 0x01 },
+ { 35, 0x05 },
+ { 40, 0x02 },
+ { 45, 0x06 },
+ { 50, 0x00 },
+ { 55, 0x04 },
+ { 60, 0x0b },
+ { 65, 0x0f },
+ { 70, 0x09 },
+ { 75, 0x0d },
+ { 80, 0x0a },
+ { 85, 0x26 },
+ { 90, 0x20 },
+ { 100, 0x2b },
+ { 0, 0xff } /* error or unknown value */
+ };
+
+ /* PIII(-M) FSB settings: see table b1-b of 24547206.pdf */
+ struct {
+ unsigned int value; /* Front Side Bus speed in MHz */
+ u8 bitmap; /* power on configuration bits [18: 19]
+ (in MSR 0x2a) */
+ } msr_decode_fsb[] = {
+ { 66, 0x0 },
+ { 100, 0x2 },
+ { 133, 0x1 },
+ { 0, 0xff}
+ };
+
+ u32 msr_lo, msr_tmp;
+ int i = 0, j = 0;
+
+ /* read MSR 0x2a - we only need the low 32 bits */
+ rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_tmp);
+ pr_debug("P3 - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n", msr_lo, msr_tmp);
+ msr_tmp = msr_lo;
+
+ /* decode the FSB */
+ msr_tmp &= 0x00c0000;
+ msr_tmp >>= 18;
+ while (msr_tmp != msr_decode_fsb[i].bitmap) {
+ if (msr_decode_fsb[i].bitmap == 0xff)
+ return 0;
+ i++;
+ }
+
+ /* decode the multiplier */
+ if (processor == SPEEDSTEP_CPU_PIII_C_EARLY) {
+ pr_debug("workaround for early PIIIs\n");
+ msr_lo &= 0x03c00000;
+ } else
+ msr_lo &= 0x0bc00000;
+ msr_lo >>= 22;
+ while (msr_lo != msr_decode_mult[j].bitmap) {
+ if (msr_decode_mult[j].bitmap == 0xff)
+ return 0;
+ j++;
+ }
+
+ pr_debug("speed is %u\n",
+ (msr_decode_mult[j].ratio * msr_decode_fsb[i].value * 100));
+
+ return msr_decode_mult[j].ratio * msr_decode_fsb[i].value * 100;
+}
+
+
+static unsigned int pentiumM_get_frequency(void)
+{
+ u32 msr_lo, msr_tmp;
+
+ rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_tmp);
+ pr_debug("PM - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n", msr_lo, msr_tmp);
+
+ /* see table B-2 of 24547212.pdf */
+ if (msr_lo & 0x00040000) {
+ printk(KERN_DEBUG PFX "PM - invalid FSB: 0x%x 0x%x\n",
+ msr_lo, msr_tmp);
+ return 0;
+ }
+
+ msr_tmp = (msr_lo >> 22) & 0x1f;
+ pr_debug("bits 22-26 are 0x%x, speed is %u\n",
+ msr_tmp, (msr_tmp * 100 * 1000));
+
+ return msr_tmp * 100 * 1000;
+}
+
+static unsigned int pentium_core_get_frequency(void)
+{
+ u32 fsb = 0;
+ u32 msr_lo, msr_tmp;
+ int ret;
+
+ rdmsr(MSR_FSB_FREQ, msr_lo, msr_tmp);
+ /* see table B-2 of 25366920.pdf */
+ switch (msr_lo & 0x07) {
+ case 5:
+ fsb = 100000;
+ break;
+ case 1:
+ fsb = 133333;
+ break;
+ case 3:
+ fsb = 166667;
+ break;
+ case 2:
+ fsb = 200000;
+ break;
+ case 0:
+ fsb = 266667;
+ break;
+ case 4:
+ fsb = 333333;
+ break;
+ default:
+ printk(KERN_ERR "PCORE - MSR_FSB_FREQ undefined value");
+ }
+
+ rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_tmp);
+ pr_debug("PCORE - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n",
+ msr_lo, msr_tmp);
+
+ msr_tmp = (msr_lo >> 22) & 0x1f;
+ pr_debug("bits 22-26 are 0x%x, speed is %u\n",
+ msr_tmp, (msr_tmp * fsb));
+
+ ret = (msr_tmp * fsb);
+ return ret;
+}
+
+
+static unsigned int pentium4_get_frequency(void)
+{
+ struct cpuinfo_x86 *c = &boot_cpu_data;
+ u32 msr_lo, msr_hi, mult;
+ unsigned int fsb = 0;
+ unsigned int ret;
+ u8 fsb_code;
+
+ /* Pentium 4 Model 0 and 1 do not have the Core Clock Frequency
+ * to System Bus Frequency Ratio Field in the Processor Frequency
+ * Configuration Register of the MSR. Therefore the current
+ * frequency cannot be calculated and has to be measured.
+ */
+ if (c->x86_model < 2)
+ return cpu_khz;
+
+ rdmsr(0x2c, msr_lo, msr_hi);
+
+ pr_debug("P4 - MSR_EBC_FREQUENCY_ID: 0x%x 0x%x\n", msr_lo, msr_hi);
+
+ /* decode the FSB: see IA-32 Intel (C) Architecture Software
+ * Developer's Manual, Volume 3: System Prgramming Guide,
+ * revision #12 in Table B-1: MSRs in the Pentium 4 and
+ * Intel Xeon Processors, on page B-4 and B-5.
+ */
+ fsb_code = (msr_lo >> 16) & 0x7;
+ switch (fsb_code) {
+ case 0:
+ fsb = 100 * 1000;
+ break;
+ case 1:
+ fsb = 13333 * 10;
+ break;
+ case 2:
+ fsb = 200 * 1000;
+ break;
+ }
+
+ if (!fsb)
+ printk(KERN_DEBUG PFX "couldn't detect FSB speed. "
+ "Please send an e-mail to <linux@brodo.de>\n");
+
+ /* Multiplier. */
+ mult = msr_lo >> 24;
+
+ pr_debug("P4 - FSB %u kHz; Multiplier %u; Speed %u kHz\n",
+ fsb, mult, (fsb * mult));
+
+ ret = (fsb * mult);
+ return ret;
+}
+
+
+/* Warning: may get called from smp_call_function_single. */
+unsigned int speedstep_get_frequency(enum speedstep_processor processor)
+{
+ switch (processor) {
+ case SPEEDSTEP_CPU_PCORE:
+ return pentium_core_get_frequency();
+ case SPEEDSTEP_CPU_PM:
+ return pentiumM_get_frequency();
+ case SPEEDSTEP_CPU_P4D:
+ case SPEEDSTEP_CPU_P4M:
+ return pentium4_get_frequency();
+ case SPEEDSTEP_CPU_PIII_T:
+ case SPEEDSTEP_CPU_PIII_C:
+ case SPEEDSTEP_CPU_PIII_C_EARLY:
+ return pentium3_get_frequency(processor);
+ default:
+ return 0;
+ };
+ return 0;
+}
+EXPORT_SYMBOL_GPL(speedstep_get_frequency);
+
+
+/*********************************************************************
+ * DETECT SPEEDSTEP-CAPABLE PROCESSOR *
+ *********************************************************************/
+
+unsigned int speedstep_detect_processor(void)
+{
+ struct cpuinfo_x86 *c = &cpu_data(0);
+ u32 ebx, msr_lo, msr_hi;
+
+ pr_debug("x86: %x, model: %x\n", c->x86, c->x86_model);
+
+ if ((c->x86_vendor != X86_VENDOR_INTEL) ||
+ ((c->x86 != 6) && (c->x86 != 0xF)))
+ return 0;
+
+ if (c->x86 == 0xF) {
+ /* Intel Mobile Pentium 4-M
+ * or Intel Mobile Pentium 4 with 533 MHz FSB */
+ if (c->x86_model != 2)
+ return 0;
+
+ ebx = cpuid_ebx(0x00000001);
+ ebx &= 0x000000FF;
+
+ pr_debug("ebx value is %x, x86_mask is %x\n", ebx, c->x86_mask);
+
+ switch (c->x86_mask) {
+ case 4:
+ /*
+ * B-stepping [M-P4-M]
+ * sample has ebx = 0x0f, production has 0x0e.
+ */
+ if ((ebx == 0x0e) || (ebx == 0x0f))
+ return SPEEDSTEP_CPU_P4M;
+ break;
+ case 7:
+ /*
+ * C-stepping [M-P4-M]
+ * needs to have ebx=0x0e, else it's a celeron:
+ * cf. 25130917.pdf / page 7, footnote 5 even
+ * though 25072120.pdf / page 7 doesn't say
+ * samples are only of B-stepping...
+ */
+ if (ebx == 0x0e)
+ return SPEEDSTEP_CPU_P4M;
+ break;
+ case 9:
+ /*
+ * D-stepping [M-P4-M or M-P4/533]
+ *
+ * this is totally strange: CPUID 0x0F29 is
+ * used by M-P4-M, M-P4/533 and(!) Celeron CPUs.
+ * The latter need to be sorted out as they don't
+ * support speedstep.
+ * Celerons with CPUID 0x0F29 may have either
+ * ebx=0x8 or 0xf -- 25130917.pdf doesn't say anything
+ * specific.
+ * M-P4-Ms may have either ebx=0xe or 0xf [see above]
+ * M-P4/533 have either ebx=0xe or 0xf. [25317607.pdf]
+ * also, M-P4M HTs have ebx=0x8, too
+ * For now, they are distinguished by the model_id
+ * string
+ */
+ if ((ebx == 0x0e) ||
+ (strstr(c->x86_model_id,
+ "Mobile Intel(R) Pentium(R) 4") != NULL))
+ return SPEEDSTEP_CPU_P4M;
+ break;
+ default:
+ break;
+ }
+ return 0;
+ }
+
+ switch (c->x86_model) {
+ case 0x0B: /* Intel PIII [Tualatin] */
+ /* cpuid_ebx(1) is 0x04 for desktop PIII,
+ * 0x06 for mobile PIII-M */
+ ebx = cpuid_ebx(0x00000001);
+ pr_debug("ebx is %x\n", ebx);
+
+ ebx &= 0x000000FF;
+
+ if (ebx != 0x06)
+ return 0;
+
+ /* So far all PIII-M processors support SpeedStep. See
+ * Intel's 24540640.pdf of June 2003
+ */
+ return SPEEDSTEP_CPU_PIII_T;
+
+ case 0x08: /* Intel PIII [Coppermine] */
+
+ /* all mobile PIII Coppermines have FSB 100 MHz
+ * ==> sort out a few desktop PIIIs. */
+ rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_hi);
+ pr_debug("Coppermine: MSR_IA32_EBL_CR_POWERON is 0x%x, 0x%x\n",
+ msr_lo, msr_hi);
+ msr_lo &= 0x00c0000;
+ if (msr_lo != 0x0080000)
+ return 0;
+
+ /*
+ * If the processor is a mobile version,
+ * platform ID has bit 50 set
+ * it has SpeedStep technology if either
+ * bit 56 or 57 is set
+ */
+ rdmsr(MSR_IA32_PLATFORM_ID, msr_lo, msr_hi);
+ pr_debug("Coppermine: MSR_IA32_PLATFORM ID is 0x%x, 0x%x\n",
+ msr_lo, msr_hi);
+ if ((msr_hi & (1<<18)) &&
+ (relaxed_check ? 1 : (msr_hi & (3<<24)))) {
+ if (c->x86_mask == 0x01) {
+ pr_debug("early PIII version\n");
+ return SPEEDSTEP_CPU_PIII_C_EARLY;
+ } else
+ return SPEEDSTEP_CPU_PIII_C;
+ }
+
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(speedstep_detect_processor);
+
+
+/*********************************************************************
+ * DETECT SPEEDSTEP SPEEDS *
+ *********************************************************************/
+
+unsigned int speedstep_get_freqs(enum speedstep_processor processor,
+ unsigned int *low_speed,
+ unsigned int *high_speed,
+ unsigned int *transition_latency,
+ void (*set_state) (unsigned int state))
+{
+ unsigned int prev_speed;
+ unsigned int ret = 0;
+ unsigned long flags;
+ struct timeval tv1, tv2;
+
+ if ((!processor) || (!low_speed) || (!high_speed) || (!set_state))
+ return -EINVAL;
+
+ pr_debug("trying to determine both speeds\n");
+
+ /* get current speed */
+ prev_speed = speedstep_get_frequency(processor);
+ if (!prev_speed)
+ return -EIO;
+
+ pr_debug("previous speed is %u\n", prev_speed);
+
+ local_irq_save(flags);
+
+ /* switch to low state */
+ set_state(SPEEDSTEP_LOW);
+ *low_speed = speedstep_get_frequency(processor);
+ if (!*low_speed) {
+ ret = -EIO;
+ goto out;
+ }
+
+ pr_debug("low speed is %u\n", *low_speed);
+
+ /* start latency measurement */
+ if (transition_latency)
+ do_gettimeofday(&tv1);
+
+ /* switch to high state */
+ set_state(SPEEDSTEP_HIGH);
+
+ /* end latency measurement */
+ if (transition_latency)
+ do_gettimeofday(&tv2);
+
+ *high_speed = speedstep_get_frequency(processor);
+ if (!*high_speed) {
+ ret = -EIO;
+ goto out;
+ }
+
+ pr_debug("high speed is %u\n", *high_speed);
+
+ if (*low_speed == *high_speed) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ /* switch to previous state, if necessary */
+ if (*high_speed != prev_speed)
+ set_state(SPEEDSTEP_LOW);
+
+ if (transition_latency) {
+ *transition_latency = (tv2.tv_sec - tv1.tv_sec) * USEC_PER_SEC +
+ tv2.tv_usec - tv1.tv_usec;
+ pr_debug("transition latency is %u uSec\n", *transition_latency);
+
+ /* convert uSec to nSec and add 20% for safety reasons */
+ *transition_latency *= 1200;
+
+ /* check if the latency measurement is too high or too low
+ * and set it to a safe value (500uSec) in that case
+ */
+ if (*transition_latency > 10000000 ||
+ *transition_latency < 50000) {
+ printk(KERN_WARNING PFX "frequency transition "
+ "measured seems out of range (%u "
+ "nSec), falling back to a safe one of"
+ "%u nSec.\n",
+ *transition_latency, 500000);
+ *transition_latency = 500000;
+ }
+ }
+
+out:
+ local_irq_restore(flags);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(speedstep_get_freqs);
+
+#ifdef CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK
+module_param(relaxed_check, int, 0444);
+MODULE_PARM_DESC(relaxed_check,
+ "Don't do all checks for speedstep capability.");
+#endif
+
+MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
+MODULE_DESCRIPTION("Library for Intel SpeedStep 1 or 2 cpufreq drivers.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/cpufreq/speedstep-lib.h b/drivers/cpufreq/speedstep-lib.h
new file mode 100644
index 00000000000..70d9cea1219
--- /dev/null
+++ b/drivers/cpufreq/speedstep-lib.h
@@ -0,0 +1,49 @@
+/*
+ * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de>
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ *
+ * Library for common functions for Intel SpeedStep v.1 and v.2 support
+ *
+ * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+
+
+/* processors */
+enum speedstep_processor {
+ SPEEDSTEP_CPU_PIII_C_EARLY = 0x00000001, /* Coppermine core */
+ SPEEDSTEP_CPU_PIII_C = 0x00000002, /* Coppermine core */
+ SPEEDSTEP_CPU_PIII_T = 0x00000003, /* Tualatin core */
+ SPEEDSTEP_CPU_P4M = 0x00000004, /* P4-M */
+/* the following processors are not speedstep-capable and are not auto-detected
+ * in speedstep_detect_processor(). However, their speed can be detected using
+ * the speedstep_get_frequency() call. */
+ SPEEDSTEP_CPU_PM = 0xFFFFFF03, /* Pentium M */
+ SPEEDSTEP_CPU_P4D = 0xFFFFFF04, /* desktop P4 */
+ SPEEDSTEP_CPU_PCORE = 0xFFFFFF05, /* Core */
+};
+
+/* speedstep states -- only two of them */
+
+#define SPEEDSTEP_HIGH 0x00000000
+#define SPEEDSTEP_LOW 0x00000001
+
+
+/* detect a speedstep-capable processor */
+extern enum speedstep_processor speedstep_detect_processor(void);
+
+/* detect the current speed (in khz) of the processor */
+extern unsigned int speedstep_get_frequency(enum speedstep_processor processor);
+
+
+/* detect the low and high speeds of the processor. The callback
+ * set_state"'s first argument is either SPEEDSTEP_HIGH or
+ * SPEEDSTEP_LOW; the second argument is zero so that no
+ * cpufreq_notify_transition calls are initiated.
+ */
+extern unsigned int speedstep_get_freqs(enum speedstep_processor processor,
+ unsigned int *low_speed,
+ unsigned int *high_speed,
+ unsigned int *transition_latency,
+ void (*set_state) (unsigned int state));
diff --git a/drivers/cpufreq/speedstep-smi.c b/drivers/cpufreq/speedstep-smi.c
new file mode 100644
index 00000000000..c76ead3490b
--- /dev/null
+++ b/drivers/cpufreq/speedstep-smi.c
@@ -0,0 +1,464 @@
+/*
+ * Intel SpeedStep SMI driver.
+ *
+ * (C) 2003 Hiroshi Miura <miura@da-cha.org>
+ *
+ * Licensed under the terms of the GNU GPL License version 2.
+ *
+ */
+
+
+/*********************************************************************
+ * SPEEDSTEP - DEFINITIONS *
+ *********************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <asm/ist.h>
+
+#include "speedstep-lib.h"
+
+/* speedstep system management interface port/command.
+ *
+ * These parameters are got from IST-SMI BIOS call.
+ * If user gives it, these are used.
+ *
+ */
+static int smi_port;
+static int smi_cmd;
+static unsigned int smi_sig;
+
+/* info about the processor */
+static enum speedstep_processor speedstep_processor;
+
+/*
+ * There are only two frequency states for each processor. Values
+ * are in kHz for the time being.
+ */
+static struct cpufreq_frequency_table speedstep_freqs[] = {
+ {SPEEDSTEP_HIGH, 0},
+ {SPEEDSTEP_LOW, 0},
+ {0, CPUFREQ_TABLE_END},
+};
+
+#define GET_SPEEDSTEP_OWNER 0
+#define GET_SPEEDSTEP_STATE 1
+#define SET_SPEEDSTEP_STATE 2
+#define GET_SPEEDSTEP_FREQS 4
+
+/* how often shall the SMI call be tried if it failed, e.g. because
+ * of DMA activity going on? */
+#define SMI_TRIES 5
+
+/**
+ * speedstep_smi_ownership
+ */
+static int speedstep_smi_ownership(void)
+{
+ u32 command, result, magic, dummy;
+ u32 function = GET_SPEEDSTEP_OWNER;
+ unsigned char magic_data[] = "Copyright (c) 1999 Intel Corporation";
+
+ command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
+ magic = virt_to_phys(magic_data);
+
+ pr_debug("trying to obtain ownership with command %x at port %x\n",
+ command, smi_port);
+
+ __asm__ __volatile__(
+ "push %%ebp\n"
+ "out %%al, (%%dx)\n"
+ "pop %%ebp\n"
+ : "=D" (result),
+ "=a" (dummy), "=b" (dummy), "=c" (dummy), "=d" (dummy),
+ "=S" (dummy)
+ : "a" (command), "b" (function), "c" (0), "d" (smi_port),
+ "D" (0), "S" (magic)
+ : "memory"
+ );
+
+ pr_debug("result is %x\n", result);
+
+ return result;
+}
+
+/**
+ * speedstep_smi_get_freqs - get SpeedStep preferred & current freq.
+ * @low: the low frequency value is placed here
+ * @high: the high frequency value is placed here
+ *
+ * Only available on later SpeedStep-enabled systems, returns false results or
+ * even hangs [cf. bugme.osdl.org # 1422] on earlier systems. Empirical testing
+ * shows that the latter occurs if !(ist_info.event & 0xFFFF).
+ */
+static int speedstep_smi_get_freqs(unsigned int *low, unsigned int *high)
+{
+ u32 command, result = 0, edi, high_mhz, low_mhz, dummy;
+ u32 state = 0;
+ u32 function = GET_SPEEDSTEP_FREQS;
+
+ if (!(ist_info.event & 0xFFFF)) {
+ pr_debug("bug #1422 -- can't read freqs from BIOS\n");
+ return -ENODEV;
+ }
+
+ command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
+
+ pr_debug("trying to determine frequencies with command %x at port %x\n",
+ command, smi_port);
+
+ __asm__ __volatile__(
+ "push %%ebp\n"
+ "out %%al, (%%dx)\n"
+ "pop %%ebp"
+ : "=a" (result),
+ "=b" (high_mhz),
+ "=c" (low_mhz),
+ "=d" (state), "=D" (edi), "=S" (dummy)
+ : "a" (command),
+ "b" (function),
+ "c" (state),
+ "d" (smi_port), "S" (0), "D" (0)
+ );
+
+ pr_debug("result %x, low_freq %u, high_freq %u\n",
+ result, low_mhz, high_mhz);
+
+ /* abort if results are obviously incorrect... */
+ if ((high_mhz + low_mhz) < 600)
+ return -EINVAL;
+
+ *high = high_mhz * 1000;
+ *low = low_mhz * 1000;
+
+ return result;
+}
+
+/**
+ * speedstep_get_state - set the SpeedStep state
+ * @state: processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
+ *
+ */
+static int speedstep_get_state(void)
+{
+ u32 function = GET_SPEEDSTEP_STATE;
+ u32 result, state, edi, command, dummy;
+
+ command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
+
+ pr_debug("trying to determine current setting with command %x "
+ "at port %x\n", command, smi_port);
+
+ __asm__ __volatile__(
+ "push %%ebp\n"
+ "out %%al, (%%dx)\n"
+ "pop %%ebp\n"
+ : "=a" (result),
+ "=b" (state), "=D" (edi),
+ "=c" (dummy), "=d" (dummy), "=S" (dummy)
+ : "a" (command), "b" (function), "c" (0),
+ "d" (smi_port), "S" (0), "D" (0)
+ );
+
+ pr_debug("state is %x, result is %x\n", state, result);
+
+ return state & 1;
+}
+
+
+/**
+ * speedstep_set_state - set the SpeedStep state
+ * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
+ *
+ */
+static void speedstep_set_state(unsigned int state)
+{
+ unsigned int result = 0, command, new_state, dummy;
+ unsigned long flags;
+ unsigned int function = SET_SPEEDSTEP_STATE;
+ unsigned int retry = 0;
+
+ if (state > 0x1)
+ return;
+
+ /* Disable IRQs */
+ local_irq_save(flags);
+
+ command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
+
+ pr_debug("trying to set frequency to state %u "
+ "with command %x at port %x\n",
+ state, command, smi_port);
+
+ do {
+ if (retry) {
+ pr_debug("retry %u, previous result %u, waiting...\n",
+ retry, result);
+ mdelay(retry * 50);
+ }
+ retry++;
+ __asm__ __volatile__(
+ "push %%ebp\n"
+ "out %%al, (%%dx)\n"
+ "pop %%ebp"
+ : "=b" (new_state), "=D" (result),
+ "=c" (dummy), "=a" (dummy),
+ "=d" (dummy), "=S" (dummy)
+ : "a" (command), "b" (function), "c" (state),
+ "d" (smi_port), "S" (0), "D" (0)
+ );
+ } while ((new_state != state) && (retry <= SMI_TRIES));
+
+ /* enable IRQs */
+ local_irq_restore(flags);
+
+ if (new_state == state)
+ pr_debug("change to %u MHz succeeded after %u tries "
+ "with result %u\n",
+ (speedstep_freqs[new_state].frequency / 1000),
+ retry, result);
+ else
+ printk(KERN_ERR "cpufreq: change to state %u "
+ "failed with new_state %u and result %u\n",
+ state, new_state, result);
+
+ return;
+}
+
+
+/**
+ * speedstep_target - set a new CPUFreq policy
+ * @policy: new policy
+ * @target_freq: new freq
+ * @relation:
+ *
+ * Sets a new CPUFreq policy/freq.
+ */
+static int speedstep_target(struct cpufreq_policy *policy,
+ unsigned int target_freq, unsigned int relation)
+{
+ unsigned int newstate = 0;
+ struct cpufreq_freqs freqs;
+
+ if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0],
+ target_freq, relation, &newstate))
+ return -EINVAL;
+
+ freqs.old = speedstep_freqs[speedstep_get_state()].frequency;
+ freqs.new = speedstep_freqs[newstate].frequency;
+ freqs.cpu = 0; /* speedstep.c is UP only driver */
+
+ if (freqs.old == freqs.new)
+ return 0;
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ speedstep_set_state(newstate);
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+ return 0;
+}
+
+
+/**
+ * speedstep_verify - verifies a new CPUFreq policy
+ * @policy: new policy
+ *
+ * Limit must be within speedstep_low_freq and speedstep_high_freq, with
+ * at least one border included.
+ */
+static int speedstep_verify(struct cpufreq_policy *policy)
+{
+ return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
+}
+
+
+static int speedstep_cpu_init(struct cpufreq_policy *policy)
+{
+ int result;
+ unsigned int speed, state;
+ unsigned int *low, *high;
+
+ /* capability check */
+ if (policy->cpu != 0)
+ return -ENODEV;
+
+ result = speedstep_smi_ownership();
+ if (result) {
+ pr_debug("fails in acquiring ownership of a SMI interface.\n");
+ return -EINVAL;
+ }
+
+ /* detect low and high frequency */
+ low = &speedstep_freqs[SPEEDSTEP_LOW].frequency;
+ high = &speedstep_freqs[SPEEDSTEP_HIGH].frequency;
+
+ result = speedstep_smi_get_freqs(low, high);
+ if (result) {
+ /* fall back to speedstep_lib.c dection mechanism:
+ * try both states out */
+ pr_debug("could not detect low and high frequencies "
+ "by SMI call.\n");
+ result = speedstep_get_freqs(speedstep_processor,
+ low, high,
+ NULL,
+ &speedstep_set_state);
+
+ if (result) {
+ pr_debug("could not detect two different speeds"
+ " -- aborting.\n");
+ return result;
+ } else
+ pr_debug("workaround worked.\n");
+ }
+
+ /* get current speed setting */
+ state = speedstep_get_state();
+ speed = speedstep_freqs[state].frequency;
+
+ pr_debug("currently at %s speed setting - %i MHz\n",
+ (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency)
+ ? "low" : "high",
+ (speed / 1000));
+
+ /* cpuinfo and default policy values */
+ policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+ policy->cur = speed;
+
+ result = cpufreq_frequency_table_cpuinfo(policy, speedstep_freqs);
+ if (result)
+ return result;
+
+ cpufreq_frequency_table_get_attr(speedstep_freqs, policy->cpu);
+
+ return 0;
+}
+
+static int speedstep_cpu_exit(struct cpufreq_policy *policy)
+{
+ cpufreq_frequency_table_put_attr(policy->cpu);
+ return 0;
+}
+
+static unsigned int speedstep_get(unsigned int cpu)
+{
+ if (cpu)
+ return -ENODEV;
+ return speedstep_get_frequency(speedstep_processor);
+}
+
+
+static int speedstep_resume(struct cpufreq_policy *policy)
+{
+ int result = speedstep_smi_ownership();
+
+ if (result)
+ pr_debug("fails in re-acquiring ownership of a SMI interface.\n");
+
+ return result;
+}
+
+static struct freq_attr *speedstep_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+static struct cpufreq_driver speedstep_driver = {
+ .name = "speedstep-smi",
+ .verify = speedstep_verify,
+ .target = speedstep_target,
+ .init = speedstep_cpu_init,
+ .exit = speedstep_cpu_exit,
+ .get = speedstep_get,
+ .resume = speedstep_resume,
+ .owner = THIS_MODULE,
+ .attr = speedstep_attr,
+};
+
+/**
+ * speedstep_init - initializes the SpeedStep CPUFreq driver
+ *
+ * Initializes the SpeedStep support. Returns -ENODEV on unsupported
+ * BIOS, -EINVAL on problems during initiatization, and zero on
+ * success.
+ */
+static int __init speedstep_init(void)
+{
+ speedstep_processor = speedstep_detect_processor();
+
+ switch (speedstep_processor) {
+ case SPEEDSTEP_CPU_PIII_T:
+ case SPEEDSTEP_CPU_PIII_C:
+ case SPEEDSTEP_CPU_PIII_C_EARLY:
+ break;
+ default:
+ speedstep_processor = 0;
+ }
+
+ if (!speedstep_processor) {
+ pr_debug("No supported Intel CPU detected.\n");
+ return -ENODEV;
+ }
+
+ pr_debug("signature:0x%.8ulx, command:0x%.8ulx, "
+ "event:0x%.8ulx, perf_level:0x%.8ulx.\n",
+ ist_info.signature, ist_info.command,
+ ist_info.event, ist_info.perf_level);
+
+ /* Error if no IST-SMI BIOS or no PARM
+ sig= 'ISGE' aka 'Intel Speedstep Gate E' */
+ if ((ist_info.signature != 0x47534943) && (
+ (smi_port == 0) || (smi_cmd == 0)))
+ return -ENODEV;
+
+ if (smi_sig == 1)
+ smi_sig = 0x47534943;
+ else
+ smi_sig = ist_info.signature;
+
+ /* setup smi_port from MODLULE_PARM or BIOS */
+ if ((smi_port > 0xff) || (smi_port < 0))
+ return -EINVAL;
+ else if (smi_port == 0)
+ smi_port = ist_info.command & 0xff;
+
+ if ((smi_cmd > 0xff) || (smi_cmd < 0))
+ return -EINVAL;
+ else if (smi_cmd == 0)
+ smi_cmd = (ist_info.command >> 16) & 0xff;
+
+ return cpufreq_register_driver(&speedstep_driver);
+}
+
+
+/**
+ * speedstep_exit - unregisters SpeedStep support
+ *
+ * Unregisters SpeedStep support.
+ */
+static void __exit speedstep_exit(void)
+{
+ cpufreq_unregister_driver(&speedstep_driver);
+}
+
+module_param(smi_port, int, 0444);
+module_param(smi_cmd, int, 0444);
+module_param(smi_sig, uint, 0444);
+
+MODULE_PARM_DESC(smi_port, "Override the BIOS-given IST port with this value "
+ "-- Intel's default setting is 0xb2");
+MODULE_PARM_DESC(smi_cmd, "Override the BIOS-given IST command with this value "
+ "-- Intel's default setting is 0x82");
+MODULE_PARM_DESC(smi_sig, "Set to 1 to fake the IST signature when using the "
+ "SMI interface.");
+
+MODULE_AUTHOR("Hiroshi Miura");
+MODULE_DESCRIPTION("Speedstep driver for IST applet SMI interface.");
+MODULE_LICENSE("GPL");
+
+module_init(speedstep_init);
+module_exit(speedstep_exit);
diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
index 6b396759e7f..8a781540590 100644
--- a/drivers/dma/fsldma.c
+++ b/drivers/dma/fsldma.c
@@ -1448,7 +1448,7 @@ static const struct of_device_id fsldma_of_ids[] = {
{}
};
-static struct of_platform_driver fsldma_of_driver = {
+static struct platform_driver fsldma_of_driver = {
.driver = {
.name = "fsl-elo-dma",
.owner = THIS_MODULE,
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index 31e71c4fc83..9a8bebcf6b1 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -211,8 +211,6 @@ static int amd64_get_scrub_rate(struct mem_ctl_info *mci)
scrubval = scrubval & 0x001F;
- amd64_debug("pci-read, sdram scrub control value: %d\n", scrubval);
-
for (i = 0; i < ARRAY_SIZE(scrubrates); i++) {
if (scrubrates[i].scrubval == scrubval) {
retval = scrubrates[i].bandwidth;
@@ -933,25 +931,74 @@ static int k8_early_channel_count(struct amd64_pvt *pvt)
/* On F10h and later ErrAddr is MC4_ADDR[47:1] */
static u64 get_error_address(struct mce *m)
{
+ struct cpuinfo_x86 *c = &boot_cpu_data;
+ u64 addr;
u8 start_bit = 1;
u8 end_bit = 47;
- if (boot_cpu_data.x86 == 0xf) {
+ if (c->x86 == 0xf) {
start_bit = 3;
end_bit = 39;
}
- return m->addr & GENMASK(start_bit, end_bit);
+ addr = m->addr & GENMASK(start_bit, end_bit);
+
+ /*
+ * Erratum 637 workaround
+ */
+ if (c->x86 == 0x15) {
+ struct amd64_pvt *pvt;
+ u64 cc6_base, tmp_addr;
+ u32 tmp;
+ u8 mce_nid, intlv_en;
+
+ if ((addr & GENMASK(24, 47)) >> 24 != 0x00fdf7)
+ return addr;
+
+ mce_nid = amd_get_nb_id(m->extcpu);
+ pvt = mcis[mce_nid]->pvt_info;
+
+ amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_LIM, &tmp);
+ intlv_en = tmp >> 21 & 0x7;
+
+ /* add [47:27] + 3 trailing bits */
+ cc6_base = (tmp & GENMASK(0, 20)) << 3;
+
+ /* reverse and add DramIntlvEn */
+ cc6_base |= intlv_en ^ 0x7;
+
+ /* pin at [47:24] */
+ cc6_base <<= 24;
+
+ if (!intlv_en)
+ return cc6_base | (addr & GENMASK(0, 23));
+
+ amd64_read_pci_cfg(pvt->F1, DRAM_LOCAL_NODE_BASE, &tmp);
+
+ /* faster log2 */
+ tmp_addr = (addr & GENMASK(12, 23)) << __fls(intlv_en + 1);
+
+ /* OR DramIntlvSel into bits [14:12] */
+ tmp_addr |= (tmp & GENMASK(21, 23)) >> 9;
+
+ /* add remaining [11:0] bits from original MC4_ADDR */
+ tmp_addr |= addr & GENMASK(0, 11);
+
+ return cc6_base | tmp_addr;
+ }
+
+ return addr;
}
static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range)
{
+ struct cpuinfo_x86 *c = &boot_cpu_data;
int off = range << 3;
amd64_read_pci_cfg(pvt->F1, DRAM_BASE_LO + off, &pvt->ranges[range].base.lo);
amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_LO + off, &pvt->ranges[range].lim.lo);
- if (boot_cpu_data.x86 == 0xf)
+ if (c->x86 == 0xf)
return;
if (!dram_rw(pvt, range))
@@ -959,6 +1006,31 @@ static void read_dram_base_limit_regs(struct amd64_pvt *pvt, unsigned range)
amd64_read_pci_cfg(pvt->F1, DRAM_BASE_HI + off, &pvt->ranges[range].base.hi);
amd64_read_pci_cfg(pvt->F1, DRAM_LIMIT_HI + off, &pvt->ranges[range].lim.hi);
+
+ /* Factor in CC6 save area by reading dst node's limit reg */
+ if (c->x86 == 0x15) {
+ struct pci_dev *f1 = NULL;
+ u8 nid = dram_dst_node(pvt, range);
+ u32 llim;
+
+ f1 = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0x18 + nid, 1));
+ if (WARN_ON(!f1))
+ return;
+
+ amd64_read_pci_cfg(f1, DRAM_LOCAL_NODE_LIM, &llim);
+
+ pvt->ranges[range].lim.lo &= GENMASK(0, 15);
+
+ /* {[39:27],111b} */
+ pvt->ranges[range].lim.lo |= ((llim & 0x1fff) << 3 | 0x7) << 16;
+
+ pvt->ranges[range].lim.hi &= GENMASK(0, 7);
+
+ /* [47:40] */
+ pvt->ranges[range].lim.hi |= llim >> 13;
+
+ pci_dev_put(f1);
+ }
}
static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr,
@@ -1403,12 +1475,8 @@ static int f1x_match_to_this_node(struct amd64_pvt *pvt, unsigned range,
return -EINVAL;
}
- if (intlv_en &&
- (intlv_sel != ((sys_addr >> 12) & intlv_en))) {
- amd64_warn("Botched intlv bits, en: 0x%x, sel: 0x%x\n",
- intlv_en, intlv_sel);
+ if (intlv_en && (intlv_sel != ((sys_addr >> 12) & intlv_en)))
return -EINVAL;
- }
sys_addr = f1x_swap_interleaved_region(pvt, sys_addr);
diff --git a/drivers/edac/amd64_edac.h b/drivers/edac/amd64_edac.h
index 11be36a311e..9a666cb985b 100644
--- a/drivers/edac/amd64_edac.h
+++ b/drivers/edac/amd64_edac.h
@@ -196,6 +196,9 @@
#define DCT_CFG_SEL 0x10C
+#define DRAM_LOCAL_NODE_BASE 0x120
+#define DRAM_LOCAL_NODE_LIM 0x124
+
#define DRAM_BASE_HI 0x140
#define DRAM_LIMIT_HI 0x144
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index 26343fd4659..29ffa350bfb 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -458,13 +458,13 @@ static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci,
return -EINVAL;
new_bw = mci->set_sdram_scrub_rate(mci, bandwidth);
- if (new_bw >= 0) {
- edac_printk(KERN_DEBUG, EDAC_MC, "Scrub rate set to %d\n", new_bw);
- return count;
+ if (new_bw < 0) {
+ edac_printk(KERN_WARNING, EDAC_MC,
+ "Error setting scrub rate to: %lu\n", bandwidth);
+ return -EINVAL;
}
- edac_printk(KERN_DEBUG, EDAC_MC, "Error setting scrub rate to: %lu\n", bandwidth);
- return -EINVAL;
+ return count;
}
/*
@@ -483,7 +483,6 @@ static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data)
return bandwidth;
}
- edac_printk(KERN_DEBUG, EDAC_MC, "Read scrub rate: %d\n", bandwidth);
return sprintf(data, "%d\n", bandwidth);
}
diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c
index c1f0045ceb8..af8e7b1aa29 100644
--- a/drivers/edac/ppc4xx_edac.c
+++ b/drivers/edac/ppc4xx_edac.c
@@ -1019,7 +1019,7 @@ ppc4xx_edac_mc_init(struct mem_ctl_info *mci,
struct ppc4xx_edac_pdata *pdata = NULL;
const struct device_node *np = op->dev.of_node;
- if (op->dev.of_match == NULL)
+ if (of_match_device(ppc4xx_edac_match, &op->dev) == NULL)
return -EINVAL;
/* Initial driver pointers and private data */
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index f903d7b6f34..23d1468ad25 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -2199,7 +2199,6 @@ static int ohci_set_config_rom(struct fw_card *card,
{
struct fw_ohci *ohci;
unsigned long flags;
- int ret = -EBUSY;
__be32 *next_config_rom;
dma_addr_t uninitialized_var(next_config_rom_bus);
@@ -2240,22 +2239,37 @@ static int ohci_set_config_rom(struct fw_card *card,
spin_lock_irqsave(&ohci->lock, flags);
+ /*
+ * If there is not an already pending config_rom update,
+ * push our new allocation into the ohci->next_config_rom
+ * and then mark the local variable as null so that we
+ * won't deallocate the new buffer.
+ *
+ * OTOH, if there is a pending config_rom update, just
+ * use that buffer with the new config_rom data, and
+ * let this routine free the unused DMA allocation.
+ */
+
if (ohci->next_config_rom == NULL) {
ohci->next_config_rom = next_config_rom;
ohci->next_config_rom_bus = next_config_rom_bus;
+ next_config_rom = NULL;
+ }
- copy_config_rom(ohci->next_config_rom, config_rom, length);
+ copy_config_rom(ohci->next_config_rom, config_rom, length);
- ohci->next_header = config_rom[0];
- ohci->next_config_rom[0] = 0;
+ ohci->next_header = config_rom[0];
+ ohci->next_config_rom[0] = 0;
- reg_write(ohci, OHCI1394_ConfigROMmap,
- ohci->next_config_rom_bus);
- ret = 0;
- }
+ reg_write(ohci, OHCI1394_ConfigROMmap, ohci->next_config_rom_bus);
spin_unlock_irqrestore(&ohci->lock, flags);
+ /* If we didn't use the DMA allocation, delete it. */
+ if (next_config_rom != NULL)
+ dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
+ next_config_rom, next_config_rom_bus);
+
/*
* Now initiate a bus reset to have the changes take
* effect. We clean up the old config rom memory and DMA
@@ -2263,13 +2277,10 @@ static int ohci_set_config_rom(struct fw_card *card,
* controller could need to access it before the bus reset
* takes effect.
*/
- if (ret == 0)
- fw_schedule_bus_reset(&ohci->card, true, true);
- else
- dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
- next_config_rom, next_config_rom_bus);
- return ret;
+ fw_schedule_bus_reset(&ohci->card, true, true);
+
+ return 0;
}
static void ohci_send_request(struct fw_card *card, struct fw_packet *packet)
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index b3a25a55ba2..efba163595d 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -157,4 +157,6 @@ config SIGMA
If unsure, say N here. Drivers that need these helpers will select
this option automatically.
+source "drivers/firmware/google/Kconfig"
+
endmenu
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 00bb0b80a79..47338c97912 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -13,3 +13,5 @@ obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o
obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o
obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
obj-$(CONFIG_SIGMA) += sigma.o
+
+obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
diff --git a/drivers/firmware/edd.c b/drivers/firmware/edd.c
index 96c25d93eed..f1b7f659d3c 100644
--- a/drivers/firmware/edd.c
+++ b/drivers/firmware/edd.c
@@ -531,8 +531,8 @@ static int
edd_has_edd30(struct edd_device *edev)
{
struct edd_info *info;
- int i, nonzero_path = 0;
- char c;
+ int i;
+ u8 csum = 0;
if (!edev)
return 0;
@@ -544,16 +544,16 @@ edd_has_edd30(struct edd_device *edev)
return 0;
}
- for (i = 30; i <= 73; i++) {
- c = *(((uint8_t *) info) + i + 4);
- if (c) {
- nonzero_path++;
- break;
- }
- }
- if (!nonzero_path) {
+
+ /* We support only T13 spec */
+ if (info->params.device_path_info_length != 44)
+ return 0;
+
+ for (i = 30; i < info->params.device_path_info_length + 30; i++)
+ csum += *(((u8 *)&info->params) + i);
+
+ if (csum)
return 0;
- }
return 1;
}
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index ff0c373e3bb..a2d2f1f0d4f 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -677,8 +677,8 @@ create_efivars_bin_attributes(struct efivars *efivars)
return 0;
out_free:
- kfree(efivars->new_var);
- efivars->new_var = NULL;
+ kfree(efivars->del_var);
+ efivars->del_var = NULL;
kfree(efivars->new_var);
efivars->new_var = NULL;
return error;
@@ -803,6 +803,8 @@ efivars_init(void)
ops.set_variable = efi.set_variable;
ops.get_next_variable = efi.get_next_variable;
error = register_efivars(&__efivars, &ops, efi_kobj);
+ if (error)
+ goto err_put;
/* Don't forget the systab entry */
error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group);
@@ -810,18 +812,25 @@ efivars_init(void)
printk(KERN_ERR
"efivars: Sysfs attribute export failed with error %d.\n",
error);
- unregister_efivars(&__efivars);
- kobject_put(efi_kobj);
+ goto err_unregister;
}
+ return 0;
+
+err_unregister:
+ unregister_efivars(&__efivars);
+err_put:
+ kobject_put(efi_kobj);
return error;
}
static void __exit
efivars_exit(void)
{
- unregister_efivars(&__efivars);
- kobject_put(efi_kobj);
+ if (efi_enabled) {
+ unregister_efivars(&__efivars);
+ kobject_put(efi_kobj);
+ }
}
module_init(efivars_init);
diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig
new file mode 100644
index 00000000000..87096b6ca5c
--- /dev/null
+++ b/drivers/firmware/google/Kconfig
@@ -0,0 +1,31 @@
+config GOOGLE_FIRMWARE
+ bool "Google Firmware Drivers"
+ depends on X86
+ default n
+ help
+ These firmware drivers are used by Google's servers. They are
+ only useful if you are working directly on one of their
+ proprietary servers. If in doubt, say "N".
+
+menu "Google Firmware Drivers"
+ depends on GOOGLE_FIRMWARE
+
+config GOOGLE_SMI
+ tristate "SMI interface for Google platforms"
+ depends on ACPI && DMI
+ select EFI_VARS
+ help
+ Say Y here if you want to enable SMI callbacks for Google
+ platforms. This provides an interface for writing to and
+ clearing the EFI event log and reading and writing NVRAM
+ variables.
+
+config GOOGLE_MEMCONSOLE
+ tristate "Firmware Memory Console"
+ depends on DMI
+ help
+ This option enables the kernel to search for a firmware log in
+ the EBDA on Google servers. If found, this log is exported to
+ userland in the file /sys/firmware/log.
+
+endmenu
diff --git a/drivers/firmware/google/Makefile b/drivers/firmware/google/Makefile
new file mode 100644
index 00000000000..54a294e3cb6
--- /dev/null
+++ b/drivers/firmware/google/Makefile
@@ -0,0 +1,3 @@
+
+obj-$(CONFIG_GOOGLE_SMI) += gsmi.o
+obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o
diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c
new file mode 100644
index 00000000000..fa7f0b3e81d
--- /dev/null
+++ b/drivers/firmware/google/gsmi.c
@@ -0,0 +1,940 @@
+/*
+ * Copyright 2010 Google Inc. All Rights Reserved.
+ * Author: dlaurie@google.com (Duncan Laurie)
+ *
+ * Re-worked to expose sysfs APIs by mikew@google.com (Mike Waychison)
+ *
+ * EFI SMI interface for Google platforms
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/ioctl.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/dmi.h>
+#include <linux/kdebug.h>
+#include <linux/reboot.h>
+#include <linux/efi.h>
+
+#define GSMI_SHUTDOWN_CLEAN 0 /* Clean Shutdown */
+/* TODO(mikew@google.com): Tie in HARDLOCKUP_DETECTOR with NMIWDT */
+#define GSMI_SHUTDOWN_NMIWDT 1 /* NMI Watchdog */
+#define GSMI_SHUTDOWN_PANIC 2 /* Panic */
+#define GSMI_SHUTDOWN_OOPS 3 /* Oops */
+#define GSMI_SHUTDOWN_DIE 4 /* Die -- No longer meaningful */
+#define GSMI_SHUTDOWN_MCE 5 /* Machine Check */
+#define GSMI_SHUTDOWN_SOFTWDT 6 /* Software Watchdog */
+#define GSMI_SHUTDOWN_MBE 7 /* Uncorrected ECC */
+#define GSMI_SHUTDOWN_TRIPLE 8 /* Triple Fault */
+
+#define DRIVER_VERSION "1.0"
+#define GSMI_GUID_SIZE 16
+#define GSMI_BUF_SIZE 1024
+#define GSMI_BUF_ALIGN sizeof(u64)
+#define GSMI_CALLBACK 0xef
+
+/* SMI return codes */
+#define GSMI_SUCCESS 0x00
+#define GSMI_UNSUPPORTED2 0x03
+#define GSMI_LOG_FULL 0x0b
+#define GSMI_VAR_NOT_FOUND 0x0e
+#define GSMI_HANDSHAKE_SPIN 0x7d
+#define GSMI_HANDSHAKE_CF 0x7e
+#define GSMI_HANDSHAKE_NONE 0x7f
+#define GSMI_INVALID_PARAMETER 0x82
+#define GSMI_UNSUPPORTED 0x83
+#define GSMI_BUFFER_TOO_SMALL 0x85
+#define GSMI_NOT_READY 0x86
+#define GSMI_DEVICE_ERROR 0x87
+#define GSMI_NOT_FOUND 0x8e
+
+#define QUIRKY_BOARD_HASH 0x78a30a50
+
+/* Internally used commands passed to the firmware */
+#define GSMI_CMD_GET_NVRAM_VAR 0x01
+#define GSMI_CMD_GET_NEXT_VAR 0x02
+#define GSMI_CMD_SET_NVRAM_VAR 0x03
+#define GSMI_CMD_SET_EVENT_LOG 0x08
+#define GSMI_CMD_CLEAR_EVENT_LOG 0x09
+#define GSMI_CMD_CLEAR_CONFIG 0x20
+#define GSMI_CMD_HANDSHAKE_TYPE 0xC1
+
+/* Magic entry type for kernel events */
+#define GSMI_LOG_ENTRY_TYPE_KERNEL 0xDEAD
+
+/* SMI buffers must be in 32bit physical address space */
+struct gsmi_buf {
+ u8 *start; /* start of buffer */
+ size_t length; /* length of buffer */
+ dma_addr_t handle; /* dma allocation handle */
+ u32 address; /* physical address of buffer */
+};
+
+struct gsmi_device {
+ struct platform_device *pdev; /* platform device */
+ struct gsmi_buf *name_buf; /* variable name buffer */
+ struct gsmi_buf *data_buf; /* generic data buffer */
+ struct gsmi_buf *param_buf; /* parameter buffer */
+ spinlock_t lock; /* serialize access to SMIs */
+ u16 smi_cmd; /* SMI command port */
+ int handshake_type; /* firmware handler interlock type */
+ struct dma_pool *dma_pool; /* DMA buffer pool */
+} gsmi_dev;
+
+/* Packed structures for communicating with the firmware */
+struct gsmi_nvram_var_param {
+ efi_guid_t guid;
+ u32 name_ptr;
+ u32 attributes;
+ u32 data_len;
+ u32 data_ptr;
+} __packed;
+
+struct gsmi_get_next_var_param {
+ u8 guid[GSMI_GUID_SIZE];
+ u32 name_ptr;
+ u32 name_len;
+} __packed;
+
+struct gsmi_set_eventlog_param {
+ u32 data_ptr;
+ u32 data_len;
+ u32 type;
+} __packed;
+
+/* Event log formats */
+struct gsmi_log_entry_type_1 {
+ u16 type;
+ u32 instance;
+} __packed;
+
+
+/*
+ * Some platforms don't have explicit SMI handshake
+ * and need to wait for SMI to complete.
+ */
+#define GSMI_DEFAULT_SPINCOUNT 0x10000
+static unsigned int spincount = GSMI_DEFAULT_SPINCOUNT;
+module_param(spincount, uint, 0600);
+MODULE_PARM_DESC(spincount,
+ "The number of loop iterations to use when using the spin handshake.");
+
+static struct gsmi_buf *gsmi_buf_alloc(void)
+{
+ struct gsmi_buf *smibuf;
+
+ smibuf = kzalloc(sizeof(*smibuf), GFP_KERNEL);
+ if (!smibuf) {
+ printk(KERN_ERR "gsmi: out of memory\n");
+ return NULL;
+ }
+
+ /* allocate buffer in 32bit address space */
+ smibuf->start = dma_pool_alloc(gsmi_dev.dma_pool, GFP_KERNEL,
+ &smibuf->handle);
+ if (!smibuf->start) {
+ printk(KERN_ERR "gsmi: failed to allocate name buffer\n");
+ kfree(smibuf);
+ return NULL;
+ }
+
+ /* fill in the buffer handle */
+ smibuf->length = GSMI_BUF_SIZE;
+ smibuf->address = (u32)virt_to_phys(smibuf->start);
+
+ return smibuf;
+}
+
+static void gsmi_buf_free(struct gsmi_buf *smibuf)
+{
+ if (smibuf) {
+ if (smibuf->start)
+ dma_pool_free(gsmi_dev.dma_pool, smibuf->start,
+ smibuf->handle);
+ kfree(smibuf);
+ }
+}
+
+/*
+ * Make a call to gsmi func(sub). GSMI error codes are translated to
+ * in-kernel errnos (0 on success, -ERRNO on error).
+ */
+static int gsmi_exec(u8 func, u8 sub)
+{
+ u16 cmd = (sub << 8) | func;
+ u16 result = 0;
+ int rc = 0;
+
+ /*
+ * AH : Subfunction number
+ * AL : Function number
+ * EBX : Parameter block address
+ * DX : SMI command port
+ *
+ * Three protocols here. See also the comment in gsmi_init().
+ */
+ if (gsmi_dev.handshake_type == GSMI_HANDSHAKE_CF) {
+ /*
+ * If handshake_type == HANDSHAKE_CF then set CF on the
+ * way in and wait for the handler to clear it; this avoids
+ * corrupting register state on those chipsets which have
+ * a delay between writing the SMI trigger register and
+ * entering SMM.
+ */
+ asm volatile (
+ "stc\n"
+ "outb %%al, %%dx\n"
+ "1: jc 1b\n"
+ : "=a" (result)
+ : "0" (cmd),
+ "d" (gsmi_dev.smi_cmd),
+ "b" (gsmi_dev.param_buf->address)
+ : "memory", "cc"
+ );
+ } else if (gsmi_dev.handshake_type == GSMI_HANDSHAKE_SPIN) {
+ /*
+ * If handshake_type == HANDSHAKE_SPIN we spin a
+ * hundred-ish usecs to ensure the SMI has triggered.
+ */
+ asm volatile (
+ "outb %%al, %%dx\n"
+ "1: loop 1b\n"
+ : "=a" (result)
+ : "0" (cmd),
+ "d" (gsmi_dev.smi_cmd),
+ "b" (gsmi_dev.param_buf->address),
+ "c" (spincount)
+ : "memory", "cc"
+ );
+ } else {
+ /*
+ * If handshake_type == HANDSHAKE_NONE we do nothing;
+ * either we don't need to or it's legacy firmware that
+ * doesn't understand the CF protocol.
+ */
+ asm volatile (
+ "outb %%al, %%dx\n\t"
+ : "=a" (result)
+ : "0" (cmd),
+ "d" (gsmi_dev.smi_cmd),
+ "b" (gsmi_dev.param_buf->address)
+ : "memory", "cc"
+ );
+ }
+
+ /* check return code from SMI handler */
+ switch (result) {
+ case GSMI_SUCCESS:
+ break;
+ case GSMI_VAR_NOT_FOUND:
+ /* not really an error, but let the caller know */
+ rc = 1;
+ break;
+ case GSMI_INVALID_PARAMETER:
+ printk(KERN_ERR "gsmi: exec 0x%04x: Invalid parameter\n", cmd);
+ rc = -EINVAL;
+ break;
+ case GSMI_BUFFER_TOO_SMALL:
+ printk(KERN_ERR "gsmi: exec 0x%04x: Buffer too small\n", cmd);
+ rc = -ENOMEM;
+ break;
+ case GSMI_UNSUPPORTED:
+ case GSMI_UNSUPPORTED2:
+ if (sub != GSMI_CMD_HANDSHAKE_TYPE)
+ printk(KERN_ERR "gsmi: exec 0x%04x: Not supported\n",
+ cmd);
+ rc = -ENOSYS;
+ break;
+ case GSMI_NOT_READY:
+ printk(KERN_ERR "gsmi: exec 0x%04x: Not ready\n", cmd);
+ rc = -EBUSY;
+ break;
+ case GSMI_DEVICE_ERROR:
+ printk(KERN_ERR "gsmi: exec 0x%04x: Device error\n", cmd);
+ rc = -EFAULT;
+ break;
+ case GSMI_NOT_FOUND:
+ printk(KERN_ERR "gsmi: exec 0x%04x: Data not found\n", cmd);
+ rc = -ENOENT;
+ break;
+ case GSMI_LOG_FULL:
+ printk(KERN_ERR "gsmi: exec 0x%04x: Log full\n", cmd);
+ rc = -ENOSPC;
+ break;
+ case GSMI_HANDSHAKE_CF:
+ case GSMI_HANDSHAKE_SPIN:
+ case GSMI_HANDSHAKE_NONE:
+ rc = result;
+ break;
+ default:
+ printk(KERN_ERR "gsmi: exec 0x%04x: Unknown error 0x%04x\n",
+ cmd, result);
+ rc = -ENXIO;
+ }
+
+ return rc;
+}
+
+/* Return the number of unicode characters in data */
+static size_t
+utf16_strlen(efi_char16_t *data, unsigned long maxlength)
+{
+ unsigned long length = 0;
+
+ while (*data++ != 0 && length < maxlength)
+ length++;
+ return length;
+}
+
+static efi_status_t gsmi_get_variable(efi_char16_t *name,
+ efi_guid_t *vendor, u32 *attr,
+ unsigned long *data_size,
+ void *data)
+{
+ struct gsmi_nvram_var_param param = {
+ .name_ptr = gsmi_dev.name_buf->address,
+ .data_ptr = gsmi_dev.data_buf->address,
+ .data_len = (u32)*data_size,
+ };
+ efi_status_t ret = EFI_SUCCESS;
+ unsigned long flags;
+ size_t name_len = utf16_strlen(name, GSMI_BUF_SIZE / 2);
+ int rc;
+
+ if (name_len >= GSMI_BUF_SIZE / 2)
+ return EFI_BAD_BUFFER_SIZE;
+
+ spin_lock_irqsave(&gsmi_dev.lock, flags);
+
+ /* Vendor guid */
+ memcpy(&param.guid, vendor, sizeof(param.guid));
+
+ /* variable name, already in UTF-16 */
+ memset(gsmi_dev.name_buf->start, 0, gsmi_dev.name_buf->length);
+ memcpy(gsmi_dev.name_buf->start, name, name_len * 2);
+
+ /* data pointer */
+ memset(gsmi_dev.data_buf->start, 0, gsmi_dev.data_buf->length);
+
+ /* parameter buffer */
+ memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length);
+ memcpy(gsmi_dev.param_buf->start, &param, sizeof(param));
+
+ rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_GET_NVRAM_VAR);
+ if (rc < 0) {
+ printk(KERN_ERR "gsmi: Get Variable failed\n");
+ ret = EFI_LOAD_ERROR;
+ } else if (rc == 1) {
+ /* variable was not found */
+ ret = EFI_NOT_FOUND;
+ } else {
+ /* Get the arguments back */
+ memcpy(&param, gsmi_dev.param_buf->start, sizeof(param));
+
+ /* The size reported is the min of all of our buffers */
+ *data_size = min(*data_size, gsmi_dev.data_buf->length);
+ *data_size = min_t(unsigned long, *data_size, param.data_len);
+
+ /* Copy data back to return buffer. */
+ memcpy(data, gsmi_dev.data_buf->start, *data_size);
+
+ /* All variables are have the following attributes */
+ *attr = EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS;
+ }
+
+ spin_unlock_irqrestore(&gsmi_dev.lock, flags);
+
+ return ret;
+}
+
+static efi_status_t gsmi_get_next_variable(unsigned long *name_size,
+ efi_char16_t *name,
+ efi_guid_t *vendor)
+{
+ struct gsmi_get_next_var_param param = {
+ .name_ptr = gsmi_dev.name_buf->address,
+ .name_len = gsmi_dev.name_buf->length,
+ };
+ efi_status_t ret = EFI_SUCCESS;
+ int rc;
+ unsigned long flags;
+
+ /* For the moment, only support buffers that exactly match in size */
+ if (*name_size != GSMI_BUF_SIZE)
+ return EFI_BAD_BUFFER_SIZE;
+
+ /* Let's make sure the thing is at least null-terminated */
+ if (utf16_strlen(name, GSMI_BUF_SIZE / 2) == GSMI_BUF_SIZE / 2)
+ return EFI_INVALID_PARAMETER;
+
+ spin_lock_irqsave(&gsmi_dev.lock, flags);
+
+ /* guid */
+ memcpy(&param.guid, vendor, sizeof(param.guid));
+
+ /* variable name, already in UTF-16 */
+ memcpy(gsmi_dev.name_buf->start, name, *name_size);
+
+ /* parameter buffer */
+ memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length);
+ memcpy(gsmi_dev.param_buf->start, &param, sizeof(param));
+
+ rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_GET_NEXT_VAR);
+ if (rc < 0) {
+ printk(KERN_ERR "gsmi: Get Next Variable Name failed\n");
+ ret = EFI_LOAD_ERROR;
+ } else if (rc == 1) {
+ /* variable not found -- end of list */
+ ret = EFI_NOT_FOUND;
+ } else {
+ /* copy variable data back to return buffer */
+ memcpy(&param, gsmi_dev.param_buf->start, sizeof(param));
+
+ /* Copy the name back */
+ memcpy(name, gsmi_dev.name_buf->start, GSMI_BUF_SIZE);
+ *name_size = utf16_strlen(name, GSMI_BUF_SIZE / 2) * 2;
+
+ /* copy guid to return buffer */
+ memcpy(vendor, &param.guid, sizeof(param.guid));
+ ret = EFI_SUCCESS;
+ }
+
+ spin_unlock_irqrestore(&gsmi_dev.lock, flags);
+
+ return ret;
+}
+
+static efi_status_t gsmi_set_variable(efi_char16_t *name,
+ efi_guid_t *vendor,
+ unsigned long attr,
+ unsigned long data_size,
+ void *data)
+{
+ struct gsmi_nvram_var_param param = {
+ .name_ptr = gsmi_dev.name_buf->address,
+ .data_ptr = gsmi_dev.data_buf->address,
+ .data_len = (u32)data_size,
+ .attributes = EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ };
+ size_t name_len = utf16_strlen(name, GSMI_BUF_SIZE / 2);
+ efi_status_t ret = EFI_SUCCESS;
+ int rc;
+ unsigned long flags;
+
+ if (name_len >= GSMI_BUF_SIZE / 2)
+ return EFI_BAD_BUFFER_SIZE;
+
+ spin_lock_irqsave(&gsmi_dev.lock, flags);
+
+ /* guid */
+ memcpy(&param.guid, vendor, sizeof(param.guid));
+
+ /* variable name, already in UTF-16 */
+ memset(gsmi_dev.name_buf->start, 0, gsmi_dev.name_buf->length);
+ memcpy(gsmi_dev.name_buf->start, name, name_len * 2);
+
+ /* data pointer */
+ memset(gsmi_dev.data_buf->start, 0, gsmi_dev.data_buf->length);
+ memcpy(gsmi_dev.data_buf->start, data, data_size);
+
+ /* parameter buffer */
+ memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length);
+ memcpy(gsmi_dev.param_buf->start, &param, sizeof(param));
+
+ rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_SET_NVRAM_VAR);
+ if (rc < 0) {
+ printk(KERN_ERR "gsmi: Set Variable failed\n");
+ ret = EFI_INVALID_PARAMETER;
+ }
+
+ spin_unlock_irqrestore(&gsmi_dev.lock, flags);
+
+ return ret;
+}
+
+static const struct efivar_operations efivar_ops = {
+ .get_variable = gsmi_get_variable,
+ .set_variable = gsmi_set_variable,
+ .get_next_variable = gsmi_get_next_variable,
+};
+
+static ssize_t eventlog_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t count)
+{
+ struct gsmi_set_eventlog_param param = {
+ .data_ptr = gsmi_dev.data_buf->address,
+ };
+ int rc = 0;
+ unsigned long flags;
+
+ /* Pull the type out */
+ if (count < sizeof(u32))
+ return -EINVAL;
+ param.type = *(u32 *)buf;
+ count -= sizeof(u32);
+ buf += sizeof(u32);
+
+ /* The remaining buffer is the data payload */
+ if (count > gsmi_dev.data_buf->length)
+ return -EINVAL;
+ param.data_len = count - sizeof(u32);
+
+ spin_lock_irqsave(&gsmi_dev.lock, flags);
+
+ /* data pointer */
+ memset(gsmi_dev.data_buf->start, 0, gsmi_dev.data_buf->length);
+ memcpy(gsmi_dev.data_buf->start, buf, param.data_len);
+
+ /* parameter buffer */
+ memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length);
+ memcpy(gsmi_dev.param_buf->start, &param, sizeof(param));
+
+ rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_SET_EVENT_LOG);
+ if (rc < 0)
+ printk(KERN_ERR "gsmi: Set Event Log failed\n");
+
+ spin_unlock_irqrestore(&gsmi_dev.lock, flags);
+
+ return rc;
+
+}
+
+static struct bin_attribute eventlog_bin_attr = {
+ .attr = {.name = "append_to_eventlog", .mode = 0200},
+ .write = eventlog_write,
+};
+
+static ssize_t gsmi_clear_eventlog_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int rc;
+ unsigned long flags;
+ unsigned long val;
+ struct {
+ u32 percentage;
+ u32 data_type;
+ } param;
+
+ rc = strict_strtoul(buf, 0, &val);
+ if (rc)
+ return rc;
+
+ /*
+ * Value entered is a percentage, 0 through 100, anything else
+ * is invalid.
+ */
+ if (val > 100)
+ return -EINVAL;
+
+ /* data_type here selects the smbios event log. */
+ param.percentage = val;
+ param.data_type = 0;
+
+ spin_lock_irqsave(&gsmi_dev.lock, flags);
+
+ /* parameter buffer */
+ memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length);
+ memcpy(gsmi_dev.param_buf->start, &param, sizeof(param));
+
+ rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_CLEAR_EVENT_LOG);
+
+ spin_unlock_irqrestore(&gsmi_dev.lock, flags);
+
+ if (rc)
+ return rc;
+ return count;
+}
+
+static struct kobj_attribute gsmi_clear_eventlog_attr = {
+ .attr = {.name = "clear_eventlog", .mode = 0200},
+ .store = gsmi_clear_eventlog_store,
+};
+
+static ssize_t gsmi_clear_config_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int rc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gsmi_dev.lock, flags);
+
+ /* clear parameter buffer */
+ memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length);
+
+ rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_CLEAR_CONFIG);
+
+ spin_unlock_irqrestore(&gsmi_dev.lock, flags);
+
+ if (rc)
+ return rc;
+ return count;
+}
+
+static struct kobj_attribute gsmi_clear_config_attr = {
+ .attr = {.name = "clear_config", .mode = 0200},
+ .store = gsmi_clear_config_store,
+};
+
+static const struct attribute *gsmi_attrs[] = {
+ &gsmi_clear_config_attr.attr,
+ &gsmi_clear_eventlog_attr.attr,
+ NULL,
+};
+
+static int gsmi_shutdown_reason(int reason)
+{
+ struct gsmi_log_entry_type_1 entry = {
+ .type = GSMI_LOG_ENTRY_TYPE_KERNEL,
+ .instance = reason,
+ };
+ struct gsmi_set_eventlog_param param = {
+ .data_len = sizeof(entry),
+ .type = 1,
+ };
+ static int saved_reason;
+ int rc = 0;
+ unsigned long flags;
+
+ /* avoid duplicate entries in the log */
+ if (saved_reason & (1 << reason))
+ return 0;
+
+ spin_lock_irqsave(&gsmi_dev.lock, flags);
+
+ saved_reason |= (1 << reason);
+
+ /* data pointer */
+ memset(gsmi_dev.data_buf->start, 0, gsmi_dev.data_buf->length);
+ memcpy(gsmi_dev.data_buf->start, &entry, sizeof(entry));
+
+ /* parameter buffer */
+ param.data_ptr = gsmi_dev.data_buf->address;
+ memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length);
+ memcpy(gsmi_dev.param_buf->start, &param, sizeof(param));
+
+ rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_SET_EVENT_LOG);
+
+ spin_unlock_irqrestore(&gsmi_dev.lock, flags);
+
+ if (rc < 0)
+ printk(KERN_ERR "gsmi: Log Shutdown Reason failed\n");
+ else
+ printk(KERN_EMERG "gsmi: Log Shutdown Reason 0x%02x\n",
+ reason);
+
+ return rc;
+}
+
+static int gsmi_reboot_callback(struct notifier_block *nb,
+ unsigned long reason, void *arg)
+{
+ gsmi_shutdown_reason(GSMI_SHUTDOWN_CLEAN);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block gsmi_reboot_notifier = {
+ .notifier_call = gsmi_reboot_callback
+};
+
+static int gsmi_die_callback(struct notifier_block *nb,
+ unsigned long reason, void *arg)
+{
+ if (reason == DIE_OOPS)
+ gsmi_shutdown_reason(GSMI_SHUTDOWN_OOPS);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block gsmi_die_notifier = {
+ .notifier_call = gsmi_die_callback
+};
+
+static int gsmi_panic_callback(struct notifier_block *nb,
+ unsigned long reason, void *arg)
+{
+ gsmi_shutdown_reason(GSMI_SHUTDOWN_PANIC);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block gsmi_panic_notifier = {
+ .notifier_call = gsmi_panic_callback,
+};
+
+/*
+ * This hash function was blatantly copied from include/linux/hash.h.
+ * It is used by this driver to obfuscate a board name that requires a
+ * quirk within this driver.
+ *
+ * Please do not remove this copy of the function as any changes to the
+ * global utility hash_64() function would break this driver's ability
+ * to identify a board and provide the appropriate quirk -- mikew@google.com
+ */
+static u64 __init local_hash_64(u64 val, unsigned bits)
+{
+ u64 hash = val;
+
+ /* Sigh, gcc can't optimise this alone like it does for 32 bits. */
+ u64 n = hash;
+ n <<= 18;
+ hash -= n;
+ n <<= 33;
+ hash -= n;
+ n <<= 3;
+ hash += n;
+ n <<= 3;
+ hash -= n;
+ n <<= 4;
+ hash += n;
+ n <<= 2;
+ hash += n;
+
+ /* High bits are more random, so use them. */
+ return hash >> (64 - bits);
+}
+
+static u32 __init hash_oem_table_id(char s[8])
+{
+ u64 input;
+ memcpy(&input, s, 8);
+ return local_hash_64(input, 32);
+}
+
+static struct dmi_system_id gsmi_dmi_table[] __initdata = {
+ {
+ .ident = "Google Board",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Google, Inc."),
+ },
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(dmi, gsmi_dmi_table);
+
+static __init int gsmi_system_valid(void)
+{
+ u32 hash;
+
+ if (!dmi_check_system(gsmi_dmi_table))
+ return -ENODEV;
+
+ /*
+ * Only newer firmware supports the gsmi interface. All older
+ * firmware that didn't support this interface used to plug the
+ * table name in the first four bytes of the oem_table_id field.
+ * Newer firmware doesn't do that though, so use that as the
+ * discriminant factor. We have to do this in order to
+ * whitewash our board names out of the public driver.
+ */
+ if (!strncmp(acpi_gbl_FADT.header.oem_table_id, "FACP", 4)) {
+ printk(KERN_INFO "gsmi: Board is too old\n");
+ return -ENODEV;
+ }
+
+ /* Disable on board with 1.0 BIOS due to Google bug 2602657 */
+ hash = hash_oem_table_id(acpi_gbl_FADT.header.oem_table_id);
+ if (hash == QUIRKY_BOARD_HASH) {
+ const char *bios_ver = dmi_get_system_info(DMI_BIOS_VERSION);
+ if (strncmp(bios_ver, "1.0", 3) == 0) {
+ pr_info("gsmi: disabled on this board's BIOS %s\n",
+ bios_ver);
+ return -ENODEV;
+ }
+ }
+
+ /* check for valid SMI command port in ACPI FADT */
+ if (acpi_gbl_FADT.smi_command == 0) {
+ pr_info("gsmi: missing smi_command\n");
+ return -ENODEV;
+ }
+
+ /* Found */
+ return 0;
+}
+
+static struct kobject *gsmi_kobj;
+static struct efivars efivars;
+
+static __init int gsmi_init(void)
+{
+ unsigned long flags;
+ int ret;
+
+ ret = gsmi_system_valid();
+ if (ret)
+ return ret;
+
+ gsmi_dev.smi_cmd = acpi_gbl_FADT.smi_command;
+
+ /* register device */
+ gsmi_dev.pdev = platform_device_register_simple("gsmi", -1, NULL, 0);
+ if (IS_ERR(gsmi_dev.pdev)) {
+ printk(KERN_ERR "gsmi: unable to register platform device\n");
+ return PTR_ERR(gsmi_dev.pdev);
+ }
+
+ /* SMI access needs to be serialized */
+ spin_lock_init(&gsmi_dev.lock);
+
+ /* SMI callbacks require 32bit addresses */
+ gsmi_dev.pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+ gsmi_dev.pdev->dev.dma_mask =
+ &gsmi_dev.pdev->dev.coherent_dma_mask;
+ ret = -ENOMEM;
+ gsmi_dev.dma_pool = dma_pool_create("gsmi", &gsmi_dev.pdev->dev,
+ GSMI_BUF_SIZE, GSMI_BUF_ALIGN, 0);
+ if (!gsmi_dev.dma_pool)
+ goto out_err;
+
+ /*
+ * pre-allocate buffers because sometimes we are called when
+ * this is not feasible: oops, panic, die, mce, etc
+ */
+ gsmi_dev.name_buf = gsmi_buf_alloc();
+ if (!gsmi_dev.name_buf) {
+ printk(KERN_ERR "gsmi: failed to allocate name buffer\n");
+ goto out_err;
+ }
+
+ gsmi_dev.data_buf = gsmi_buf_alloc();
+ if (!gsmi_dev.data_buf) {
+ printk(KERN_ERR "gsmi: failed to allocate data buffer\n");
+ goto out_err;
+ }
+
+ gsmi_dev.param_buf = gsmi_buf_alloc();
+ if (!gsmi_dev.param_buf) {
+ printk(KERN_ERR "gsmi: failed to allocate param buffer\n");
+ goto out_err;
+ }
+
+ /*
+ * Determine type of handshake used to serialize the SMI
+ * entry. See also gsmi_exec().
+ *
+ * There's a "behavior" present on some chipsets where writing the
+ * SMI trigger register in the southbridge doesn't result in an
+ * immediate SMI. Rather, the processor can execute "a few" more
+ * instructions before the SMI takes effect. To ensure synchronous
+ * behavior, implement a handshake between the kernel driver and the
+ * firmware handler to spin until released. This ioctl determines
+ * the type of handshake.
+ *
+ * NONE: The firmware handler does not implement any
+ * handshake. Either it doesn't need to, or it's legacy firmware
+ * that doesn't know it needs to and never will.
+ *
+ * CF: The firmware handler will clear the CF in the saved
+ * state before returning. The driver may set the CF and test for
+ * it to clear before proceeding.
+ *
+ * SPIN: The firmware handler does not implement any handshake
+ * but the driver should spin for a hundred or so microseconds
+ * to ensure the SMI has triggered.
+ *
+ * Finally, the handler will return -ENOSYS if
+ * GSMI_CMD_HANDSHAKE_TYPE is unimplemented, which implies
+ * HANDSHAKE_NONE.
+ */
+ spin_lock_irqsave(&gsmi_dev.lock, flags);
+ gsmi_dev.handshake_type = GSMI_HANDSHAKE_SPIN;
+ gsmi_dev.handshake_type =
+ gsmi_exec(GSMI_CALLBACK, GSMI_CMD_HANDSHAKE_TYPE);
+ if (gsmi_dev.handshake_type == -ENOSYS)
+ gsmi_dev.handshake_type = GSMI_HANDSHAKE_NONE;
+ spin_unlock_irqrestore(&gsmi_dev.lock, flags);
+
+ /* Remove and clean up gsmi if the handshake could not complete. */
+ if (gsmi_dev.handshake_type == -ENXIO) {
+ printk(KERN_INFO "gsmi version " DRIVER_VERSION
+ " failed to load\n");
+ ret = -ENODEV;
+ goto out_err;
+ }
+
+ printk(KERN_INFO "gsmi version " DRIVER_VERSION " loaded\n");
+
+ /* Register in the firmware directory */
+ ret = -ENOMEM;
+ gsmi_kobj = kobject_create_and_add("gsmi", firmware_kobj);
+ if (!gsmi_kobj) {
+ printk(KERN_INFO "gsmi: Failed to create firmware kobj\n");
+ goto out_err;
+ }
+
+ /* Setup eventlog access */
+ ret = sysfs_create_bin_file(gsmi_kobj, &eventlog_bin_attr);
+ if (ret) {
+ printk(KERN_INFO "gsmi: Failed to setup eventlog");
+ goto out_err;
+ }
+
+ /* Other attributes */
+ ret = sysfs_create_files(gsmi_kobj, gsmi_attrs);
+ if (ret) {
+ printk(KERN_INFO "gsmi: Failed to add attrs");
+ goto out_err;
+ }
+
+ if (register_efivars(&efivars, &efivar_ops, gsmi_kobj)) {
+ printk(KERN_INFO "gsmi: Failed to register efivars\n");
+ goto out_err;
+ }
+
+ register_reboot_notifier(&gsmi_reboot_notifier);
+ register_die_notifier(&gsmi_die_notifier);
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &gsmi_panic_notifier);
+
+ return 0;
+
+ out_err:
+ kobject_put(gsmi_kobj);
+ gsmi_buf_free(gsmi_dev.param_buf);
+ gsmi_buf_free(gsmi_dev.data_buf);
+ gsmi_buf_free(gsmi_dev.name_buf);
+ if (gsmi_dev.dma_pool)
+ dma_pool_destroy(gsmi_dev.dma_pool);
+ platform_device_unregister(gsmi_dev.pdev);
+ pr_info("gsmi: failed to load: %d\n", ret);
+ return ret;
+}
+
+static void __exit gsmi_exit(void)
+{
+ unregister_reboot_notifier(&gsmi_reboot_notifier);
+ unregister_die_notifier(&gsmi_die_notifier);
+ atomic_notifier_chain_unregister(&panic_notifier_list,
+ &gsmi_panic_notifier);
+ unregister_efivars(&efivars);
+
+ kobject_put(gsmi_kobj);
+ gsmi_buf_free(gsmi_dev.param_buf);
+ gsmi_buf_free(gsmi_dev.data_buf);
+ gsmi_buf_free(gsmi_dev.name_buf);
+ dma_pool_destroy(gsmi_dev.dma_pool);
+ platform_device_unregister(gsmi_dev.pdev);
+}
+
+module_init(gsmi_init);
+module_exit(gsmi_exit);
+
+MODULE_AUTHOR("Google, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/firmware/google/memconsole.c b/drivers/firmware/google/memconsole.c
new file mode 100644
index 00000000000..2a90ba61361
--- /dev/null
+++ b/drivers/firmware/google/memconsole.c
@@ -0,0 +1,166 @@
+/*
+ * memconsole.c
+ *
+ * Infrastructure for importing the BIOS memory based console
+ * into the kernel log ringbuffer.
+ *
+ * Copyright 2010 Google Inc. All rights reserved.
+ */
+
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <asm/bios_ebda.h>
+
+#define BIOS_MEMCONSOLE_V1_MAGIC 0xDEADBABE
+#define BIOS_MEMCONSOLE_V2_MAGIC (('M')|('C'<<8)|('O'<<16)|('N'<<24))
+
+struct biosmemcon_ebda {
+ u32 signature;
+ union {
+ struct {
+ u8 enabled;
+ u32 buffer_addr;
+ u16 start;
+ u16 end;
+ u16 num_chars;
+ u8 wrapped;
+ } __packed v1;
+ struct {
+ u32 buffer_addr;
+ /* Misdocumented as number of pages! */
+ u16 num_bytes;
+ u16 start;
+ u16 end;
+ } __packed v2;
+ };
+} __packed;
+
+static char *memconsole_baseaddr;
+static size_t memconsole_length;
+
+static ssize_t memconsole_read(struct file *filp, struct kobject *kobp,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t pos, size_t count)
+{
+ return memory_read_from_buffer(buf, count, &pos, memconsole_baseaddr,
+ memconsole_length);
+}
+
+static struct bin_attribute memconsole_bin_attr = {
+ .attr = {.name = "log", .mode = 0444},
+ .read = memconsole_read,
+};
+
+
+static void found_v1_header(struct biosmemcon_ebda *hdr)
+{
+ printk(KERN_INFO "BIOS console v1 EBDA structure found at %p\n", hdr);
+ printk(KERN_INFO "BIOS console buffer at 0x%.8x, "
+ "start = %d, end = %d, num = %d\n",
+ hdr->v1.buffer_addr, hdr->v1.start,
+ hdr->v1.end, hdr->v1.num_chars);
+
+ memconsole_length = hdr->v1.num_chars;
+ memconsole_baseaddr = phys_to_virt(hdr->v1.buffer_addr);
+}
+
+static void found_v2_header(struct biosmemcon_ebda *hdr)
+{
+ printk(KERN_INFO "BIOS console v2 EBDA structure found at %p\n", hdr);
+ printk(KERN_INFO "BIOS console buffer at 0x%.8x, "
+ "start = %d, end = %d, num_bytes = %d\n",
+ hdr->v2.buffer_addr, hdr->v2.start,
+ hdr->v2.end, hdr->v2.num_bytes);
+
+ memconsole_length = hdr->v2.end - hdr->v2.start;
+ memconsole_baseaddr = phys_to_virt(hdr->v2.buffer_addr
+ + hdr->v2.start);
+}
+
+/*
+ * Search through the EBDA for the BIOS Memory Console, and
+ * set the global variables to point to it. Return true if found.
+ */
+static bool found_memconsole(void)
+{
+ unsigned int address;
+ size_t length, cur;
+
+ address = get_bios_ebda();
+ if (!address) {
+ printk(KERN_INFO "BIOS EBDA non-existent.\n");
+ return false;
+ }
+
+ /* EBDA length is byte 0 of EBDA (in KB) */
+ length = *(u8 *)phys_to_virt(address);
+ length <<= 10; /* convert to bytes */
+
+ /*
+ * Search through EBDA for BIOS memory console structure
+ * note: signature is not necessarily dword-aligned
+ */
+ for (cur = 0; cur < length; cur++) {
+ struct biosmemcon_ebda *hdr = phys_to_virt(address + cur);
+
+ /* memconsole v1 */
+ if (hdr->signature == BIOS_MEMCONSOLE_V1_MAGIC) {
+ found_v1_header(hdr);
+ return true;
+ }
+
+ /* memconsole v2 */
+ if (hdr->signature == BIOS_MEMCONSOLE_V2_MAGIC) {
+ found_v2_header(hdr);
+ return true;
+ }
+ }
+
+ printk(KERN_INFO "BIOS console EBDA structure not found!\n");
+ return false;
+}
+
+static struct dmi_system_id memconsole_dmi_table[] __initdata = {
+ {
+ .ident = "Google Board",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Google, Inc."),
+ },
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(dmi, memconsole_dmi_table);
+
+static int __init memconsole_init(void)
+{
+ int ret;
+
+ if (!dmi_check_system(memconsole_dmi_table))
+ return -ENODEV;
+
+ if (!found_memconsole())
+ return -ENODEV;
+
+ memconsole_bin_attr.size = memconsole_length;
+
+ ret = sysfs_create_bin_file(firmware_kobj, &memconsole_bin_attr);
+
+ return ret;
+}
+
+static void __exit memconsole_exit(void)
+{
+ sysfs_remove_bin_file(firmware_kobj, &memconsole_bin_attr);
+}
+
+module_init(memconsole_init);
+module_exit(memconsole_exit);
+
+MODULE_AUTHOR("Google, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/firmware/iscsi_ibft_find.c b/drivers/firmware/iscsi_ibft_find.c
index 2192456dfd6..f032e446fc1 100644
--- a/drivers/firmware/iscsi_ibft_find.c
+++ b/drivers/firmware/iscsi_ibft_find.c
@@ -42,7 +42,20 @@
struct acpi_table_ibft *ibft_addr;
EXPORT_SYMBOL_GPL(ibft_addr);
-#define IBFT_SIGN "iBFT"
+static const struct {
+ char *sign;
+} ibft_signs[] = {
+#ifdef CONFIG_ACPI
+ /*
+ * One spec says "IBFT", the other says "iBFT". We have to check
+ * for both.
+ */
+ { ACPI_SIG_IBFT },
+#endif
+ { "iBFT" },
+ { "BIFT" }, /* Broadcom iSCSI Offload */
+};
+
#define IBFT_SIGN_LEN 4
#define IBFT_START 0x80000 /* 512kB */
#define IBFT_END 0x100000 /* 1MB */
@@ -62,6 +75,7 @@ static int __init find_ibft_in_mem(void)
unsigned long pos;
unsigned int len = 0;
void *virt;
+ int i;
for (pos = IBFT_START; pos < IBFT_END; pos += 16) {
/* The table can't be inside the VGA BIOS reserved space,
@@ -69,18 +83,23 @@ static int __init find_ibft_in_mem(void)
if (pos == VGA_MEM)
pos += VGA_SIZE;
virt = isa_bus_to_virt(pos);
- if (memcmp(virt, IBFT_SIGN, IBFT_SIGN_LEN) == 0) {
- unsigned long *addr =
- (unsigned long *)isa_bus_to_virt(pos + 4);
- len = *addr;
- /* if the length of the table extends past 1M,
- * the table cannot be valid. */
- if (pos + len <= (IBFT_END-1)) {
- ibft_addr = (struct acpi_table_ibft *)virt;
- break;
+
+ for (i = 0; i < ARRAY_SIZE(ibft_signs); i++) {
+ if (memcmp(virt, ibft_signs[i].sign, IBFT_SIGN_LEN) ==
+ 0) {
+ unsigned long *addr =
+ (unsigned long *)isa_bus_to_virt(pos + 4);
+ len = *addr;
+ /* if the length of the table extends past 1M,
+ * the table cannot be valid. */
+ if (pos + len <= (IBFT_END-1)) {
+ ibft_addr = (struct acpi_table_ibft *)virt;
+ goto done;
+ }
}
}
}
+done:
return len;
}
/*
@@ -89,18 +108,12 @@ static int __init find_ibft_in_mem(void)
*/
unsigned long __init find_ibft_region(unsigned long *sizep)
{
-
+ int i;
ibft_addr = NULL;
#ifdef CONFIG_ACPI
- /*
- * One spec says "IBFT", the other says "iBFT". We have to check
- * for both.
- */
- if (!ibft_addr)
- acpi_table_parse(ACPI_SIG_IBFT, acpi_find_ibft);
- if (!ibft_addr)
- acpi_table_parse(IBFT_SIGN, acpi_find_ibft);
+ for (i = 0; i < ARRAY_SIZE(ibft_signs) && !ibft_addr; i++)
+ acpi_table_parse(ibft_signs[i].sign, acpi_find_ibft);
#endif /* CONFIG_ACPI */
/* iBFT 1.03 section 1.4.3.1 mandates that UEFI machines will
diff --git a/drivers/gpio/ml_ioh_gpio.c b/drivers/gpio/ml_ioh_gpio.c
index 7f6f01a4b14..0a775f7987c 100644
--- a/drivers/gpio/ml_ioh_gpio.c
+++ b/drivers/gpio/ml_ioh_gpio.c
@@ -116,6 +116,7 @@ static int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
reg_val |= (1 << nr);
else
reg_val &= ~(1 << nr);
+ iowrite32(reg_val, &chip->reg->regs[chip->ch].po);
mutex_unlock(&chip->lock);
diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c
index 583e9259207..7630ab7b9be 100644
--- a/drivers/gpio/pca953x.c
+++ b/drivers/gpio/pca953x.c
@@ -558,7 +558,7 @@ static int __devinit pca953x_probe(struct i2c_client *client,
ret = gpiochip_add(&chip->gpio_chip);
if (ret)
- goto out_failed;
+ goto out_failed_irq;
if (pdata->setup) {
ret = pdata->setup(client, chip->gpio_chip.base,
@@ -570,8 +570,9 @@ static int __devinit pca953x_probe(struct i2c_client *client,
i2c_set_clientdata(client, chip);
return 0;
-out_failed:
+out_failed_irq:
pca953x_irq_teardown(chip);
+out_failed:
kfree(chip->dyn_pdata);
kfree(chip);
return ret;
diff --git a/drivers/gpio/pch_gpio.c b/drivers/gpio/pch_gpio.c
index 2c6af870510..f970a5f3585 100644
--- a/drivers/gpio/pch_gpio.c
+++ b/drivers/gpio/pch_gpio.c
@@ -105,6 +105,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
reg_val |= (1 << nr);
else
reg_val &= ~(1 << nr);
+ iowrite32(reg_val, &chip->reg->po);
mutex_unlock(&chip->lock);
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index a6feb78c404..b493663c7ba 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -24,6 +24,7 @@ config DRM_KMS_HELPER
depends on DRM
select FB
select FRAMEBUFFER_CONSOLE if !EXPERT
+ select FRAMEBUFFER_CONSOLE_DETECT_PRIMARY if FRAMEBUFFER_CONSOLE
help
FB and CRTC helpers for KMS drivers.
@@ -96,6 +97,7 @@ config DRM_I915
# i915 depends on ACPI_VIDEO when ACPI is enabled
# but for select to work, need to select ACPI_VIDEO's dependencies, ick
select BACKLIGHT_CLASS_DEVICE if ACPI
+ select VIDEO_OUTPUT_CONTROL if ACPI
select INPUT if ACPI
select ACPI_VIDEO if ACPI
select ACPI_BUTTON if ACPI
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 95072047396..140b9525b48 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -342,9 +342,22 @@ int drm_fb_helper_debug_leave(struct fb_info *info)
}
EXPORT_SYMBOL(drm_fb_helper_debug_leave);
+bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
+{
+ bool error = false;
+ int i, ret;
+ for (i = 0; i < fb_helper->crtc_count; i++) {
+ struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
+ ret = drm_crtc_helper_set_config(mode_set);
+ if (ret)
+ error = true;
+ }
+ return error;
+}
+EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode);
+
bool drm_fb_helper_force_kernel_mode(void)
{
- int i = 0;
bool ret, error = false;
struct drm_fb_helper *helper;
@@ -352,12 +365,12 @@ bool drm_fb_helper_force_kernel_mode(void)
return false;
list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
- for (i = 0; i < helper->crtc_count; i++) {
- struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
- ret = drm_crtc_helper_set_config(mode_set);
- if (ret)
- error = true;
- }
+ if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+ continue;
+
+ ret = drm_fb_helper_restore_fbdev_mode(helper);
+ if (ret)
+ error = true;
}
return error;
}
@@ -1503,17 +1516,33 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
}
EXPORT_SYMBOL(drm_fb_helper_initial_config);
-bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
+/**
+ * drm_fb_helper_hotplug_event - respond to a hotplug notification by
+ * probing all the outputs attached to the fb.
+ * @fb_helper: the drm_fb_helper
+ *
+ * LOCKING:
+ * Called at runtime, must take mode config lock.
+ *
+ * Scan the connectors attached to the fb_helper and try to put together a
+ * setup after *notification of a change in output configuration.
+ *
+ * RETURNS:
+ * 0 on success and a non-zero error code otherwise.
+ */
+int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
{
+ struct drm_device *dev = fb_helper->dev;
int count = 0;
u32 max_width, max_height, bpp_sel;
bool bound = false, crtcs_bound = false;
struct drm_crtc *crtc;
if (!fb_helper->fb)
- return false;
+ return 0;
- list_for_each_entry(crtc, &fb_helper->dev->mode_config.crtc_list, head) {
+ mutex_lock(&dev->mode_config.mutex);
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
if (crtc->fb)
crtcs_bound = true;
if (crtc->fb == fb_helper->fb)
@@ -1522,7 +1551,8 @@ bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
if (!bound && crtcs_bound) {
fb_helper->delayed_hotplug = true;
- return false;
+ mutex_unlock(&dev->mode_config.mutex);
+ return 0;
}
DRM_DEBUG_KMS("\n");
@@ -1533,6 +1563,7 @@ bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
count = drm_fb_helper_probe_connector_modes(fb_helper, max_width,
max_height);
drm_setup_crtcs(fb_helper);
+ mutex_unlock(&dev->mode_config.mutex);
return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
}
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 741457bd1c4..a1f12cb043d 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -932,11 +932,34 @@ EXPORT_SYMBOL(drm_vblank_put);
void drm_vblank_off(struct drm_device *dev, int crtc)
{
+ struct drm_pending_vblank_event *e, *t;
+ struct timeval now;
unsigned long irqflags;
+ unsigned int seq;
spin_lock_irqsave(&dev->vbl_lock, irqflags);
vblank_disable_and_save(dev, crtc);
DRM_WAKEUP(&dev->vbl_queue[crtc]);
+
+ /* Send any queued vblank events, lest the natives grow disquiet */
+ seq = drm_vblank_count_and_time(dev, crtc, &now);
+ list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
+ if (e->pipe != crtc)
+ continue;
+ DRM_DEBUG("Sending premature vblank event on disable: \
+ wanted %d, current %d\n",
+ e->event.sequence, seq);
+
+ e->event.sequence = seq;
+ e->event.tv_sec = now.tv_sec;
+ e->event.tv_usec = now.tv_usec;
+ drm_vblank_put(dev, e->pipe);
+ list_move_tail(&e->base.link, &e->base.file_priv->event_list);
+ wake_up_interruptible(&e->base.file_priv->event_wait);
+ trace_drm_vblank_event_delivered(e->base.pid, e->pipe,
+ e->event.sequence);
+ }
+
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
}
EXPORT_SYMBOL(drm_vblank_off);
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
index 5d00b0fc0d9..959186cbf32 100644
--- a/drivers/gpu/drm/drm_mm.c
+++ b/drivers/gpu/drm/drm_mm.c
@@ -431,7 +431,7 @@ EXPORT_SYMBOL(drm_mm_search_free_in_range);
void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
{
list_replace(&old->node_list, &new->node_list);
- list_replace(&old->node_list, &new->hole_stack);
+ list_replace(&old->hole_stack, &new->hole_stack);
new->hole_follows = old->hole_follows;
new->mm = old->mm;
new->start = old->start;
@@ -699,8 +699,8 @@ int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
entry->size);
total_used += entry->size;
if (entry->hole_follows) {
- hole_start = drm_mm_hole_node_start(&mm->head_node);
- hole_end = drm_mm_hole_node_end(&mm->head_node);
+ hole_start = drm_mm_hole_node_start(entry);
+ hole_end = drm_mm_hole_node_end(entry);
hole_size = hole_end - hole_start;
seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: free\n",
hole_start, hole_end, hole_size);
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 72730377a01..12876f2795d 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -2207,7 +2207,7 @@ void i915_driver_lastclose(struct drm_device * dev)
drm_i915_private_t *dev_priv = dev->dev_private;
if (!dev_priv || drm_core_check_feature(dev, DRIVER_MODESET)) {
- drm_fb_helper_restore();
+ intel_fb_restore_mode(dev);
vga_switcheroo_process_delayed_switch();
return;
}
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index c34a8dd31d0..32d1b3e829c 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -49,7 +49,7 @@ module_param_named(panel_ignore_lid, i915_panel_ignore_lid, int, 0600);
unsigned int i915_powersave = 1;
module_param_named(powersave, i915_powersave, int, 0600);
-unsigned int i915_semaphores = 1;
+unsigned int i915_semaphores = 0;
module_param_named(semaphores, i915_semaphores, int, 0600);
unsigned int i915_enable_rc6 = 0;
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 432fc04c6bf..2166ee071dd 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -3771,8 +3771,11 @@ static bool g4x_compute_wm0(struct drm_device *dev,
int entries, tlb_miss;
crtc = intel_get_crtc_for_plane(dev, plane);
- if (crtc->fb == NULL || !crtc->enabled)
+ if (crtc->fb == NULL || !crtc->enabled) {
+ *cursor_wm = cursor->guard_size;
+ *plane_wm = display->guard_size;
return false;
+ }
htotal = crtc->mode.htotal;
hdisplay = crtc->mode.hdisplay;
@@ -5602,9 +5605,9 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
intel_clock_t clock;
if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0)
- fp = FP0(pipe);
+ fp = I915_READ(FP0(pipe));
else
- fp = FP1(pipe);
+ fp = I915_READ(FP1(pipe));
clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT;
if (IS_PINEVIEW(dev)) {
@@ -6215,36 +6218,6 @@ cleanup_work:
return ret;
}
-static void intel_crtc_reset(struct drm_crtc *crtc)
-{
- struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-
- /* Reset flags back to the 'unknown' status so that they
- * will be correctly set on the initial modeset.
- */
- intel_crtc->dpms_mode = -1;
-}
-
-static struct drm_crtc_helper_funcs intel_helper_funcs = {
- .dpms = intel_crtc_dpms,
- .mode_fixup = intel_crtc_mode_fixup,
- .mode_set = intel_crtc_mode_set,
- .mode_set_base = intel_pipe_set_base,
- .mode_set_base_atomic = intel_pipe_set_base_atomic,
- .load_lut = intel_crtc_load_lut,
- .disable = intel_crtc_disable,
-};
-
-static const struct drm_crtc_funcs intel_crtc_funcs = {
- .reset = intel_crtc_reset,
- .cursor_set = intel_crtc_cursor_set,
- .cursor_move = intel_crtc_cursor_move,
- .gamma_set = intel_crtc_gamma_set,
- .set_config = drm_crtc_helper_set_config,
- .destroy = intel_crtc_destroy,
- .page_flip = intel_crtc_page_flip,
-};
-
static void intel_sanitize_modesetting(struct drm_device *dev,
int pipe, int plane)
{
@@ -6281,6 +6254,42 @@ static void intel_sanitize_modesetting(struct drm_device *dev,
intel_disable_pipe(dev_priv, pipe);
}
+static void intel_crtc_reset(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+ /* Reset flags back to the 'unknown' status so that they
+ * will be correctly set on the initial modeset.
+ */
+ intel_crtc->dpms_mode = -1;
+
+ /* We need to fix up any BIOS configuration that conflicts with
+ * our expectations.
+ */
+ intel_sanitize_modesetting(dev, intel_crtc->pipe, intel_crtc->plane);
+}
+
+static struct drm_crtc_helper_funcs intel_helper_funcs = {
+ .dpms = intel_crtc_dpms,
+ .mode_fixup = intel_crtc_mode_fixup,
+ .mode_set = intel_crtc_mode_set,
+ .mode_set_base = intel_pipe_set_base,
+ .mode_set_base_atomic = intel_pipe_set_base_atomic,
+ .load_lut = intel_crtc_load_lut,
+ .disable = intel_crtc_disable,
+};
+
+static const struct drm_crtc_funcs intel_crtc_funcs = {
+ .reset = intel_crtc_reset,
+ .cursor_set = intel_crtc_cursor_set,
+ .cursor_move = intel_crtc_cursor_move,
+ .gamma_set = intel_crtc_gamma_set,
+ .set_config = drm_crtc_helper_set_config,
+ .destroy = intel_crtc_destroy,
+ .page_flip = intel_crtc_page_flip,
+};
+
static void intel_crtc_init(struct drm_device *dev, int pipe)
{
drm_i915_private_t *dev_priv = dev->dev_private;
@@ -6330,8 +6339,6 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
setup_timer(&intel_crtc->idle_timer, intel_crtc_idle_timer,
(unsigned long)intel_crtc);
-
- intel_sanitize_modesetting(dev, intel_crtc->pipe, intel_crtc->plane);
}
int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
@@ -6572,8 +6579,10 @@ intel_user_framebuffer_create(struct drm_device *dev,
return ERR_PTR(-ENOENT);
intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
- if (!intel_fb)
+ if (!intel_fb) {
+ drm_gem_object_unreference_unlocked(&obj->base);
return ERR_PTR(-ENOMEM);
+ }
ret = intel_framebuffer_init(dev, intel_fb, mode_cmd, obj);
if (ret) {
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index cb8578b7e44..a4d80314e7f 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -1470,7 +1470,8 @@ intel_dp_link_down(struct intel_dp *intel_dp)
if (!HAS_PCH_CPT(dev) &&
I915_READ(intel_dp->output_reg) & DP_PIPEB_SELECT) {
- struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.base.crtc);
+ struct drm_crtc *crtc = intel_dp->base.base.crtc;
+
/* Hardware workaround: leaving our transcoder select
* set to transcoder B while it's off will prevent the
* corresponding HDMI output on transcoder A.
@@ -1485,7 +1486,19 @@ intel_dp_link_down(struct intel_dp *intel_dp)
/* Changes to enable or select take place the vblank
* after being written.
*/
- intel_wait_for_vblank(dev, intel_crtc->pipe);
+ if (crtc == NULL) {
+ /* We can arrive here never having been attached
+ * to a CRTC, for instance, due to inheriting
+ * random state from the BIOS.
+ *
+ * If the pipe is not running, play safe and
+ * wait for the clocks to stabilise before
+ * continuing.
+ */
+ POSTING_READ(intel_dp->output_reg);
+ msleep(50);
+ } else
+ intel_wait_for_vblank(dev, to_intel_crtc(crtc)->pipe);
}
I915_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN);
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index f5b0d8306d8..1d20712d527 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -338,4 +338,5 @@ extern int intel_overlay_attrs(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern void intel_fb_output_poll_changed(struct drm_device *dev);
+extern void intel_fb_restore_mode(struct drm_device *dev);
#endif /* __INTEL_DRV_H__ */
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
index 512782728e5..ec49bae7338 100644
--- a/drivers/gpu/drm/i915/intel_fb.c
+++ b/drivers/gpu/drm/i915/intel_fb.c
@@ -264,3 +264,13 @@ void intel_fb_output_poll_changed(struct drm_device *dev)
drm_i915_private_t *dev_priv = dev->dev_private;
drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper);
}
+
+void intel_fb_restore_mode(struct drm_device *dev)
+{
+ int ret;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper);
+ if (ret)
+ DRM_DEBUG("failed to restore crtc mode\n");
+}
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index a562bd2648c..67cb076d271 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -539,6 +539,9 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val,
struct drm_device *dev = dev_priv->dev;
struct drm_connector *connector = dev_priv->int_lvds_connector;
+ if (dev->switch_power_state != DRM_SWITCH_POWER_ON)
+ return NOTIFY_OK;
+
/*
* check and update the status of LVDS connector after receiving
* the LID nofication event.
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index 4256b8ef394..6b22c1dcc01 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -1151,10 +1151,10 @@ intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
(video_levels->blank << TV_BLANK_LEVEL_SHIFT)));
{
int pipeconf_reg = PIPECONF(pipe);
- int dspcntr_reg = DSPCNTR(pipe);
+ int dspcntr_reg = DSPCNTR(intel_crtc->plane);
int pipeconf = I915_READ(pipeconf_reg);
int dspcntr = I915_READ(dspcntr_reg);
- int dspbase_reg = DSPADDR(pipe);
+ int dspbase_reg = DSPADDR(intel_crtc->plane);
int xpos = 0x0, ypos = 0x0;
unsigned int xsize, ysize;
/* Pipe must be off here */
@@ -1378,7 +1378,9 @@ intel_tv_detect(struct drm_connector *connector, bool force)
if (type < 0)
return connector_status_disconnected;
+ intel_tv->type = type;
intel_tv_find_better_format(connector);
+
return connector_status_connected;
}
@@ -1670,8 +1672,7 @@ intel_tv_init(struct drm_device *dev)
*
* More recent chipsets favour HDMI rather than integrated S-Video.
*/
- connector->polled =
- DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT;
drm_connector_init(dev, connector, &intel_tv_connector_funcs,
DRM_MODE_CONNECTOR_SVIDEO);
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index 8314a49b6b9..90aef64b76f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -269,7 +269,7 @@ struct init_tbl_entry {
int (*handler)(struct nvbios *, uint16_t, struct init_exec *);
};
-static int parse_init_table(struct nvbios *, unsigned int, struct init_exec *);
+static int parse_init_table(struct nvbios *, uint16_t, struct init_exec *);
#define MACRO_INDEX_SIZE 2
#define MACRO_SIZE 8
@@ -2011,6 +2011,27 @@ init_sub_direct(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
}
static int
+init_jump(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+ /*
+ * INIT_JUMP opcode: 0x5C ('\')
+ *
+ * offset (8 bit): opcode
+ * offset + 1 (16 bit): offset (in bios)
+ *
+ * Continue execution of init table from 'offset'
+ */
+
+ uint16_t jmp_offset = ROM16(bios->data[offset + 1]);
+
+ if (!iexec->execute)
+ return 3;
+
+ BIOSLOG(bios, "0x%04X: Jump to 0x%04X\n", offset, jmp_offset);
+ return jmp_offset - offset;
+}
+
+static int
init_i2c_if(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
{
/*
@@ -3659,6 +3680,7 @@ static struct init_tbl_entry itbl_entry[] = {
{ "INIT_ZM_REG_SEQUENCE" , 0x58, init_zm_reg_sequence },
/* INIT_INDIRECT_REG (0x5A, 7, 0, 0) removed due to no example of use */
{ "INIT_SUB_DIRECT" , 0x5B, init_sub_direct },
+ { "INIT_JUMP" , 0x5C, init_jump },
{ "INIT_I2C_IF" , 0x5E, init_i2c_if },
{ "INIT_COPY_NV_REG" , 0x5F, init_copy_nv_reg },
{ "INIT_ZM_INDEX_IO" , 0x62, init_zm_index_io },
@@ -3700,8 +3722,7 @@ static struct init_tbl_entry itbl_entry[] = {
#define MAX_TABLE_OPS 1000
static int
-parse_init_table(struct nvbios *bios, unsigned int offset,
- struct init_exec *iexec)
+parse_init_table(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
{
/*
* Parses all commands in an init table.
@@ -6333,6 +6354,32 @@ apply_dcb_encoder_quirks(struct drm_device *dev, int idx, u32 *conn, u32 *conf)
}
}
+ /* XFX GT-240X-YA
+ *
+ * So many things wrong here, replace the entire encoder table..
+ */
+ if (nv_match_device(dev, 0x0ca3, 0x1682, 0x3003)) {
+ if (idx == 0) {
+ *conn = 0x02001300; /* VGA, connector 1 */
+ *conf = 0x00000028;
+ } else
+ if (idx == 1) {
+ *conn = 0x01010312; /* DVI, connector 0 */
+ *conf = 0x00020030;
+ } else
+ if (idx == 2) {
+ *conn = 0x01010310; /* VGA, connector 0 */
+ *conf = 0x00000028;
+ } else
+ if (idx == 3) {
+ *conn = 0x02022362; /* HDMI, connector 2 */
+ *conf = 0x00020010;
+ } else {
+ *conn = 0x0000000e; /* EOL */
+ *conf = 0x00000000;
+ }
+ }
+
return true;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c
index ce38e97b942..568caedd721 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dma.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dma.c
@@ -83,7 +83,7 @@ nouveau_dma_init(struct nouveau_channel *chan)
return ret;
/* NV_MEMORY_TO_MEMORY_FORMAT requires a notifier object */
- ret = nouveau_notifier_alloc(chan, NvNotify0, 32, 0xfd0, 0x1000,
+ ret = nouveau_notifier_alloc(chan, NvNotify0, 32, 0xfe0, 0x1000,
&chan->m2mf_ntfy);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 57e5302503d..a76514a209b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -682,6 +682,9 @@ struct drm_nouveau_private {
/* For PFIFO and PGRAPH. */
spinlock_t context_switch_lock;
+ /* VM/PRAMIN flush, legacy PRAMIN aperture */
+ spinlock_t vm_lock;
+
/* RAMIN configuration, RAMFC, RAMHT and RAMRO offsets */
struct nouveau_ramht *ramht;
struct nouveau_gpuobj *ramfc;
@@ -1190,7 +1193,7 @@ extern int nv50_graph_load_context(struct nouveau_channel *);
extern int nv50_graph_unload_context(struct drm_device *);
extern int nv50_grctx_init(struct nouveau_grctx *);
extern void nv50_graph_tlb_flush(struct drm_device *dev);
-extern void nv86_graph_tlb_flush(struct drm_device *dev);
+extern void nv84_graph_tlb_flush(struct drm_device *dev);
extern struct nouveau_enum nv50_data_error_names[];
/* nvc0_graph.c */
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 889c4454682..39aee6d4daf 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -181,13 +181,13 @@ nouveau_fbcon_sync(struct fb_info *info)
OUT_RING (chan, 0);
}
- nouveau_bo_wr32(chan->notifier_bo, chan->m2mf_ntfy + 3, 0xffffffff);
+ nouveau_bo_wr32(chan->notifier_bo, chan->m2mf_ntfy/4 + 3, 0xffffffff);
FIRE_RING(chan);
mutex_unlock(&chan->mutex);
ret = -EBUSY;
for (i = 0; i < 100000; i++) {
- if (!nouveau_bo_rd32(chan->notifier_bo, chan->m2mf_ntfy + 3)) {
+ if (!nouveau_bo_rd32(chan->notifier_bo, chan->m2mf_ntfy/4 + 3)) {
ret = 0;
break;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
index 2683377f413..c3e953b0899 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
@@ -152,8 +152,6 @@ nouveau_mem_vram_fini(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- nouveau_bo_ref(NULL, &dev_priv->vga_ram);
-
ttm_bo_device_release(&dev_priv->ttm.bdev);
nouveau_ttm_global_release(dev_priv);
@@ -398,7 +396,7 @@ nouveau_mem_vram_init(struct drm_device *dev)
dma_bits = 40;
} else
if (drm_pci_device_is_pcie(dev) &&
- dev_priv->chipset != 0x40 &&
+ dev_priv->chipset > 0x40 &&
dev_priv->chipset != 0x45) {
if (pci_dma_supported(dev->pdev, DMA_BIT_MASK(39)))
dma_bits = 39;
@@ -552,6 +550,7 @@ nouveau_mem_timing_init(struct drm_device *dev)
u8 tRC; /* Byte 9 */
u8 tUNK_10, tUNK_11, tUNK_12, tUNK_13, tUNK_14;
u8 tUNK_18, tUNK_19, tUNK_20, tUNK_21;
+ u8 magic_number = 0; /* Yeah... sorry*/
u8 *mem = NULL, *entry;
int i, recordlen, entries;
@@ -596,6 +595,12 @@ nouveau_mem_timing_init(struct drm_device *dev)
if (!memtimings->timing)
return;
+ /* Get "some number" from the timing reg for NV_40
+ * Used in calculations later */
+ if(dev_priv->card_type == NV_40) {
+ magic_number = (nv_rd32(dev,0x100228) & 0x0f000000) >> 24;
+ }
+
entry = mem + mem[1];
for (i = 0; i < entries; i++, entry += recordlen) {
struct nouveau_pm_memtiming *timing = &pm->memtimings.timing[i];
@@ -635,36 +640,51 @@ nouveau_mem_timing_init(struct drm_device *dev)
/* XXX: I don't trust the -1's and +1's... they must come
* from somewhere! */
- timing->reg_100224 = ((tUNK_0 + tUNK_19 + 1) << 24 |
+ timing->reg_100224 = (tUNK_0 + tUNK_19 + 1 + magic_number) << 24 |
tUNK_18 << 16 |
- (tUNK_1 + tUNK_19 + 1) << 8 |
- (tUNK_2 - 1));
+ (tUNK_1 + tUNK_19 + 1 + magic_number) << 8;
+ if(dev_priv->chipset == 0xa8) {
+ timing->reg_100224 |= (tUNK_2 - 1);
+ } else {
+ timing->reg_100224 |= (tUNK_2 + 2 - magic_number);
+ }
timing->reg_100228 = (tUNK_12 << 16 | tUNK_11 << 8 | tUNK_10);
- if(recordlen > 19) {
- timing->reg_100228 += (tUNK_19 - 1) << 24;
- }/* I cannot back-up this else-statement right now
- else {
- timing->reg_100228 += tUNK_12 << 24;
- }*/
-
- /* XXX: reg_10022c */
- timing->reg_10022c = tUNK_2 - 1;
-
- timing->reg_100230 = (tUNK_20 << 24 | tUNK_21 << 16 |
- tUNK_13 << 8 | tUNK_13);
-
- /* XXX: +6? */
- timing->reg_100234 = (tRAS << 24 | (tUNK_19 + 6) << 8 | tRC);
- timing->reg_100234 += max(tUNK_10,tUNK_11) << 16;
-
- /* XXX; reg_100238, reg_10023c
- * reg: 0x00??????
- * reg_10023c:
- * 0 for pre-NV50 cards
- * 0x????0202 for NV50+ cards (empirical evidence) */
- if(dev_priv->card_type >= NV_50) {
+ if(dev_priv->chipset >= 0xa3 && dev_priv->chipset < 0xaa) {
+ timing->reg_100228 |= (tUNK_19 - 1) << 24;
+ }
+
+ if(dev_priv->card_type == NV_40) {
+ /* NV40: don't know what the rest of the regs are..
+ * And don't need to know either */
+ timing->reg_100228 |= 0x20200000 | magic_number << 24;
+ } else if(dev_priv->card_type >= NV_50) {
+ /* XXX: reg_10022c */
+ timing->reg_10022c = tUNK_2 - 1;
+
+ timing->reg_100230 = (tUNK_20 << 24 | tUNK_21 << 16 |
+ tUNK_13 << 8 | tUNK_13);
+
+ timing->reg_100234 = (tRAS << 24 | tRC);
+ timing->reg_100234 += max(tUNK_10,tUNK_11) << 16;
+
+ if(dev_priv->chipset < 0xa3) {
+ timing->reg_100234 |= (tUNK_2 + 2) << 8;
+ } else {
+ /* XXX: +6? */
+ timing->reg_100234 |= (tUNK_19 + 6) << 8;
+ }
+
+ /* XXX; reg_100238, reg_10023c
+ * reg_100238: 0x00??????
+ * reg_10023c: 0x!!??0202 for NV50+ cards (empirical evidence) */
timing->reg_10023c = 0x202;
+ if(dev_priv->chipset < 0xa3) {
+ timing->reg_10023c |= 0x4000000 | (tUNK_2 - 1) << 16;
+ } else {
+ /* currently unknown
+ * 10023c seen as 06xxxxxx, 0bxxxxxx or 0fxxxxxx */
+ }
}
NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", i,
@@ -675,7 +695,7 @@ nouveau_mem_timing_init(struct drm_device *dev)
timing->reg_100238, timing->reg_10023c);
}
- memtimings->nr_timing = entries;
+ memtimings->nr_timing = entries;
memtimings->supported = true;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_notifier.c b/drivers/gpu/drm/nouveau/nouveau_notifier.c
index 7ba3fc0b30c..5b39718ae1f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_notifier.c
+++ b/drivers/gpu/drm/nouveau/nouveau_notifier.c
@@ -35,19 +35,22 @@ nouveau_notifier_init_channel(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct nouveau_bo *ntfy = NULL;
- uint32_t flags;
+ uint32_t flags, ttmpl;
int ret;
- if (nouveau_vram_notify)
+ if (nouveau_vram_notify) {
flags = NOUVEAU_GEM_DOMAIN_VRAM;
- else
+ ttmpl = TTM_PL_FLAG_VRAM;
+ } else {
flags = NOUVEAU_GEM_DOMAIN_GART;
+ ttmpl = TTM_PL_FLAG_TT;
+ }
ret = nouveau_gem_new(dev, NULL, PAGE_SIZE, 0, flags, 0, 0, &ntfy);
if (ret)
return ret;
- ret = nouveau_bo_pin(ntfy, flags);
+ ret = nouveau_bo_pin(ntfy, ttmpl);
if (ret)
goto out_err;
diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c
index 4f00c87ed86..67a16e01ffa 100644
--- a/drivers/gpu/drm/nouveau/nouveau_object.c
+++ b/drivers/gpu/drm/nouveau/nouveau_object.c
@@ -1039,19 +1039,20 @@ nv_ro32(struct nouveau_gpuobj *gpuobj, u32 offset)
{
struct drm_nouveau_private *dev_priv = gpuobj->dev->dev_private;
struct drm_device *dev = gpuobj->dev;
+ unsigned long flags;
if (gpuobj->pinst == ~0 || !dev_priv->ramin_available) {
u64 ptr = gpuobj->vinst + offset;
u32 base = ptr >> 16;
u32 val;
- spin_lock(&dev_priv->ramin_lock);
+ spin_lock_irqsave(&dev_priv->vm_lock, flags);
if (dev_priv->ramin_base != base) {
dev_priv->ramin_base = base;
nv_wr32(dev, 0x001700, dev_priv->ramin_base);
}
val = nv_rd32(dev, 0x700000 + (ptr & 0xffff));
- spin_unlock(&dev_priv->ramin_lock);
+ spin_unlock_irqrestore(&dev_priv->vm_lock, flags);
return val;
}
@@ -1063,18 +1064,19 @@ nv_wo32(struct nouveau_gpuobj *gpuobj, u32 offset, u32 val)
{
struct drm_nouveau_private *dev_priv = gpuobj->dev->dev_private;
struct drm_device *dev = gpuobj->dev;
+ unsigned long flags;
if (gpuobj->pinst == ~0 || !dev_priv->ramin_available) {
u64 ptr = gpuobj->vinst + offset;
u32 base = ptr >> 16;
- spin_lock(&dev_priv->ramin_lock);
+ spin_lock_irqsave(&dev_priv->vm_lock, flags);
if (dev_priv->ramin_base != base) {
dev_priv->ramin_base = base;
nv_wr32(dev, 0x001700, dev_priv->ramin_base);
}
nv_wr32(dev, 0x700000 + (ptr & 0xffff), val);
- spin_unlock(&dev_priv->ramin_lock);
+ spin_unlock_irqrestore(&dev_priv->vm_lock, flags);
return;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c
index ac62a1b8c4f..670e3cb697e 100644
--- a/drivers/gpu/drm/nouveau/nouveau_perf.c
+++ b/drivers/gpu/drm/nouveau/nouveau_perf.c
@@ -134,7 +134,7 @@ nouveau_perf_init(struct drm_device *dev)
case 0x13:
case 0x15:
perflvl->fanspeed = entry[55];
- perflvl->voltage = entry[56];
+ perflvl->voltage = (recordlen > 56) ? entry[56] : 0;
perflvl->core = ROM32(entry[1]) * 10;
perflvl->memory = ROM32(entry[5]) * 20;
break;
diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
index a33fe401928..c77111eca6a 100644
--- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c
+++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
@@ -42,7 +42,8 @@ nouveau_sgdma_populate(struct ttm_backend *be, unsigned long num_pages,
nvbe->nr_pages = 0;
while (num_pages--) {
- if (dma_addrs[nvbe->nr_pages] != DMA_ERROR_CODE) {
+ /* this code path isn't called and is incorrect anyways */
+ if (0) { /*dma_addrs[nvbe->nr_pages] != DMA_ERROR_CODE)*/
nvbe->pages[nvbe->nr_pages] =
dma_addrs[nvbe->nr_pages];
nvbe->ttm_alloced[nvbe->nr_pages] = true;
@@ -55,6 +56,7 @@ nouveau_sgdma_populate(struct ttm_backend *be, unsigned long num_pages,
be->func->clear(be);
return -EFAULT;
}
+ nvbe->ttm_alloced[nvbe->nr_pages] = false;
}
nvbe->nr_pages++;
@@ -427,7 +429,7 @@ nouveau_sgdma_init(struct drm_device *dev)
u32 aper_size, align;
int ret;
- if (dev_priv->card_type >= NV_50 || drm_pci_device_is_pcie(dev))
+ if (dev_priv->card_type >= NV_40 && drm_pci_device_is_pcie(dev))
aper_size = 512 * 1024 * 1024;
else
aper_size = 64 * 1024 * 1024;
@@ -457,7 +459,7 @@ nouveau_sgdma_init(struct drm_device *dev)
dev_priv->gart_info.func = &nv50_sgdma_backend;
} else
if (drm_pci_device_is_pcie(dev) &&
- dev_priv->chipset != 0x40 && dev_priv->chipset != 0x45) {
+ dev_priv->chipset > 0x40 && dev_priv->chipset != 0x45) {
if (nv44_graph_class(dev)) {
dev_priv->gart_info.func = &nv44_sgdma_backend;
align = 512 * 1024;
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index 5bb2859001e..915fbce8959 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -376,15 +376,11 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->graph.destroy_context = nv50_graph_destroy_context;
engine->graph.load_context = nv50_graph_load_context;
engine->graph.unload_context = nv50_graph_unload_context;
- if (dev_priv->chipset != 0x86)
+ if (dev_priv->chipset == 0x50 ||
+ dev_priv->chipset == 0xac)
engine->graph.tlb_flush = nv50_graph_tlb_flush;
- else {
- /* from what i can see nvidia do this on every
- * pre-NVA3 board except NVAC, but, we've only
- * ever seen problems on NV86
- */
- engine->graph.tlb_flush = nv86_graph_tlb_flush;
- }
+ else
+ engine->graph.tlb_flush = nv84_graph_tlb_flush;
engine->fifo.channels = 128;
engine->fifo.init = nv50_fifo_init;
engine->fifo.takedown = nv50_fifo_takedown;
@@ -612,6 +608,7 @@ nouveau_card_init(struct drm_device *dev)
spin_lock_init(&dev_priv->channels.lock);
spin_lock_init(&dev_priv->tile.lock);
spin_lock_init(&dev_priv->context_switch_lock);
+ spin_lock_init(&dev_priv->vm_lock);
/* Make the CRTCs and I2C buses accessible */
ret = engine->display.early_init(dev);
@@ -771,6 +768,11 @@ static void nouveau_card_takedown(struct drm_device *dev)
engine->mc.takedown(dev);
engine->display.late_takedown(dev);
+ if (dev_priv->vga_ram) {
+ nouveau_bo_unpin(dev_priv->vga_ram);
+ nouveau_bo_ref(NULL, &dev_priv->vga_ram);
+ }
+
mutex_lock(&dev->struct_mutex);
ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM);
ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_TT);
diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c
index c82db37d9f4..12098bf839c 100644
--- a/drivers/gpu/drm/nouveau/nv04_dfp.c
+++ b/drivers/gpu/drm/nouveau/nv04_dfp.c
@@ -581,12 +581,13 @@ static void nv04_dfp_restore(struct drm_encoder *encoder)
int head = nv_encoder->restore.head;
if (nv_encoder->dcb->type == OUTPUT_LVDS) {
- struct drm_display_mode *native_mode = nouveau_encoder_connector_get(nv_encoder)->native_mode;
- if (native_mode)
- call_lvds_script(dev, nv_encoder->dcb, head, LVDS_PANEL_ON,
- native_mode->clock);
- else
- NV_ERROR(dev, "Not restoring LVDS without native mode\n");
+ struct nouveau_connector *connector =
+ nouveau_encoder_connector_get(nv_encoder);
+
+ if (connector && connector->native_mode)
+ call_lvds_script(dev, nv_encoder->dcb, head,
+ LVDS_PANEL_ON,
+ connector->native_mode->clock);
} else if (nv_encoder->dcb->type == OUTPUT_TMDS) {
int clock = nouveau_hw_pllvals_to_clk
diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c
index 2b9984027f4..a19ccaa025b 100644
--- a/drivers/gpu/drm/nouveau/nv50_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv50_crtc.c
@@ -469,9 +469,6 @@ nv50_crtc_wait_complete(struct drm_crtc *crtc)
start = ptimer->read(dev);
do {
- nv_wr32(dev, 0x61002c, 0x370);
- nv_wr32(dev, 0x000140, 1);
-
if (nv_ro32(disp->ntfy, 0x000))
return 0;
} while (ptimer->read(dev) - start < 2000000000ULL);
diff --git a/drivers/gpu/drm/nouveau/nv50_evo.c b/drivers/gpu/drm/nouveau/nv50_evo.c
index a2cfaa691e9..c8e83c1a4de 100644
--- a/drivers/gpu/drm/nouveau/nv50_evo.c
+++ b/drivers/gpu/drm/nouveau/nv50_evo.c
@@ -186,6 +186,7 @@ nv50_evo_channel_init(struct nouveau_channel *evo)
nv_mask(dev, 0x610028, 0x00000000, 0x00010001 << id);
evo->dma.max = (4096/4) - 2;
+ evo->dma.max &= ~7;
evo->dma.put = 0;
evo->dma.cur = evo->dma.put;
evo->dma.free = evo->dma.max - evo->dma.cur;
diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c
index 8675b00caf1..b02a5b1e7d3 100644
--- a/drivers/gpu/drm/nouveau/nv50_graph.c
+++ b/drivers/gpu/drm/nouveau/nv50_graph.c
@@ -503,7 +503,7 @@ nv50_graph_tlb_flush(struct drm_device *dev)
}
void
-nv86_graph_tlb_flush(struct drm_device *dev)
+nv84_graph_tlb_flush(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c
index a6f8aa651fc..4f95a1e5822 100644
--- a/drivers/gpu/drm/nouveau/nv50_instmem.c
+++ b/drivers/gpu/drm/nouveau/nv50_instmem.c
@@ -404,23 +404,25 @@ void
nv50_instmem_flush(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ unsigned long flags;
- spin_lock(&dev_priv->ramin_lock);
+ spin_lock_irqsave(&dev_priv->vm_lock, flags);
nv_wr32(dev, 0x00330c, 0x00000001);
if (!nv_wait(dev, 0x00330c, 0x00000002, 0x00000000))
NV_ERROR(dev, "PRAMIN flush timeout\n");
- spin_unlock(&dev_priv->ramin_lock);
+ spin_unlock_irqrestore(&dev_priv->vm_lock, flags);
}
void
nv84_instmem_flush(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ unsigned long flags;
- spin_lock(&dev_priv->ramin_lock);
+ spin_lock_irqsave(&dev_priv->vm_lock, flags);
nv_wr32(dev, 0x070000, 0x00000001);
if (!nv_wait(dev, 0x070000, 0x00000002, 0x00000000))
NV_ERROR(dev, "PRAMIN flush timeout\n");
- spin_unlock(&dev_priv->ramin_lock);
+ spin_unlock_irqrestore(&dev_priv->vm_lock, flags);
}
diff --git a/drivers/gpu/drm/nouveau/nv50_vm.c b/drivers/gpu/drm/nouveau/nv50_vm.c
index 4fd3432b5b8..6c269449074 100644
--- a/drivers/gpu/drm/nouveau/nv50_vm.c
+++ b/drivers/gpu/drm/nouveau/nv50_vm.c
@@ -174,10 +174,11 @@ void
nv50_vm_flush_engine(struct drm_device *dev, int engine)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ unsigned long flags;
- spin_lock(&dev_priv->ramin_lock);
+ spin_lock_irqsave(&dev_priv->vm_lock, flags);
nv_wr32(dev, 0x100c80, (engine << 16) | 1);
if (!nv_wait(dev, 0x100c80, 0x00000001, 0x00000000))
NV_ERROR(dev, "vm flush timeout: engine %d\n", engine);
- spin_unlock(&dev_priv->ramin_lock);
+ spin_unlock_irqrestore(&dev_priv->vm_lock, flags);
}
diff --git a/drivers/gpu/drm/nouveau/nvc0_vm.c b/drivers/gpu/drm/nouveau/nvc0_vm.c
index 69af0ba7edd..a179e6c55af 100644
--- a/drivers/gpu/drm/nouveau/nvc0_vm.c
+++ b/drivers/gpu/drm/nouveau/nvc0_vm.c
@@ -104,20 +104,27 @@ nvc0_vm_flush(struct nouveau_vm *vm)
struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem;
struct drm_device *dev = vm->dev;
struct nouveau_vm_pgd *vpgd;
- u32 r100c80, engine;
+ unsigned long flags;
+ u32 engine = (dev_priv->chan_vm == vm) ? 1 : 5;
pinstmem->flush(vm->dev);
- if (vm == dev_priv->chan_vm)
- engine = 1;
- else
- engine = 5;
-
+ spin_lock_irqsave(&dev_priv->vm_lock, flags);
list_for_each_entry(vpgd, &vm->pgd_list, head) {
- r100c80 = nv_rd32(dev, 0x100c80);
+ /* looks like maybe a "free flush slots" counter, the
+ * faster you write to 0x100cbc to more it decreases
+ */
+ if (!nv_wait_ne(dev, 0x100c80, 0x00ff0000, 0x00000000)) {
+ NV_ERROR(dev, "vm timeout 0: 0x%08x %d\n",
+ nv_rd32(dev, 0x100c80), engine);
+ }
nv_wr32(dev, 0x100cb8, vpgd->obj->vinst >> 8);
nv_wr32(dev, 0x100cbc, 0x80000000 | engine);
- if (!nv_wait(dev, 0x100c80, 0xffffffff, r100c80))
- NV_ERROR(dev, "vm flush timeout eng %d\n", engine);
+ /* wait for flush to be queued? */
+ if (!nv_wait(dev, 0x100c80, 0x00008000, 0x00008000)) {
+ NV_ERROR(dev, "vm timeout 1: 0x%08x %d\n",
+ nv_rd32(dev, 0x100c80), engine);
+ }
}
+ spin_unlock_irqrestore(&dev_priv->vm_lock, flags);
}
diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c
index 258fa5e7a2d..7bd74568909 100644
--- a/drivers/gpu/drm/radeon/atom.c
+++ b/drivers/gpu/drm/radeon/atom.c
@@ -32,6 +32,7 @@
#include "atom.h"
#include "atom-names.h"
#include "atom-bits.h"
+#include "radeon.h"
#define ATOM_COND_ABOVE 0
#define ATOM_COND_ABOVEOREQUAL 1
@@ -101,7 +102,9 @@ static void debug_print_spaces(int n)
static uint32_t atom_iio_execute(struct atom_context *ctx, int base,
uint32_t index, uint32_t data)
{
+ struct radeon_device *rdev = ctx->card->dev->dev_private;
uint32_t temp = 0xCDCDCDCD;
+
while (1)
switch (CU8(base)) {
case ATOM_IIO_NOP:
@@ -112,7 +115,8 @@ static uint32_t atom_iio_execute(struct atom_context *ctx, int base,
base += 3;
break;
case ATOM_IIO_WRITE:
- (void)ctx->card->ioreg_read(ctx->card, CU16(base + 1));
+ if (rdev->family == CHIP_RV515)
+ (void)ctx->card->ioreg_read(ctx->card, CU16(base + 1));
ctx->card->ioreg_write(ctx->card, CU16(base + 1), temp);
base += 3;
break;
@@ -131,7 +135,7 @@ static uint32_t atom_iio_execute(struct atom_context *ctx, int base,
case ATOM_IIO_MOVE_INDEX:
temp &=
~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
- CU8(base + 2));
+ CU8(base + 3));
temp |=
((index >> CU8(base + 2)) &
(0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base +
@@ -141,7 +145,7 @@ static uint32_t atom_iio_execute(struct atom_context *ctx, int base,
case ATOM_IIO_MOVE_DATA:
temp &=
~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
- CU8(base + 2));
+ CU8(base + 3));
temp |=
((data >> CU8(base + 2)) &
(0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base +
@@ -151,7 +155,7 @@ static uint32_t atom_iio_execute(struct atom_context *ctx, int base,
case ATOM_IIO_MOVE_ATTR:
temp &=
~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
- CU8(base + 2));
+ CU8(base + 3));
temp |=
((ctx->
io_attr >> CU8(base + 2)) & (0xFFFFFFFF >> (32 -
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index b41ec59c710..529a3a70473 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -531,6 +531,9 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
else
pll->flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
+
+ if (rdev->family < CHIP_RV770)
+ pll->flags |= RADEON_PLL_PREFER_MINM_OVER_MAXP;
} else {
pll->flags |= RADEON_PLL_LEGACY;
@@ -559,7 +562,6 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
if (ss_enabled) {
if (ss->refdiv) {
- pll->flags |= RADEON_PLL_PREFER_MINM_OVER_MAXP;
pll->flags |= RADEON_PLL_USE_REF_DIV;
pll->reference_div = ss->refdiv;
if (ASIC_IS_AVIVO(rdev))
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index 0b0cc74c08c..9073e3bfb08 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -120,11 +120,16 @@ void evergreen_pm_misc(struct radeon_device *rdev)
struct radeon_power_state *ps = &rdev->pm.power_state[req_ps_idx];
struct radeon_voltage *voltage = &ps->clock_info[req_cm_idx].voltage;
- if ((voltage->type == VOLTAGE_SW) && voltage->voltage) {
- if (voltage->voltage != rdev->pm.current_vddc) {
- radeon_atom_set_voltage(rdev, voltage->voltage);
+ if (voltage->type == VOLTAGE_SW) {
+ if (voltage->voltage && (voltage->voltage != rdev->pm.current_vddc)) {
+ radeon_atom_set_voltage(rdev, voltage->voltage, SET_VOLTAGE_TYPE_ASIC_VDDC);
rdev->pm.current_vddc = voltage->voltage;
- DRM_DEBUG("Setting: v: %d\n", voltage->voltage);
+ DRM_DEBUG("Setting: vddc: %d\n", voltage->voltage);
+ }
+ if (voltage->vddci && (voltage->vddci != rdev->pm.current_vddci)) {
+ radeon_atom_set_voltage(rdev, voltage->vddci, SET_VOLTAGE_TYPE_ASIC_VDDCI);
+ rdev->pm.current_vddci = voltage->vddci;
+ DRM_DEBUG("Setting: vddci: %d\n", voltage->vddci);
}
}
}
@@ -348,7 +353,7 @@ static u32 evergreen_line_buffer_adjust(struct radeon_device *rdev,
struct drm_display_mode *mode,
struct drm_display_mode *other_mode)
{
- u32 tmp = 0;
+ u32 tmp;
/*
* Line Buffer Setup
* There are 3 line buffers, each one shared by 2 display controllers.
@@ -358,64 +363,63 @@ static u32 evergreen_line_buffer_adjust(struct radeon_device *rdev,
* first display controller
* 0 - first half of lb (3840 * 2)
* 1 - first 3/4 of lb (5760 * 2)
- * 2 - whole lb (7680 * 2)
+ * 2 - whole lb (7680 * 2), other crtc must be disabled
* 3 - first 1/4 of lb (1920 * 2)
* second display controller
* 4 - second half of lb (3840 * 2)
* 5 - second 3/4 of lb (5760 * 2)
- * 6 - whole lb (7680 * 2)
+ * 6 - whole lb (7680 * 2), other crtc must be disabled
* 7 - last 1/4 of lb (1920 * 2)
*/
- if (mode && other_mode) {
- if (mode->hdisplay > other_mode->hdisplay) {
- if (mode->hdisplay > 2560)
- tmp = 1; /* 3/4 */
- else
- tmp = 0; /* 1/2 */
- } else if (other_mode->hdisplay > mode->hdisplay) {
- if (other_mode->hdisplay > 2560)
- tmp = 3; /* 1/4 */
- else
- tmp = 0; /* 1/2 */
- } else
+ /* this can get tricky if we have two large displays on a paired group
+ * of crtcs. Ideally for multiple large displays we'd assign them to
+ * non-linked crtcs for maximum line buffer allocation.
+ */
+ if (radeon_crtc->base.enabled && mode) {
+ if (other_mode)
tmp = 0; /* 1/2 */
- } else if (mode)
- tmp = 2; /* whole */
- else if (other_mode)
- tmp = 3; /* 1/4 */
+ else
+ tmp = 2; /* whole */
+ } else
+ tmp = 0;
/* second controller of the pair uses second half of the lb */
if (radeon_crtc->crtc_id % 2)
tmp += 4;
WREG32(DC_LB_MEMORY_SPLIT + radeon_crtc->crtc_offset, tmp);
- switch (tmp) {
- case 0:
- case 4:
- default:
- if (ASIC_IS_DCE5(rdev))
- return 4096 * 2;
- else
- return 3840 * 2;
- case 1:
- case 5:
- if (ASIC_IS_DCE5(rdev))
- return 6144 * 2;
- else
- return 5760 * 2;
- case 2:
- case 6:
- if (ASIC_IS_DCE5(rdev))
- return 8192 * 2;
- else
- return 7680 * 2;
- case 3:
- case 7:
- if (ASIC_IS_DCE5(rdev))
- return 2048 * 2;
- else
- return 1920 * 2;
+ if (radeon_crtc->base.enabled && mode) {
+ switch (tmp) {
+ case 0:
+ case 4:
+ default:
+ if (ASIC_IS_DCE5(rdev))
+ return 4096 * 2;
+ else
+ return 3840 * 2;
+ case 1:
+ case 5:
+ if (ASIC_IS_DCE5(rdev))
+ return 6144 * 2;
+ else
+ return 5760 * 2;
+ case 2:
+ case 6:
+ if (ASIC_IS_DCE5(rdev))
+ return 8192 * 2;
+ else
+ return 7680 * 2;
+ case 3:
+ case 7:
+ if (ASIC_IS_DCE5(rdev))
+ return 2048 * 2;
+ else
+ return 1920 * 2;
+ }
}
+
+ /* controller not enabled, so no lb used */
+ return 0;
}
static u32 evergreen_get_number_of_dram_channels(struct radeon_device *rdev)
@@ -858,9 +862,15 @@ int evergreen_pcie_gart_enable(struct radeon_device *rdev)
SYSTEM_ACCESS_MODE_NOT_IN_SYS |
SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU |
EFFECTIVE_L1_TLB_SIZE(5) | EFFECTIVE_L1_QUEUE_SIZE(5);
- WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp);
- WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp);
- WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp);
+ if (rdev->flags & RADEON_IS_IGP) {
+ WREG32(FUS_MC_VM_MD_L1_TLB0_CNTL, tmp);
+ WREG32(FUS_MC_VM_MD_L1_TLB1_CNTL, tmp);
+ WREG32(FUS_MC_VM_MD_L1_TLB2_CNTL, tmp);
+ } else {
+ WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp);
+ WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp);
+ WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp);
+ }
WREG32(MC_VM_MB_L1_TLB0_CNTL, tmp);
WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp);
WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp);
@@ -1770,7 +1780,10 @@ static void evergreen_gpu_init(struct radeon_device *rdev)
mc_shared_chmap = RREG32(MC_SHARED_CHMAP);
- mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG);
+ if (rdev->flags & RADEON_IS_IGP)
+ mc_arb_ramcfg = RREG32(FUS_MC_ARB_RAMCFG);
+ else
+ mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG);
switch (rdev->config.evergreen.max_tile_pipes) {
case 1:
@@ -2576,7 +2589,7 @@ static inline u32 evergreen_get_ih_wptr(struct radeon_device *rdev)
u32 wptr, tmp;
if (rdev->wb.enabled)
- wptr = rdev->wb.wb[R600_WB_IH_WPTR_OFFSET/4];
+ wptr = le32_to_cpu(rdev->wb.wb[R600_WB_IH_WPTR_OFFSET/4]);
else
wptr = RREG32(IH_RB_WPTR);
@@ -2919,11 +2932,6 @@ static int evergreen_startup(struct radeon_device *rdev)
rdev->asic->copy = NULL;
dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r);
}
- /* XXX: ontario has problems blitting to gart at the moment */
- if (rdev->family == CHIP_PALM) {
- rdev->asic->copy = NULL;
- radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size);
- }
/* allocate wb buffer */
r = radeon_wb_init(rdev);
@@ -3036,9 +3044,6 @@ int evergreen_init(struct radeon_device *rdev)
{
int r;
- r = radeon_dummy_page_init(rdev);
- if (r)
- return r;
/* This don't do much */
r = radeon_gem_init(rdev);
if (r)
@@ -3150,7 +3155,6 @@ void evergreen_fini(struct radeon_device *rdev)
radeon_atombios_fini(rdev);
kfree(rdev->bios);
rdev->bios = NULL;
- radeon_dummy_page_fini(rdev);
}
static void evergreen_pcie_gen2_enable(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h
index 9aaa3f0c937..fc40e0cc345 100644
--- a/drivers/gpu/drm/radeon/evergreend.h
+++ b/drivers/gpu/drm/radeon/evergreend.h
@@ -200,6 +200,7 @@
#define BURSTLENGTH_SHIFT 9
#define BURSTLENGTH_MASK 0x00000200
#define CHANSIZE_OVERRIDE (1 << 11)
+#define FUS_MC_ARB_RAMCFG 0x2768
#define MC_VM_AGP_TOP 0x2028
#define MC_VM_AGP_BOT 0x202C
#define MC_VM_AGP_BASE 0x2030
@@ -221,6 +222,11 @@
#define MC_VM_MD_L1_TLB0_CNTL 0x2654
#define MC_VM_MD_L1_TLB1_CNTL 0x2658
#define MC_VM_MD_L1_TLB2_CNTL 0x265C
+
+#define FUS_MC_VM_MD_L1_TLB0_CNTL 0x265C
+#define FUS_MC_VM_MD_L1_TLB1_CNTL 0x2660
+#define FUS_MC_VM_MD_L1_TLB2_CNTL 0x2664
+
#define MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR 0x203C
#define MC_VM_SYSTEM_APERTURE_HIGH_ADDR 0x2038
#define MC_VM_SYSTEM_APERTURE_LOW_ADDR 0x2034
diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c
index 7aade20f63a..3d8a7634bbe 100644
--- a/drivers/gpu/drm/radeon/ni.c
+++ b/drivers/gpu/drm/radeon/ni.c
@@ -674,7 +674,7 @@ static void cayman_gpu_init(struct radeon_device *rdev)
cc_rb_backend_disable = RREG32(CC_RB_BACKEND_DISABLE);
cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG);
- cgts_tcc_disable = RREG32(CGTS_TCC_DISABLE);
+ cgts_tcc_disable = 0xff000000;
gc_user_rb_backend_disable = RREG32(GC_USER_RB_BACKEND_DISABLE);
gc_user_shader_pipe_config = RREG32(GC_USER_SHADER_PIPE_CONFIG);
cgts_user_tcc_disable = RREG32(CGTS_USER_TCC_DISABLE);
@@ -871,7 +871,7 @@ static void cayman_gpu_init(struct radeon_device *rdev)
smx_dc_ctl0 = RREG32(SMX_DC_CTL0);
smx_dc_ctl0 &= ~NUMBER_OF_SETS(0x1ff);
- smx_dc_ctl0 |= NUMBER_OF_SETS(rdev->config.evergreen.sx_num_of_sets);
+ smx_dc_ctl0 |= NUMBER_OF_SETS(rdev->config.cayman.sx_num_of_sets);
WREG32(SMX_DC_CTL0, smx_dc_ctl0);
WREG32(SPI_CONFIG_CNTL_1, VTX_DONE_DELAY(4) | CRC_SIMD_ID_WADDR_DISABLE);
@@ -887,20 +887,20 @@ static void cayman_gpu_init(struct radeon_device *rdev)
WREG32(TA_CNTL_AUX, DISABLE_CUBE_ANISO);
- WREG32(SX_EXPORT_BUFFER_SIZES, (COLOR_BUFFER_SIZE((rdev->config.evergreen.sx_max_export_size / 4) - 1) |
- POSITION_BUFFER_SIZE((rdev->config.evergreen.sx_max_export_pos_size / 4) - 1) |
- SMX_BUFFER_SIZE((rdev->config.evergreen.sx_max_export_smx_size / 4) - 1)));
+ WREG32(SX_EXPORT_BUFFER_SIZES, (COLOR_BUFFER_SIZE((rdev->config.cayman.sx_max_export_size / 4) - 1) |
+ POSITION_BUFFER_SIZE((rdev->config.cayman.sx_max_export_pos_size / 4) - 1) |
+ SMX_BUFFER_SIZE((rdev->config.cayman.sx_max_export_smx_size / 4) - 1)));
- WREG32(PA_SC_FIFO_SIZE, (SC_PRIM_FIFO_SIZE(rdev->config.evergreen.sc_prim_fifo_size) |
- SC_HIZ_TILE_FIFO_SIZE(rdev->config.evergreen.sc_hiz_tile_fifo_size) |
- SC_EARLYZ_TILE_FIFO_SIZE(rdev->config.evergreen.sc_earlyz_tile_fifo_size)));
+ WREG32(PA_SC_FIFO_SIZE, (SC_PRIM_FIFO_SIZE(rdev->config.cayman.sc_prim_fifo_size) |
+ SC_HIZ_TILE_FIFO_SIZE(rdev->config.cayman.sc_hiz_tile_fifo_size) |
+ SC_EARLYZ_TILE_FIFO_SIZE(rdev->config.cayman.sc_earlyz_tile_fifo_size)));
WREG32(VGT_NUM_INSTANCES, 1);
WREG32(CP_PERFMON_CNTL, 0);
- WREG32(SQ_MS_FIFO_SIZES, (CACHE_FIFO_SIZE(16 * rdev->config.evergreen.sq_num_cf_insts) |
+ WREG32(SQ_MS_FIFO_SIZES, (CACHE_FIFO_SIZE(16 * rdev->config.cayman.sq_num_cf_insts) |
FETCH_FIFO_HIWATER(0x4) |
DONE_FIFO_HIWATER(0xe0) |
ALU_UPDATE_FIFO_HIWATER(0x8)));
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index be271c42de4..6f27593901c 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -587,7 +587,7 @@ void r600_pm_misc(struct radeon_device *rdev)
if ((voltage->type == VOLTAGE_SW) && voltage->voltage) {
if (voltage->voltage != rdev->pm.current_vddc) {
- radeon_atom_set_voltage(rdev, voltage->voltage);
+ radeon_atom_set_voltage(rdev, voltage->voltage, SET_VOLTAGE_TYPE_ASIC_VDDC);
rdev->pm.current_vddc = voltage->voltage;
DRM_DEBUG_DRIVER("Setting: v: %d\n", voltage->voltage);
}
@@ -2509,9 +2509,6 @@ int r600_init(struct radeon_device *rdev)
{
int r;
- r = radeon_dummy_page_init(rdev);
- if (r)
- return r;
if (r600_debugfs_mc_info_init(rdev)) {
DRM_ERROR("Failed to register debugfs file for mc !\n");
}
@@ -2625,7 +2622,6 @@ void r600_fini(struct radeon_device *rdev)
radeon_atombios_fini(rdev);
kfree(rdev->bios);
rdev->bios = NULL;
- radeon_dummy_page_fini(rdev);
}
@@ -3235,7 +3231,7 @@ static inline u32 r600_get_ih_wptr(struct radeon_device *rdev)
u32 wptr, tmp;
if (rdev->wb.enabled)
- wptr = rdev->wb.wb[R600_WB_IH_WPTR_OFFSET/4];
+ wptr = le32_to_cpu(rdev->wb.wb[R600_WB_IH_WPTR_OFFSET/4]);
else
wptr = RREG32(IH_RB_WPTR);
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 93f536594c7..ba643b57605 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -177,7 +177,7 @@ void radeon_pm_suspend(struct radeon_device *rdev);
void radeon_pm_resume(struct radeon_device *rdev);
void radeon_combios_get_power_modes(struct radeon_device *rdev);
void radeon_atombios_get_power_modes(struct radeon_device *rdev);
-void radeon_atom_set_voltage(struct radeon_device *rdev, u16 level);
+void radeon_atom_set_voltage(struct radeon_device *rdev, u16 voltage_level, u8 voltage_type);
void rs690_pm_info(struct radeon_device *rdev);
extern int rv6xx_get_temp(struct radeon_device *rdev);
extern int rv770_get_temp(struct radeon_device *rdev);
@@ -767,7 +767,9 @@ struct radeon_voltage {
u8 vddci_id; /* index into vddci voltage table */
bool vddci_enabled;
/* r6xx+ sw */
- u32 voltage;
+ u16 voltage;
+ /* evergreen+ vddci */
+ u16 vddci;
};
/* clock mode flags */
@@ -835,10 +837,12 @@ struct radeon_pm {
int default_power_state_index;
u32 current_sclk;
u32 current_mclk;
- u32 current_vddc;
+ u16 current_vddc;
+ u16 current_vddci;
u32 default_sclk;
u32 default_mclk;
- u32 default_vddc;
+ u16 default_vddc;
+ u16 default_vddci;
struct radeon_i2c_chan *i2c_bus;
/* selected pm method */
enum radeon_pm_method pm_method;
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c
index eb888ee5f67..ca576191d05 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.c
+++ b/drivers/gpu/drm/radeon/radeon_asic.c
@@ -94,7 +94,7 @@ static void radeon_register_accessor_init(struct radeon_device *rdev)
rdev->mc_rreg = &rs600_mc_rreg;
rdev->mc_wreg = &rs600_mc_wreg;
}
- if ((rdev->family >= CHIP_R600) && (rdev->family <= CHIP_HEMLOCK)) {
+ if (rdev->family >= CHIP_R600) {
rdev->pciep_rreg = &r600_pciep_rreg;
rdev->pciep_wreg = &r600_pciep_wreg;
}
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index 99768d9d91d..90dfb2b8cf0 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -431,7 +431,7 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev,
}
}
- /* Acer laptop (Acer TravelMate 5730G) has an HDMI port
+ /* Acer laptop (Acer TravelMate 5730/5730G) has an HDMI port
* on the laptop and a DVI port on the docking station and
* both share the same encoder, hpd pin, and ddc line.
* So while the bios table is technically correct,
@@ -440,7 +440,7 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev,
* with different crtcs which isn't possible on the hardware
* side and leaves no crtcs for LVDS or VGA.
*/
- if ((dev->pdev->device == 0x95c4) &&
+ if (((dev->pdev->device == 0x95c4) || (dev->pdev->device == 0x9591)) &&
(dev->pdev->subsystem_vendor == 0x1025) &&
(dev->pdev->subsystem_device == 0x013c)) {
if ((*connector_type == DRM_MODE_CONNECTOR_DVII) &&
@@ -1574,9 +1574,17 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct
ATOM_FAKE_EDID_PATCH_RECORD *fake_edid_record;
ATOM_PANEL_RESOLUTION_PATCH_RECORD *panel_res_record;
bool bad_record = false;
- u8 *record = (u8 *)(mode_info->atom_context->bios +
- data_offset +
- le16_to_cpu(lvds_info->info.usModePatchTableOffset));
+ u8 *record;
+
+ if ((frev == 1) && (crev < 2))
+ /* absolute */
+ record = (u8 *)(mode_info->atom_context->bios +
+ le16_to_cpu(lvds_info->info.usModePatchTableOffset));
+ else
+ /* relative */
+ record = (u8 *)(mode_info->atom_context->bios +
+ data_offset +
+ le16_to_cpu(lvds_info->info.usModePatchTableOffset));
while (*record != ATOM_RECORD_END_TYPE) {
switch (*record) {
case LCD_MODE_PATCH_RECORD_MODE_TYPE:
@@ -1599,9 +1607,10 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct
memcpy((u8 *)edid, (u8 *)&fake_edid_record->ucFakeEDIDString[0],
fake_edid_record->ucFakeEDIDLength);
- if (drm_edid_is_valid(edid))
+ if (drm_edid_is_valid(edid)) {
rdev->mode_info.bios_hardcoded_edid = edid;
- else
+ rdev->mode_info.bios_hardcoded_edid_size = edid_size;
+ } else
kfree(edid);
}
}
@@ -2176,24 +2185,27 @@ static void radeon_atombios_add_pplib_thermal_controller(struct radeon_device *r
}
}
-static u16 radeon_atombios_get_default_vddc(struct radeon_device *rdev)
+static void radeon_atombios_get_default_voltages(struct radeon_device *rdev,
+ u16 *vddc, u16 *vddci)
{
struct radeon_mode_info *mode_info = &rdev->mode_info;
int index = GetIndexIntoMasterTable(DATA, FirmwareInfo);
u8 frev, crev;
u16 data_offset;
union firmware_info *firmware_info;
- u16 vddc = 0;
+
+ *vddc = 0;
+ *vddci = 0;
if (atom_parse_data_header(mode_info->atom_context, index, NULL,
&frev, &crev, &data_offset)) {
firmware_info =
(union firmware_info *)(mode_info->atom_context->bios +
data_offset);
- vddc = le16_to_cpu(firmware_info->info_14.usBootUpVDDCVoltage);
+ *vddc = le16_to_cpu(firmware_info->info_14.usBootUpVDDCVoltage);
+ if ((frev == 2) && (crev >= 2))
+ *vddci = le16_to_cpu(firmware_info->info_22.usBootUpVDDCIVoltage);
}
-
- return vddc;
}
static void radeon_atombios_parse_pplib_non_clock_info(struct radeon_device *rdev,
@@ -2203,7 +2215,9 @@ static void radeon_atombios_parse_pplib_non_clock_info(struct radeon_device *rde
int j;
u32 misc = le32_to_cpu(non_clock_info->ulCapsAndSettings);
u32 misc2 = le16_to_cpu(non_clock_info->usClassification);
- u16 vddc = radeon_atombios_get_default_vddc(rdev);
+ u16 vddc, vddci;
+
+ radeon_atombios_get_default_voltages(rdev, &vddc, &vddci);
rdev->pm.power_state[state_index].misc = misc;
rdev->pm.power_state[state_index].misc2 = misc2;
@@ -2244,6 +2258,7 @@ static void radeon_atombios_parse_pplib_non_clock_info(struct radeon_device *rde
rdev->pm.default_sclk = rdev->pm.power_state[state_index].clock_info[0].sclk;
rdev->pm.default_mclk = rdev->pm.power_state[state_index].clock_info[0].mclk;
rdev->pm.default_vddc = rdev->pm.power_state[state_index].clock_info[0].voltage.voltage;
+ rdev->pm.default_vddci = rdev->pm.power_state[state_index].clock_info[0].voltage.vddci;
} else {
/* patch the table values with the default slck/mclk from firmware info */
for (j = 0; j < mode_index; j++) {
@@ -2286,6 +2301,8 @@ static bool radeon_atombios_parse_pplib_clock_info(struct radeon_device *rdev,
VOLTAGE_SW;
rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage =
le16_to_cpu(clock_info->evergreen.usVDDC);
+ rdev->pm.power_state[state_index].clock_info[mode_index].voltage.vddci =
+ le16_to_cpu(clock_info->evergreen.usVDDCI);
} else {
sclk = le16_to_cpu(clock_info->r600.usEngineClockLow);
sclk |= clock_info->r600.ucEngineClockHigh << 16;
@@ -2577,25 +2594,25 @@ union set_voltage {
struct _SET_VOLTAGE_PARAMETERS_V2 v2;
};
-void radeon_atom_set_voltage(struct radeon_device *rdev, u16 level)
+void radeon_atom_set_voltage(struct radeon_device *rdev, u16 voltage_level, u8 voltage_type)
{
union set_voltage args;
int index = GetIndexIntoMasterTable(COMMAND, SetVoltage);
- u8 frev, crev, volt_index = level;
+ u8 frev, crev, volt_index = voltage_level;
if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
return;
switch (crev) {
case 1:
- args.v1.ucVoltageType = SET_VOLTAGE_TYPE_ASIC_VDDC;
+ args.v1.ucVoltageType = voltage_type;
args.v1.ucVoltageMode = SET_ASIC_VOLTAGE_MODE_ALL_SOURCE;
args.v1.ucVoltageIndex = volt_index;
break;
case 2:
- args.v2.ucVoltageType = SET_VOLTAGE_TYPE_ASIC_VDDC;
+ args.v2.ucVoltageType = voltage_type;
args.v2.ucVoltageMode = SET_ASIC_VOLTAGE_MODE_SET_VOLTAGE;
- args.v2.usVoltageLevel = cpu_to_le16(level);
+ args.v2.usVoltageLevel = cpu_to_le16(voltage_level);
break;
default:
DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
index ed5dfe58f29..9d95792bea3 100644
--- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
+++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
@@ -15,6 +15,9 @@
#define ATPX_VERSION 0
#define ATPX_GPU_PWR 2
#define ATPX_MUX_SELECT 3
+#define ATPX_I2C_MUX_SELECT 4
+#define ATPX_SWITCH_START 5
+#define ATPX_SWITCH_END 6
#define ATPX_INTEGRATED 0
#define ATPX_DISCRETE 1
@@ -149,13 +152,35 @@ static int radeon_atpx_switch_mux(acpi_handle handle, int mux_id)
return radeon_atpx_execute(handle, ATPX_MUX_SELECT, mux_id);
}
+static int radeon_atpx_switch_i2c_mux(acpi_handle handle, int mux_id)
+{
+ return radeon_atpx_execute(handle, ATPX_I2C_MUX_SELECT, mux_id);
+}
+
+static int radeon_atpx_switch_start(acpi_handle handle, int gpu_id)
+{
+ return radeon_atpx_execute(handle, ATPX_SWITCH_START, gpu_id);
+}
+
+static int radeon_atpx_switch_end(acpi_handle handle, int gpu_id)
+{
+ return radeon_atpx_execute(handle, ATPX_SWITCH_END, gpu_id);
+}
static int radeon_atpx_switchto(enum vga_switcheroo_client_id id)
{
+ int gpu_id;
+
if (id == VGA_SWITCHEROO_IGD)
- radeon_atpx_switch_mux(radeon_atpx_priv.atpx_handle, 0);
+ gpu_id = ATPX_INTEGRATED;
else
- radeon_atpx_switch_mux(radeon_atpx_priv.atpx_handle, 1);
+ gpu_id = ATPX_DISCRETE;
+
+ radeon_atpx_switch_start(radeon_atpx_priv.atpx_handle, gpu_id);
+ radeon_atpx_switch_mux(radeon_atpx_priv.atpx_handle, gpu_id);
+ radeon_atpx_switch_i2c_mux(radeon_atpx_priv.atpx_handle, gpu_id);
+ radeon_atpx_switch_end(radeon_atpx_priv.atpx_handle, gpu_id);
+
return 0;
}
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 2ef6d513506..5f45fa12bb8 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -1199,7 +1199,7 @@ radeon_add_atom_connector(struct drm_device *dev,
if (router->ddc_valid || router->cd_valid) {
radeon_connector->router_bus = radeon_i2c_lookup(rdev, &router->i2c_info);
if (!radeon_connector->router_bus)
- goto failed;
+ DRM_ERROR("Failed to assign router i2c bus! Check dmesg for i2c errors.\n");
}
switch (connector_type) {
case DRM_MODE_CONNECTOR_VGA:
@@ -1208,7 +1208,7 @@ radeon_add_atom_connector(struct drm_device *dev,
if (i2c_bus->valid) {
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("VGA: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
radeon_connector->dac_load_detect = true;
drm_connector_attach_property(&radeon_connector->base,
@@ -1226,7 +1226,7 @@ radeon_add_atom_connector(struct drm_device *dev,
if (i2c_bus->valid) {
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("DVIA: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
radeon_connector->dac_load_detect = true;
drm_connector_attach_property(&radeon_connector->base,
@@ -1249,7 +1249,7 @@ radeon_add_atom_connector(struct drm_device *dev,
if (i2c_bus->valid) {
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("DVI: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
subpixel_order = SubPixelHorizontalRGB;
drm_connector_attach_property(&radeon_connector->base,
@@ -1290,7 +1290,7 @@ radeon_add_atom_connector(struct drm_device *dev,
if (i2c_bus->valid) {
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("HDMI: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
drm_connector_attach_property(&radeon_connector->base,
rdev->mode_info.coherent_mode_property,
@@ -1329,10 +1329,10 @@ radeon_add_atom_connector(struct drm_device *dev,
else
radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "DP-auxch");
if (!radeon_dig_connector->dp_i2c_bus)
- goto failed;
+ DRM_ERROR("DP: Failed to assign dp ddc bus! Check dmesg for i2c errors.\n");
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
subpixel_order = SubPixelHorizontalRGB;
drm_connector_attach_property(&radeon_connector->base,
@@ -1381,7 +1381,7 @@ radeon_add_atom_connector(struct drm_device *dev,
if (i2c_bus->valid) {
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("LVDS: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
drm_connector_attach_property(&radeon_connector->base,
dev->mode_config.scaling_mode_property,
@@ -1457,7 +1457,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
if (i2c_bus->valid) {
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("VGA: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
radeon_connector->dac_load_detect = true;
drm_connector_attach_property(&radeon_connector->base,
@@ -1475,7 +1475,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
if (i2c_bus->valid) {
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("DVIA: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
radeon_connector->dac_load_detect = true;
drm_connector_attach_property(&radeon_connector->base,
@@ -1493,7 +1493,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
if (i2c_bus->valid) {
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("DVI: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
if (connector_type == DRM_MODE_CONNECTOR_DVII) {
radeon_connector->dac_load_detect = true;
@@ -1538,7 +1538,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
if (i2c_bus->valid) {
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- goto failed;
+ DRM_ERROR("LVDS: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
drm_connector_attach_property(&radeon_connector->base,
dev->mode_config.scaling_mode_property,
@@ -1567,9 +1567,4 @@ radeon_add_legacy_connector(struct drm_device *dev,
radeon_legacy_backlight_init(radeon_encoder, connector);
}
}
- return;
-
-failed:
- drm_connector_cleanup(connector);
- kfree(connector);
}
diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c
index bdf2fa1189a..3189a7efb2e 100644
--- a/drivers/gpu/drm/radeon/radeon_cursor.c
+++ b/drivers/gpu/drm/radeon/radeon_cursor.c
@@ -167,9 +167,6 @@ int radeon_crtc_cursor_set(struct drm_crtc *crtc,
return -EINVAL;
}
- radeon_crtc->cursor_width = width;
- radeon_crtc->cursor_height = height;
-
obj = drm_gem_object_lookup(crtc->dev, file_priv, handle);
if (!obj) {
DRM_ERROR("Cannot find cursor object %x for crtc %d\n", handle, radeon_crtc->crtc_id);
@@ -180,6 +177,9 @@ int radeon_crtc_cursor_set(struct drm_crtc *crtc,
if (ret)
goto fail;
+ radeon_crtc->cursor_width = width;
+ radeon_crtc->cursor_height = height;
+
radeon_lock_cursor(crtc, true);
/* XXX only 27 bit offset for legacy cursor */
radeon_set_cursor(crtc, obj, gpu_addr);
diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c
index 9e59868d354..bbcd1dd7bac 100644
--- a/drivers/gpu/drm/radeon/radeon_fence.c
+++ b/drivers/gpu/drm/radeon/radeon_fence.c
@@ -79,7 +79,7 @@ static bool radeon_fence_poll_locked(struct radeon_device *rdev)
scratch_index = R600_WB_EVENT_OFFSET + rdev->fence_drv.scratch_reg - rdev->scratch.reg_base;
else
scratch_index = RADEON_WB_SCRATCH_OFFSET + rdev->fence_drv.scratch_reg - rdev->scratch.reg_base;
- seq = rdev->wb.wb[scratch_index/4];
+ seq = le32_to_cpu(rdev->wb.wb[scratch_index/4]);
} else
seq = RREG32(rdev->fence_drv.scratch_reg);
if (seq != rdev->fence_drv.last_seq) {
diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c
index f0534ef2f33..a533f52fd16 100644
--- a/drivers/gpu/drm/radeon/radeon_gart.c
+++ b/drivers/gpu/drm/radeon/radeon_gart.c
@@ -181,9 +181,9 @@ int radeon_gart_bind(struct radeon_device *rdev, unsigned offset,
p = t / (PAGE_SIZE / RADEON_GPU_PAGE_SIZE);
for (i = 0; i < pages; i++, p++) {
- /* On TTM path, we only use the DMA API if TTM_PAGE_FLAG_DMA32
- * is requested. */
- if (dma_addr[i] != DMA_ERROR_CODE) {
+ /* we reverted the patch using dma_addr in TTM for now but this
+ * code stops building on alpha so just comment it out for now */
+ if (0) { /*dma_addr[i] != DMA_ERROR_CODE) */
rdev->gart.ttm_alloced[p] = true;
rdev->gart.pages_addr[p] = dma_addr[i];
} else {
@@ -285,4 +285,6 @@ void radeon_gart_fini(struct radeon_device *rdev)
rdev->gart.pages = NULL;
rdev->gart.pages_addr = NULL;
rdev->gart.ttm_alloced = NULL;
+
+ radeon_dummy_page_fini(rdev);
}
diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c
index ded2a45bc95..983cbac75af 100644
--- a/drivers/gpu/drm/radeon/radeon_i2c.c
+++ b/drivers/gpu/drm/radeon/radeon_i2c.c
@@ -1062,7 +1062,7 @@ void radeon_i2c_get_byte(struct radeon_i2c_chan *i2c_bus,
*val = in_buf[0];
DRM_DEBUG("val = 0x%02x\n", *val);
} else {
- DRM_ERROR("i2c 0x%02x 0x%02x read failed\n",
+ DRM_DEBUG("i2c 0x%02x 0x%02x read failed\n",
addr, *val);
}
}
@@ -1084,7 +1084,7 @@ void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c_bus,
out_buf[1] = val;
if (i2c_transfer(&i2c_bus->adapter, &msg, 1) != 1)
- DRM_ERROR("i2c 0x%02x 0x%02x write failed\n",
+ DRM_DEBUG("i2c 0x%02x 0x%02x write failed\n",
addr, val);
}
@@ -1096,6 +1096,9 @@ void radeon_router_select_ddc_port(struct radeon_connector *radeon_connector)
if (!radeon_connector->router.ddc_valid)
return;
+ if (!radeon_connector->router_bus)
+ return;
+
radeon_i2c_get_byte(radeon_connector->router_bus,
radeon_connector->router.i2c_addr,
0x3, &val);
@@ -1121,6 +1124,9 @@ void radeon_router_select_cd_port(struct radeon_connector *radeon_connector)
if (!radeon_connector->router.cd_valid)
return;
+ if (!radeon_connector->router_bus)
+ return;
+
radeon_i2c_get_byte(radeon_connector->router_bus,
radeon_connector->router.i2c_addr,
0x3, &val);
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index bf7d4c06145..bd58af65858 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -221,6 +221,22 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
return -EINVAL;
}
break;
+ case RADEON_INFO_NUM_TILE_PIPES:
+ if (rdev->family >= CHIP_CAYMAN)
+ value = rdev->config.cayman.max_tile_pipes;
+ else if (rdev->family >= CHIP_CEDAR)
+ value = rdev->config.evergreen.max_tile_pipes;
+ else if (rdev->family >= CHIP_RV770)
+ value = rdev->config.rv770.max_tile_pipes;
+ else if (rdev->family >= CHIP_R600)
+ value = rdev->config.r600.max_tile_pipes;
+ else {
+ return -EINVAL;
+ }
+ break;
+ case RADEON_INFO_FUSION_GART_WORKING:
+ value = 1;
+ break;
default:
DRM_DEBUG_KMS("Invalid request %d\n", info->request);
return -EINVAL;
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c
index 5b54268ed6b..2f46e0c8df5 100644
--- a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c
+++ b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c
@@ -269,7 +269,7 @@ static const struct drm_encoder_helper_funcs radeon_legacy_lvds_helper_funcs = {
.disable = radeon_legacy_encoder_disable,
};
-#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)
#define MAX_RADEON_LEVEL 0xFF
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index 08de669e025..86eda1ea94d 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -23,6 +23,7 @@
#include "drmP.h"
#include "radeon.h"
#include "avivod.h"
+#include "atom.h"
#ifdef CONFIG_ACPI
#include <linux/acpi.h>
#endif
@@ -535,7 +536,11 @@ void radeon_pm_resume(struct radeon_device *rdev)
/* set up the default clocks if the MC ucode is loaded */
if (ASIC_IS_DCE5(rdev) && rdev->mc_fw) {
if (rdev->pm.default_vddc)
- radeon_atom_set_voltage(rdev, rdev->pm.default_vddc);
+ radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
+ SET_VOLTAGE_TYPE_ASIC_VDDC);
+ if (rdev->pm.default_vddci)
+ radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
+ SET_VOLTAGE_TYPE_ASIC_VDDCI);
if (rdev->pm.default_sclk)
radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
if (rdev->pm.default_mclk)
@@ -548,6 +553,7 @@ void radeon_pm_resume(struct radeon_device *rdev)
rdev->pm.current_sclk = rdev->pm.default_sclk;
rdev->pm.current_mclk = rdev->pm.default_mclk;
rdev->pm.current_vddc = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.voltage;
+ rdev->pm.current_vddci = rdev->pm.power_state[rdev->pm.default_power_state_index].clock_info[0].voltage.vddci;
if (rdev->pm.pm_method == PM_METHOD_DYNPM
&& rdev->pm.dynpm_state == DYNPM_STATE_SUSPENDED) {
rdev->pm.dynpm_state = DYNPM_STATE_ACTIVE;
@@ -585,7 +591,8 @@ int radeon_pm_init(struct radeon_device *rdev)
/* set up the default clocks if the MC ucode is loaded */
if (ASIC_IS_DCE5(rdev) && rdev->mc_fw) {
if (rdev->pm.default_vddc)
- radeon_atom_set_voltage(rdev, rdev->pm.default_vddc);
+ radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
+ SET_VOLTAGE_TYPE_ASIC_VDDC);
if (rdev->pm.default_sclk)
radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
if (rdev->pm.default_mclk)
diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c
index bbc9cd82333..c6776e48fdd 100644
--- a/drivers/gpu/drm/radeon/radeon_ring.c
+++ b/drivers/gpu/drm/radeon/radeon_ring.c
@@ -248,7 +248,7 @@ void radeon_ib_pool_fini(struct radeon_device *rdev)
void radeon_ring_free_size(struct radeon_device *rdev)
{
if (rdev->wb.enabled)
- rdev->cp.rptr = rdev->wb.wb[RADEON_WB_CP_RPTR_OFFSET/4];
+ rdev->cp.rptr = le32_to_cpu(rdev->wb.wb[RADEON_WB_CP_RPTR_OFFSET/4]);
else {
if (rdev->family >= CHIP_R600)
rdev->cp.rptr = RREG32(R600_CP_RB_RPTR);
diff --git a/drivers/gpu/drm/radeon/reg_srcs/cayman b/drivers/gpu/drm/radeon/reg_srcs/cayman
index 6334f8ac120..0aa8e85a945 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/cayman
+++ b/drivers/gpu/drm/radeon/reg_srcs/cayman
@@ -33,6 +33,7 @@ cayman 0x9400
0x00008E48 SQ_EX_ALLOC_TABLE_SLOTS
0x00009100 SPI_CONFIG_CNTL
0x0000913C SPI_CONFIG_CNTL_1
+0x00009508 TA_CNTL_AUX
0x00009830 DB_DEBUG
0x00009834 DB_DEBUG2
0x00009838 DB_DEBUG3
diff --git a/drivers/gpu/drm/radeon/reg_srcs/evergreen b/drivers/gpu/drm/radeon/reg_srcs/evergreen
index 7e1637176e0..0e28cae7ea4 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/evergreen
+++ b/drivers/gpu/drm/radeon/reg_srcs/evergreen
@@ -46,6 +46,7 @@ evergreen 0x9400
0x00008E48 SQ_EX_ALLOC_TABLE_SLOTS
0x00009100 SPI_CONFIG_CNTL
0x0000913C SPI_CONFIG_CNTL_1
+0x00009508 TA_CNTL_AUX
0x00009700 VC_CNTL
0x00009714 VC_ENHANCE
0x00009830 DB_DEBUG
diff --git a/drivers/gpu/drm/radeon/reg_srcs/r600 b/drivers/gpu/drm/radeon/reg_srcs/r600
index af0da4ae3f5..92f1900dc7c 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/r600
+++ b/drivers/gpu/drm/radeon/reg_srcs/r600
@@ -708,6 +708,7 @@ r600 0x9400
0x00028D0C DB_RENDER_CONTROL
0x00028D10 DB_RENDER_OVERRIDE
0x0002880C DB_SHADER_CONTROL
+0x00028D28 DB_SRESULTS_COMPARE_STATE0
0x00028D2C DB_SRESULTS_COMPARE_STATE1
0x00028430 DB_STENCILREFMASK
0x00028434 DB_STENCILREFMASK_BF
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c
index 876cebc4b8b..6e3b11e5abb 100644
--- a/drivers/gpu/drm/radeon/rs600.c
+++ b/drivers/gpu/drm/radeon/rs600.c
@@ -114,7 +114,7 @@ void rs600_pm_misc(struct radeon_device *rdev)
udelay(voltage->delay);
}
} else if (voltage->type == VOLTAGE_VDDC)
- radeon_atom_set_voltage(rdev, voltage->vddc_id);
+ radeon_atom_set_voltage(rdev, voltage->vddc_id, SET_VOLTAGE_TYPE_ASIC_VDDC);
dyn_pwrmgt_sclk_length = RREG32_PLL(DYN_PWRMGT_SCLK_LENGTH);
dyn_pwrmgt_sclk_length &= ~REDUCED_POWER_SCLK_HILEN(0xf);
diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c
index b974ac7df8d..ef8a5babe9f 100644
--- a/drivers/gpu/drm/radeon/rv770.c
+++ b/drivers/gpu/drm/radeon/rv770.c
@@ -106,7 +106,7 @@ void rv770_pm_misc(struct radeon_device *rdev)
if ((voltage->type == VOLTAGE_SW) && voltage->voltage) {
if (voltage->voltage != rdev->pm.current_vddc) {
- radeon_atom_set_voltage(rdev, voltage->voltage);
+ radeon_atom_set_voltage(rdev, voltage->voltage, SET_VOLTAGE_TYPE_ASIC_VDDC);
rdev->pm.current_vddc = voltage->voltage;
DRM_DEBUG("Setting: v: %d\n", voltage->voltage);
}
@@ -1255,9 +1255,6 @@ int rv770_init(struct radeon_device *rdev)
{
int r;
- r = radeon_dummy_page_init(rdev);
- if (r)
- return r;
/* This don't do much */
r = radeon_gem_init(rdev);
if (r)
@@ -1372,7 +1369,6 @@ void rv770_fini(struct radeon_device *rdev)
radeon_atombios_fini(rdev);
kfree(rdev->bios);
rdev->bios = NULL;
- radeon_dummy_page_fini(rdev);
}
static void rv770_pcie_gen2_enable(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c
index 737a2a2e46a..9d9d92945f8 100644
--- a/drivers/gpu/drm/ttm/ttm_page_alloc.c
+++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c
@@ -683,22 +683,14 @@ int ttm_get_pages(struct list_head *pages, int flags,
gfp_flags |= GFP_HIGHUSER;
for (r = 0; r < count; ++r) {
- if ((flags & TTM_PAGE_FLAG_DMA32) && dma_address) {
- void *addr;
- addr = dma_alloc_coherent(NULL, PAGE_SIZE,
- &dma_address[r],
- gfp_flags);
- if (addr == NULL)
- return -ENOMEM;
- p = virt_to_page(addr);
- } else
- p = alloc_page(gfp_flags);
+ p = alloc_page(gfp_flags);
if (!p) {
printk(KERN_ERR TTM_PFX
"Unable to allocate page.");
return -ENOMEM;
}
+
list_add(&p->lru, pages);
}
return 0;
@@ -746,24 +738,12 @@ void ttm_put_pages(struct list_head *pages, unsigned page_count, int flags,
unsigned long irq_flags;
struct ttm_page_pool *pool = ttm_get_pool(flags, cstate);
struct page *p, *tmp;
- unsigned r;
if (pool == NULL) {
/* No pool for this memory type so free the pages */
- r = page_count-1;
list_for_each_entry_safe(p, tmp, pages, lru) {
- if ((flags & TTM_PAGE_FLAG_DMA32) && dma_address) {
- void *addr = page_address(p);
- WARN_ON(!addr || !dma_address[r]);
- if (addr)
- dma_free_coherent(NULL, PAGE_SIZE,
- addr,
- dma_address[r]);
- dma_address[r] = 0;
- } else
- __free_page(p);
- r--;
+ __free_page(p);
}
/* Make the pages list empty */
INIT_LIST_HEAD(pages);
diff --git a/drivers/gpu/stub/Kconfig b/drivers/gpu/stub/Kconfig
index 70e60a4bb67..419917955bf 100644
--- a/drivers/gpu/stub/Kconfig
+++ b/drivers/gpu/stub/Kconfig
@@ -5,6 +5,7 @@ config STUB_POULSBO
# Poulsbo stub depends on ACPI_VIDEO when ACPI is enabled
# but for select to work, need to select ACPI_VIDEO's dependencies, ick
select BACKLIGHT_CLASS_DEVICE if ACPI
+ select VIDEO_OUTPUT_CONTROL if ACPI
select INPUT if ACPI
select ACPI_VIDEO if ACPI
select THERMAL if ACPI
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
index e01cacba685..498b284e5ef 100644
--- a/drivers/gpu/vga/vga_switcheroo.c
+++ b/drivers/gpu/vga/vga_switcheroo.c
@@ -219,9 +219,6 @@ static int vga_switchto_stage1(struct vga_switcheroo_client *new_client)
int i;
struct vga_switcheroo_client *active = NULL;
- if (new_client->active == true)
- return 0;
-
for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
if (vgasr_priv.clients[i].active == true) {
active = &vgasr_priv.clients[i];
@@ -372,6 +369,9 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf,
goto out;
}
+ if (client->active == true)
+ goto out;
+
/* okay we want a switch - test if devices are willing to switch */
can_switch = true;
for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 060ef632787..50e40dbd8bb 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -110,8 +110,7 @@ config SENSORS_ADM1021
help
If you say yes here you get support for Analog Devices ADM1021
and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A,
- Genesys Logic GL523SM, National Semiconductor LM84, TI THMC10,
- and the XEON processor built-in sensor.
+ Genesys Logic GL523SM, National Semiconductor LM84 and TI THMC10.
This driver can also be built as a module. If so, the module
will be called adm1021.
@@ -618,10 +617,10 @@ config SENSORS_LM90
depends on I2C
help
If you say yes here you get support for National Semiconductor LM90,
- LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, Maxim
- MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659,
- MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, and Winbond/Nuvoton
- W83L771W/G/AWG/ASG sensor chips.
+ LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A,
+ Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659,
+ MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008,
+ and Winbond/Nuvoton W83L771W/G/AWG/ASG sensor chips.
This driver can also be built as a module. If so, the module
will be called lm90.
diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c
index 250d099ca39..da72dc12068 100644
--- a/drivers/hwmon/lm85.c
+++ b/drivers/hwmon/lm85.c
@@ -1094,6 +1094,7 @@ static struct attribute *lm85_attributes_minctl[] = {
&sensor_dev_attr_pwm1_auto_pwm_minctl.dev_attr.attr,
&sensor_dev_attr_pwm2_auto_pwm_minctl.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_pwm_minctl.dev_attr.attr,
+ NULL
};
static const struct attribute_group lm85_group_minctl = {
@@ -1104,6 +1105,7 @@ static struct attribute *lm85_attributes_temp_off[] = {
&sensor_dev_attr_temp1_auto_temp_off.dev_attr.attr,
&sensor_dev_attr_temp2_auto_temp_off.dev_attr.attr,
&sensor_dev_attr_temp3_auto_temp_off.dev_attr.attr,
+ NULL
};
static const struct attribute_group lm85_group_temp_off = {
@@ -1329,11 +1331,11 @@ static int lm85_probe(struct i2c_client *client,
if (data->type != emc6d103s) {
err = sysfs_create_group(&client->dev.kobj, &lm85_group_minctl);
if (err)
- goto err_kfree;
+ goto err_remove_files;
err = sysfs_create_group(&client->dev.kobj,
&lm85_group_temp_off);
if (err)
- goto err_kfree;
+ goto err_remove_files;
}
/* The ADT7463/68 have an optional VRM 10 mode where pin 21 is used
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index c43b4e9f96a..2f94f950480 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -49,10 +49,10 @@
* chips, but support three temperature sensors instead of two. MAX6695
* and MAX6696 only differ in the pinout so they can be treated identically.
*
- * This driver also supports the ADT7461 chip from Analog Devices.
- * It's supported in both compatibility and extended mode. It is mostly
- * compatible with LM90 except for a data format difference for the
- * temperature value registers.
+ * This driver also supports ADT7461 and ADT7461A from Analog Devices as well as
+ * NCT1008 from ON Semiconductor. The chips are supported in both compatibility
+ * and extended mode. They are mostly compatible with LM90 except for a data
+ * format difference for the temperature value registers.
*
* Since the LM90 was the first chipset supported by this driver, most
* comments will refer to this chipset, but are actually general and
@@ -88,9 +88,10 @@
* Addresses to scan
* Address is fully defined internally and cannot be changed except for
* MAX6659, MAX6680 and MAX6681.
- * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6649, MAX6657,
- * MAX6658 and W83L771 have address 0x4c.
- * ADM1032-2, ADT7461-2, LM89-1, LM99-1 and MAX6646 have address 0x4d.
+ * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, ADT7461A, MAX6649,
+ * MAX6657, MAX6658, NCT1008 and W83L771 have address 0x4c.
+ * ADM1032-2, ADT7461-2, ADT7461A-2, LM89-1, LM99-1, MAX6646, and NCT1008D
+ * have address 0x4d.
* MAX6647 has address 0x4e.
* MAX6659 can have address 0x4c, 0x4d or 0x4e.
* MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b,
@@ -174,6 +175,7 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,
static const struct i2c_device_id lm90_id[] = {
{ "adm1032", adm1032 },
{ "adt7461", adt7461 },
+ { "adt7461a", adt7461 },
{ "lm90", lm90 },
{ "lm86", lm86 },
{ "lm89", lm86 },
@@ -188,6 +190,7 @@ static const struct i2c_device_id lm90_id[] = {
{ "max6681", max6680 },
{ "max6695", max6696 },
{ "max6696", max6696 },
+ { "nct1008", adt7461 },
{ "w83l771", w83l771 },
{ }
};
@@ -1153,6 +1156,11 @@ static int lm90_detect(struct i2c_client *new_client,
&& (reg_config1 & 0x1B) == 0x00
&& reg_convrate <= 0x0A) {
name = "adt7461";
+ } else
+ if (chip_id == 0x57 /* ADT7461A, NCT1008 */
+ && (reg_config1 & 0x1B) == 0x00
+ && reg_convrate <= 0x0A) {
+ name = "adt7461a";
}
} else
if (man_id == 0x4D) { /* Maxim */
diff --git a/drivers/hwmon/pmbus_core.c b/drivers/hwmon/pmbus_core.c
index edfb92e4173..196ffafafd8 100644
--- a/drivers/hwmon/pmbus_core.c
+++ b/drivers/hwmon/pmbus_core.c
@@ -139,7 +139,6 @@ struct pmbus_data {
* A single status register covers multiple attributes,
* so we keep them all together.
*/
- u8 status_bits;
u8 status[PB_NUM_STATUS_REG];
u8 currpage;
diff --git a/drivers/hwmon/twl4030-madc-hwmon.c b/drivers/hwmon/twl4030-madc-hwmon.c
index de5819199e2..57240740b16 100644
--- a/drivers/hwmon/twl4030-madc-hwmon.c
+++ b/drivers/hwmon/twl4030-madc-hwmon.c
@@ -98,7 +98,6 @@ static const struct attribute_group twl4030_madc_group = {
static int __devinit twl4030_madc_hwmon_probe(struct platform_device *pdev)
{
int ret;
- int status;
struct device *hwmon;
ret = sysfs_create_group(&pdev->dev.kobj, &twl4030_madc_group);
@@ -107,7 +106,7 @@ static int __devinit twl4030_madc_hwmon_probe(struct platform_device *pdev)
hwmon = hwmon_device_register(&pdev->dev);
if (IS_ERR(hwmon)) {
dev_err(&pdev->dev, "hwmon_device_register failed.\n");
- status = PTR_ERR(hwmon);
+ ret = PTR_ERR(hwmon);
goto err_reg;
}
diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c
index 38319a69bd0..d6d58684712 100644
--- a/drivers/i2c/algos/i2c-algo-bit.c
+++ b/drivers/i2c/algos/i2c-algo-bit.c
@@ -232,9 +232,17 @@ static int i2c_inb(struct i2c_adapter *i2c_adap)
* Sanity check for the adapter hardware - check the reaction of
* the bus lines only if it seems to be idle.
*/
-static int test_bus(struct i2c_algo_bit_data *adap, char *name)
+static int test_bus(struct i2c_adapter *i2c_adap)
{
- int scl, sda;
+ struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
+ const char *name = i2c_adap->name;
+ int scl, sda, ret;
+
+ if (adap->pre_xfer) {
+ ret = adap->pre_xfer(i2c_adap);
+ if (ret < 0)
+ return -ENODEV;
+ }
if (adap->getscl == NULL)
pr_info("%s: Testing SDA only, SCL is not readable\n", name);
@@ -297,11 +305,19 @@ static int test_bus(struct i2c_algo_bit_data *adap, char *name)
"while pulling SCL high!\n", name);
goto bailout;
}
+
+ if (adap->post_xfer)
+ adap->post_xfer(i2c_adap);
+
pr_info("%s: Test OK\n", name);
return 0;
bailout:
sdahi(adap);
sclhi(adap);
+
+ if (adap->post_xfer)
+ adap->post_xfer(i2c_adap);
+
return -ENODEV;
}
@@ -607,7 +623,7 @@ static int __i2c_bit_add_bus(struct i2c_adapter *adap,
int ret;
if (bit_test) {
- ret = test_bus(bit_adap, adap->name);
+ ret = test_bus(adap);
if (ret < 0)
return -ENODEV;
}
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 72c0415f6f9..455e909bc76 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -134,10 +134,15 @@
SMBHSTSTS_BUS_ERR | SMBHSTSTS_DEV_ERR | \
SMBHSTSTS_INTR)
+/* Older devices have their ID defined in <linux/pci_ids.h> */
+#define PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS 0x1c22
+#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS 0x1d22
/* Patsburg also has three 'Integrated Device Function' SMBus controllers */
#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0 0x1d70
#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1 0x1d71
#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2 0x1d72
+#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330
+#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30
struct i801_priv {
struct i2c_adapter adapter;
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index 75b984c519a..107397a606b 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -560,15 +560,18 @@ static struct i2c_adapter mpc_ops = {
.timeout = HZ,
};
+static const struct of_device_id mpc_i2c_of_match[];
static int __devinit fsl_i2c_probe(struct platform_device *op)
{
+ const struct of_device_id *match;
struct mpc_i2c *i2c;
const u32 *prop;
u32 clock = MPC_I2C_CLOCK_LEGACY;
int result = 0;
int plen;
- if (!op->dev.of_match)
+ match = of_match_device(mpc_i2c_of_match, &op->dev);
+ if (!match)
return -EINVAL;
i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
@@ -605,8 +608,8 @@ static int __devinit fsl_i2c_probe(struct platform_device *op)
clock = *prop;
}
- if (op->dev.of_match->data) {
- struct mpc_i2c_data *data = op->dev.of_match->data;
+ if (match->data) {
+ struct mpc_i2c_data *data = match->data;
data->setup(op->dev.of_node, i2c, clock, data->prescaler);
} else {
/* Backwards compatibility */
diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c
index 0eb1515541e..2dbba163b10 100644
--- a/drivers/i2c/busses/i2c-parport.c
+++ b/drivers/i2c/busses/i2c-parport.c
@@ -1,7 +1,7 @@
/* ------------------------------------------------------------------------ *
* i2c-parport.c I2C bus over parallel port *
* ------------------------------------------------------------------------ *
- Copyright (C) 2003-2010 Jean Delvare <khali@linux-fr.org>
+ Copyright (C) 2003-2011 Jean Delvare <khali@linux-fr.org>
Based on older i2c-philips-par.c driver
Copyright (C) 1995-2000 Simon G. Vogl
@@ -33,6 +33,8 @@
#include <linux/i2c-algo-bit.h>
#include <linux/i2c-smbus.h>
#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
#include "i2c-parport.h"
/* ----- Device list ------------------------------------------------------ */
@@ -43,10 +45,11 @@ struct i2c_par {
struct i2c_algo_bit_data algo_data;
struct i2c_smbus_alert_setup alert_data;
struct i2c_client *ara;
- struct i2c_par *next;
+ struct list_head node;
};
-static struct i2c_par *adapter_list;
+static LIST_HEAD(adapter_list);
+static DEFINE_MUTEX(adapter_list_lock);
/* ----- Low-level parallel port access ----------------------------------- */
@@ -228,8 +231,9 @@ static void i2c_parport_attach (struct parport *port)
}
/* Add the new adapter to the list */
- adapter->next = adapter_list;
- adapter_list = adapter;
+ mutex_lock(&adapter_list_lock);
+ list_add_tail(&adapter->node, &adapter_list);
+ mutex_unlock(&adapter_list_lock);
return;
ERROR1:
@@ -241,11 +245,11 @@ ERROR0:
static void i2c_parport_detach (struct parport *port)
{
- struct i2c_par *adapter, *prev;
+ struct i2c_par *adapter, *_n;
/* Walk the list */
- for (prev = NULL, adapter = adapter_list; adapter;
- prev = adapter, adapter = adapter->next) {
+ mutex_lock(&adapter_list_lock);
+ list_for_each_entry_safe(adapter, _n, &adapter_list, node) {
if (adapter->pdev->port == port) {
if (adapter->ara) {
parport_disable_irq(port);
@@ -259,14 +263,11 @@ static void i2c_parport_detach (struct parport *port)
parport_release(adapter->pdev);
parport_unregister_device(adapter->pdev);
- if (prev)
- prev->next = adapter->next;
- else
- adapter_list = adapter->next;
+ list_del(&adapter->node);
kfree(adapter);
- return;
}
}
+ mutex_unlock(&adapter_list_lock);
}
static struct parport_driver i2c_parport_driver = {
diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c
index a97e3fec814..04be9f82e14 100644
--- a/drivers/i2c/busses/i2c-pnx.c
+++ b/drivers/i2c/busses/i2c-pnx.c
@@ -65,7 +65,7 @@ static inline void i2c_pnx_arm_timer(struct i2c_pnx_algo_data *alg_data)
jiffies, expires);
timer->expires = jiffies + expires;
- timer->data = (unsigned long)&alg_data;
+ timer->data = (unsigned long)alg_data;
add_timer(timer);
}
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 70c30e6bce0..9a58994ff7e 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -797,7 +797,8 @@ static int i2c_do_add_adapter(struct i2c_driver *driver,
/* Let legacy drivers scan this bus for matching devices */
if (driver->attach_adapter) {
- dev_warn(&adap->dev, "attach_adapter method is deprecated\n");
+ dev_warn(&adap->dev, "%s: attach_adapter method is deprecated\n",
+ driver->driver.name);
dev_warn(&adap->dev, "Please use another way to instantiate "
"your i2c_client\n");
/* We ignore the return code; if it fails, too bad */
@@ -984,7 +985,8 @@ static int i2c_do_del_adapter(struct i2c_driver *driver,
if (!driver->detach_adapter)
return 0;
- dev_warn(&adapter->dev, "detach_adapter method is deprecated\n");
+ dev_warn(&adapter->dev, "%s: detach_adapter method is deprecated\n",
+ driver->driver.name);
res = driver->detach_adapter(adapter);
if (res)
dev_err(&adapter->dev, "detach_adapter failed (%d) "
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
index fd1e1179913..a5ec5a7cb38 100644
--- a/drivers/ide/ide-cd.c
+++ b/drivers/ide/ide-cd.c
@@ -1782,7 +1782,6 @@ static int ide_cd_probe(ide_drive_t *drive)
ide_cd_read_toc(drive, &sense);
g->fops = &idecd_ops;
g->flags |= GENHD_FL_REMOVABLE;
- g->events = DISK_EVENT_MEDIA_CHANGE;
add_disk(g);
return 0;
diff --git a/drivers/ide/ide-cd_ioctl.c b/drivers/ide/ide-cd_ioctl.c
index 2a6bc50e8a4..02caa7dd51c 100644
--- a/drivers/ide/ide-cd_ioctl.c
+++ b/drivers/ide/ide-cd_ioctl.c
@@ -79,6 +79,12 @@ int ide_cdrom_drive_status(struct cdrom_device_info *cdi, int slot_nr)
return CDS_DRIVE_NOT_READY;
}
+/*
+ * ide-cd always generates media changed event if media is missing, which
+ * makes it impossible to use for proper event reporting, so disk->events
+ * is cleared to 0 and the following function is used only to trigger
+ * revalidation and never propagated to userland.
+ */
unsigned int ide_cdrom_check_events_real(struct cdrom_device_info *cdi,
unsigned int clearing, int slot_nr)
{
diff --git a/drivers/ide/ide-gd.c b/drivers/ide/ide-gd.c
index c4ffd488893..70ea8763567 100644
--- a/drivers/ide/ide-gd.c
+++ b/drivers/ide/ide-gd.c
@@ -298,6 +298,12 @@ static unsigned int ide_gd_check_events(struct gendisk *disk,
return 0;
}
+ /*
+ * The following is used to force revalidation on the first open on
+ * removeable devices, and never gets reported to userland as
+ * genhd->events is 0. This is intended as removeable ide disk
+ * can't really detect MEDIA_CHANGE events.
+ */
ret = drive->dev_flags & IDE_DFLAG_MEDIA_CHANGED;
drive->dev_flags &= ~IDE_DFLAG_MEDIA_CHANGED;
@@ -413,7 +419,6 @@ static int ide_gd_probe(ide_drive_t *drive)
if (drive->dev_flags & IDE_DFLAG_REMOVABLE)
g->flags = GENHD_FL_REMOVABLE;
g->fops = &ide_gd_ops;
- g->events = DISK_EVENT_MEDIA_CHANGE;
add_disk(g);
return 0;
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 5ed9d25d021..99dde874fbb 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -148,6 +148,7 @@ struct rdma_id_private {
u32 qp_num;
u8 srq;
u8 tos;
+ u8 reuseaddr;
};
struct cma_multicast {
@@ -712,6 +713,21 @@ static inline int cma_any_addr(struct sockaddr *addr)
return cma_zero_addr(addr) || cma_loopback_addr(addr);
}
+static int cma_addr_cmp(struct sockaddr *src, struct sockaddr *dst)
+{
+ if (src->sa_family != dst->sa_family)
+ return -1;
+
+ switch (src->sa_family) {
+ case AF_INET:
+ return ((struct sockaddr_in *) src)->sin_addr.s_addr !=
+ ((struct sockaddr_in *) dst)->sin_addr.s_addr;
+ default:
+ return ipv6_addr_cmp(&((struct sockaddr_in6 *) src)->sin6_addr,
+ &((struct sockaddr_in6 *) dst)->sin6_addr);
+ }
+}
+
static inline __be16 cma_port(struct sockaddr *addr)
{
if (addr->sa_family == AF_INET)
@@ -1564,50 +1580,6 @@ static void cma_listen_on_all(struct rdma_id_private *id_priv)
mutex_unlock(&lock);
}
-int rdma_listen(struct rdma_cm_id *id, int backlog)
-{
- struct rdma_id_private *id_priv;
- int ret;
-
- id_priv = container_of(id, struct rdma_id_private, id);
- if (id_priv->state == CMA_IDLE) {
- ((struct sockaddr *) &id->route.addr.src_addr)->sa_family = AF_INET;
- ret = rdma_bind_addr(id, (struct sockaddr *) &id->route.addr.src_addr);
- if (ret)
- return ret;
- }
-
- if (!cma_comp_exch(id_priv, CMA_ADDR_BOUND, CMA_LISTEN))
- return -EINVAL;
-
- id_priv->backlog = backlog;
- if (id->device) {
- switch (rdma_node_get_transport(id->device->node_type)) {
- case RDMA_TRANSPORT_IB:
- ret = cma_ib_listen(id_priv);
- if (ret)
- goto err;
- break;
- case RDMA_TRANSPORT_IWARP:
- ret = cma_iw_listen(id_priv, backlog);
- if (ret)
- goto err;
- break;
- default:
- ret = -ENOSYS;
- goto err;
- }
- } else
- cma_listen_on_all(id_priv);
-
- return 0;
-err:
- id_priv->backlog = 0;
- cma_comp_exch(id_priv, CMA_LISTEN, CMA_ADDR_BOUND);
- return ret;
-}
-EXPORT_SYMBOL(rdma_listen);
-
void rdma_set_service_type(struct rdma_cm_id *id, int tos)
{
struct rdma_id_private *id_priv;
@@ -2090,6 +2062,25 @@ err:
}
EXPORT_SYMBOL(rdma_resolve_addr);
+int rdma_set_reuseaddr(struct rdma_cm_id *id, int reuse)
+{
+ struct rdma_id_private *id_priv;
+ unsigned long flags;
+ int ret;
+
+ id_priv = container_of(id, struct rdma_id_private, id);
+ spin_lock_irqsave(&id_priv->lock, flags);
+ if (id_priv->state == CMA_IDLE) {
+ id_priv->reuseaddr = reuse;
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+ spin_unlock_irqrestore(&id_priv->lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(rdma_set_reuseaddr);
+
static void cma_bind_port(struct rdma_bind_list *bind_list,
struct rdma_id_private *id_priv)
{
@@ -2165,41 +2156,71 @@ retry:
return -EADDRNOTAVAIL;
}
-static int cma_use_port(struct idr *ps, struct rdma_id_private *id_priv)
+/*
+ * Check that the requested port is available. This is called when trying to
+ * bind to a specific port, or when trying to listen on a bound port. In
+ * the latter case, the provided id_priv may already be on the bind_list, but
+ * we still need to check that it's okay to start listening.
+ */
+static int cma_check_port(struct rdma_bind_list *bind_list,
+ struct rdma_id_private *id_priv, uint8_t reuseaddr)
{
struct rdma_id_private *cur_id;
- struct sockaddr_in *sin, *cur_sin;
- struct rdma_bind_list *bind_list;
+ struct sockaddr *addr, *cur_addr;
struct hlist_node *node;
+
+ addr = (struct sockaddr *) &id_priv->id.route.addr.src_addr;
+ if (cma_any_addr(addr) && !reuseaddr)
+ return -EADDRNOTAVAIL;
+
+ hlist_for_each_entry(cur_id, node, &bind_list->owners, node) {
+ if (id_priv == cur_id)
+ continue;
+
+ if ((cur_id->state == CMA_LISTEN) ||
+ !reuseaddr || !cur_id->reuseaddr) {
+ cur_addr = (struct sockaddr *) &cur_id->id.route.addr.src_addr;
+ if (cma_any_addr(cur_addr))
+ return -EADDRNOTAVAIL;
+
+ if (!cma_addr_cmp(addr, cur_addr))
+ return -EADDRINUSE;
+ }
+ }
+ return 0;
+}
+
+static int cma_use_port(struct idr *ps, struct rdma_id_private *id_priv)
+{
+ struct rdma_bind_list *bind_list;
unsigned short snum;
+ int ret;
- sin = (struct sockaddr_in *) &id_priv->id.route.addr.src_addr;
- snum = ntohs(sin->sin_port);
+ snum = ntohs(cma_port((struct sockaddr *) &id_priv->id.route.addr.src_addr));
if (snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
return -EACCES;
bind_list = idr_find(ps, snum);
- if (!bind_list)
- return cma_alloc_port(ps, id_priv, snum);
-
- /*
- * We don't support binding to any address if anyone is bound to
- * a specific address on the same port.
- */
- if (cma_any_addr((struct sockaddr *) &id_priv->id.route.addr.src_addr))
- return -EADDRNOTAVAIL;
-
- hlist_for_each_entry(cur_id, node, &bind_list->owners, node) {
- if (cma_any_addr((struct sockaddr *) &cur_id->id.route.addr.src_addr))
- return -EADDRNOTAVAIL;
-
- cur_sin = (struct sockaddr_in *) &cur_id->id.route.addr.src_addr;
- if (sin->sin_addr.s_addr == cur_sin->sin_addr.s_addr)
- return -EADDRINUSE;
+ if (!bind_list) {
+ ret = cma_alloc_port(ps, id_priv, snum);
+ } else {
+ ret = cma_check_port(bind_list, id_priv, id_priv->reuseaddr);
+ if (!ret)
+ cma_bind_port(bind_list, id_priv);
}
+ return ret;
+}
- cma_bind_port(bind_list, id_priv);
- return 0;
+static int cma_bind_listen(struct rdma_id_private *id_priv)
+{
+ struct rdma_bind_list *bind_list = id_priv->bind_list;
+ int ret = 0;
+
+ mutex_lock(&lock);
+ if (bind_list->owners.first->next)
+ ret = cma_check_port(bind_list, id_priv, 0);
+ mutex_unlock(&lock);
+ return ret;
}
static int cma_get_port(struct rdma_id_private *id_priv)
@@ -2253,6 +2274,56 @@ static int cma_check_linklocal(struct rdma_dev_addr *dev_addr,
return 0;
}
+int rdma_listen(struct rdma_cm_id *id, int backlog)
+{
+ struct rdma_id_private *id_priv;
+ int ret;
+
+ id_priv = container_of(id, struct rdma_id_private, id);
+ if (id_priv->state == CMA_IDLE) {
+ ((struct sockaddr *) &id->route.addr.src_addr)->sa_family = AF_INET;
+ ret = rdma_bind_addr(id, (struct sockaddr *) &id->route.addr.src_addr);
+ if (ret)
+ return ret;
+ }
+
+ if (!cma_comp_exch(id_priv, CMA_ADDR_BOUND, CMA_LISTEN))
+ return -EINVAL;
+
+ if (id_priv->reuseaddr) {
+ ret = cma_bind_listen(id_priv);
+ if (ret)
+ goto err;
+ }
+
+ id_priv->backlog = backlog;
+ if (id->device) {
+ switch (rdma_node_get_transport(id->device->node_type)) {
+ case RDMA_TRANSPORT_IB:
+ ret = cma_ib_listen(id_priv);
+ if (ret)
+ goto err;
+ break;
+ case RDMA_TRANSPORT_IWARP:
+ ret = cma_iw_listen(id_priv, backlog);
+ if (ret)
+ goto err;
+ break;
+ default:
+ ret = -ENOSYS;
+ goto err;
+ }
+ } else
+ cma_listen_on_all(id_priv);
+
+ return 0;
+err:
+ id_priv->backlog = 0;
+ cma_comp_exch(id_priv, CMA_LISTEN, CMA_ADDR_BOUND);
+ return ret;
+}
+EXPORT_SYMBOL(rdma_listen);
+
int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr)
{
struct rdma_id_private *id_priv;
diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c
index 2a1e9ae134b..a9c042345c6 100644
--- a/drivers/infiniband/core/iwcm.c
+++ b/drivers/infiniband/core/iwcm.c
@@ -725,7 +725,7 @@ static int cm_conn_rep_handler(struct iwcm_id_private *cm_id_priv,
*/
clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_SENT);
- if (iw_event->status == IW_CM_EVENT_STATUS_ACCEPTED) {
+ if (iw_event->status == 0) {
cm_id_priv->id.local_addr = iw_event->local_addr;
cm_id_priv->id.remote_addr = iw_event->remote_addr;
cm_id_priv->state = IW_CM_STATE_ESTABLISHED;
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index ec1e9da1488..b3fa798525b 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -883,6 +883,13 @@ static int ucma_set_option_id(struct ucma_context *ctx, int optname,
}
rdma_set_service_type(ctx->cm_id, *((u8 *) optval));
break;
+ case RDMA_OPTION_ID_REUSEADDR:
+ if (optlen != sizeof(int)) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = rdma_set_reuseaddr(ctx->cm_id, *((int *) optval) ? 1 : 0);
+ break;
default:
ret = -ENOSYS;
}
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
index 6aa53cd6947..f660cd04ec2 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -1199,9 +1199,7 @@ static int pass_open_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
}
PDBG("%s ep %p status %d error %d\n", __func__, ep,
rpl->status, status2errno(rpl->status));
- ep->com.wr_wait.ret = status2errno(rpl->status);
- ep->com.wr_wait.done = 1;
- wake_up(&ep->com.wr_wait.wait);
+ c4iw_wake_up(&ep->com.wr_wait, status2errno(rpl->status));
return 0;
}
@@ -1235,9 +1233,7 @@ static int close_listsrv_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
struct c4iw_listen_ep *ep = lookup_stid(t, stid);
PDBG("%s ep %p\n", __func__, ep);
- ep->com.wr_wait.ret = status2errno(rpl->status);
- ep->com.wr_wait.done = 1;
- wake_up(&ep->com.wr_wait.wait);
+ c4iw_wake_up(&ep->com.wr_wait, status2errno(rpl->status));
return 0;
}
@@ -1467,7 +1463,7 @@ static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb)
struct c4iw_qp_attributes attrs;
int disconnect = 1;
int release = 0;
- int closing = 0;
+ int abort = 0;
struct tid_info *t = dev->rdev.lldi.tids;
unsigned int tid = GET_TID(hdr);
@@ -1493,23 +1489,22 @@ static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb)
* in rdma connection migration (see c4iw_accept_cr()).
*/
__state_set(&ep->com, CLOSING);
- ep->com.wr_wait.done = 1;
- ep->com.wr_wait.ret = -ECONNRESET;
PDBG("waking up ep %p tid %u\n", ep, ep->hwtid);
- wake_up(&ep->com.wr_wait.wait);
+ c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET);
break;
case MPA_REP_SENT:
__state_set(&ep->com, CLOSING);
- ep->com.wr_wait.done = 1;
- ep->com.wr_wait.ret = -ECONNRESET;
PDBG("waking up ep %p tid %u\n", ep, ep->hwtid);
- wake_up(&ep->com.wr_wait.wait);
+ c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET);
break;
case FPDU_MODE:
start_ep_timer(ep);
__state_set(&ep->com, CLOSING);
- closing = 1;
+ attrs.next_state = C4IW_QP_STATE_CLOSING;
+ abort = c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp,
+ C4IW_QP_ATTR_NEXT_STATE, &attrs, 1);
peer_close_upcall(ep);
+ disconnect = 1;
break;
case ABORTING:
disconnect = 0;
@@ -1537,11 +1532,6 @@ static int peer_close(struct c4iw_dev *dev, struct sk_buff *skb)
BUG_ON(1);
}
mutex_unlock(&ep->com.mutex);
- if (closing) {
- attrs.next_state = C4IW_QP_STATE_CLOSING;
- c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp,
- C4IW_QP_ATTR_NEXT_STATE, &attrs, 1);
- }
if (disconnect)
c4iw_ep_disconnect(ep, 0, GFP_KERNEL);
if (release)
@@ -1582,9 +1572,7 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb)
/*
* Wake up any threads in rdma_init() or rdma_fini().
*/
- ep->com.wr_wait.done = 1;
- ep->com.wr_wait.ret = -ECONNRESET;
- wake_up(&ep->com.wr_wait.wait);
+ c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET);
mutex_lock(&ep->com.mutex);
switch (ep->com.state) {
@@ -1711,14 +1699,14 @@ static int terminate(struct c4iw_dev *dev, struct sk_buff *skb)
ep = lookup_tid(t, tid);
BUG_ON(!ep);
- if (ep->com.qp) {
+ if (ep && ep->com.qp) {
printk(KERN_WARNING MOD "TERM received tid %u qpid %u\n", tid,
ep->com.qp->wq.sq.qid);
attrs.next_state = C4IW_QP_STATE_TERMINATE;
c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp,
C4IW_QP_ATTR_NEXT_STATE, &attrs, 1);
} else
- printk(KERN_WARNING MOD "TERM received tid %u no qp\n", tid);
+ printk(KERN_WARNING MOD "TERM received tid %u no ep/qp\n", tid);
return 0;
}
@@ -2297,14 +2285,8 @@ static int fw6_msg(struct c4iw_dev *dev, struct sk_buff *skb)
ret = (int)((be64_to_cpu(rpl->data[0]) >> 8) & 0xff);
wr_waitp = (struct c4iw_wr_wait *)(__force unsigned long) rpl->data[1];
PDBG("%s wr_waitp %p ret %u\n", __func__, wr_waitp, ret);
- if (wr_waitp) {
- if (ret)
- wr_waitp->ret = -ret;
- else
- wr_waitp->ret = 0;
- wr_waitp->done = 1;
- wake_up(&wr_waitp->wait);
- }
+ if (wr_waitp)
+ c4iw_wake_up(wr_waitp, ret ? -ret : 0);
kfree_skb(skb);
break;
case 2:
diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c
index e29172c2afc..40a13cc633a 100644
--- a/drivers/infiniband/hw/cxgb4/device.c
+++ b/drivers/infiniband/hw/cxgb4/device.c
@@ -44,7 +44,7 @@ MODULE_DESCRIPTION("Chelsio T4 RDMA Driver");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION(DRV_VERSION);
-static LIST_HEAD(dev_list);
+static LIST_HEAD(uld_ctx_list);
static DEFINE_MUTEX(dev_mutex);
static struct dentry *c4iw_debugfs_root;
@@ -370,18 +370,23 @@ static void c4iw_rdev_close(struct c4iw_rdev *rdev)
c4iw_destroy_resource(&rdev->resource);
}
-static void c4iw_remove(struct c4iw_dev *dev)
+struct uld_ctx {
+ struct list_head entry;
+ struct cxgb4_lld_info lldi;
+ struct c4iw_dev *dev;
+};
+
+static void c4iw_remove(struct uld_ctx *ctx)
{
- PDBG("%s c4iw_dev %p\n", __func__, dev);
- list_del(&dev->entry);
- if (dev->registered)
- c4iw_unregister_device(dev);
- c4iw_rdev_close(&dev->rdev);
- idr_destroy(&dev->cqidr);
- idr_destroy(&dev->qpidr);
- idr_destroy(&dev->mmidr);
- iounmap(dev->rdev.oc_mw_kva);
- ib_dealloc_device(&dev->ibdev);
+ PDBG("%s c4iw_dev %p\n", __func__, ctx->dev);
+ c4iw_unregister_device(ctx->dev);
+ c4iw_rdev_close(&ctx->dev->rdev);
+ idr_destroy(&ctx->dev->cqidr);
+ idr_destroy(&ctx->dev->qpidr);
+ idr_destroy(&ctx->dev->mmidr);
+ iounmap(ctx->dev->rdev.oc_mw_kva);
+ ib_dealloc_device(&ctx->dev->ibdev);
+ ctx->dev = NULL;
}
static struct c4iw_dev *c4iw_alloc(const struct cxgb4_lld_info *infop)
@@ -392,7 +397,7 @@ static struct c4iw_dev *c4iw_alloc(const struct cxgb4_lld_info *infop)
devp = (struct c4iw_dev *)ib_alloc_device(sizeof(*devp));
if (!devp) {
printk(KERN_ERR MOD "Cannot allocate ib device\n");
- return NULL;
+ return ERR_PTR(-ENOMEM);
}
devp->rdev.lldi = *infop;
@@ -402,27 +407,23 @@ static struct c4iw_dev *c4iw_alloc(const struct cxgb4_lld_info *infop)
devp->rdev.oc_mw_kva = ioremap_wc(devp->rdev.oc_mw_pa,
devp->rdev.lldi.vr->ocq.size);
- printk(KERN_INFO MOD "ocq memory: "
+ PDBG(KERN_INFO MOD "ocq memory: "
"hw_start 0x%x size %u mw_pa 0x%lx mw_kva %p\n",
devp->rdev.lldi.vr->ocq.start, devp->rdev.lldi.vr->ocq.size,
devp->rdev.oc_mw_pa, devp->rdev.oc_mw_kva);
- mutex_lock(&dev_mutex);
-
ret = c4iw_rdev_open(&devp->rdev);
if (ret) {
mutex_unlock(&dev_mutex);
printk(KERN_ERR MOD "Unable to open CXIO rdev err %d\n", ret);
ib_dealloc_device(&devp->ibdev);
- return NULL;
+ return ERR_PTR(ret);
}
idr_init(&devp->cqidr);
idr_init(&devp->qpidr);
idr_init(&devp->mmidr);
spin_lock_init(&devp->lock);
- list_add_tail(&devp->entry, &dev_list);
- mutex_unlock(&dev_mutex);
if (c4iw_debugfs_root) {
devp->debugfs_root = debugfs_create_dir(
@@ -435,7 +436,7 @@ static struct c4iw_dev *c4iw_alloc(const struct cxgb4_lld_info *infop)
static void *c4iw_uld_add(const struct cxgb4_lld_info *infop)
{
- struct c4iw_dev *dev;
+ struct uld_ctx *ctx;
static int vers_printed;
int i;
@@ -443,25 +444,33 @@ static void *c4iw_uld_add(const struct cxgb4_lld_info *infop)
printk(KERN_INFO MOD "Chelsio T4 RDMA Driver - version %s\n",
DRV_VERSION);
- dev = c4iw_alloc(infop);
- if (!dev)
+ ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+ if (!ctx) {
+ ctx = ERR_PTR(-ENOMEM);
goto out;
+ }
+ ctx->lldi = *infop;
PDBG("%s found device %s nchan %u nrxq %u ntxq %u nports %u\n",
- __func__, pci_name(dev->rdev.lldi.pdev),
- dev->rdev.lldi.nchan, dev->rdev.lldi.nrxq,
- dev->rdev.lldi.ntxq, dev->rdev.lldi.nports);
+ __func__, pci_name(ctx->lldi.pdev),
+ ctx->lldi.nchan, ctx->lldi.nrxq,
+ ctx->lldi.ntxq, ctx->lldi.nports);
+
+ mutex_lock(&dev_mutex);
+ list_add_tail(&ctx->entry, &uld_ctx_list);
+ mutex_unlock(&dev_mutex);
- for (i = 0; i < dev->rdev.lldi.nrxq; i++)
- PDBG("rxqid[%u] %u\n", i, dev->rdev.lldi.rxq_ids[i]);
+ for (i = 0; i < ctx->lldi.nrxq; i++)
+ PDBG("rxqid[%u] %u\n", i, ctx->lldi.rxq_ids[i]);
out:
- return dev;
+ return ctx;
}
static int c4iw_uld_rx_handler(void *handle, const __be64 *rsp,
const struct pkt_gl *gl)
{
- struct c4iw_dev *dev = handle;
+ struct uld_ctx *ctx = handle;
+ struct c4iw_dev *dev = ctx->dev;
struct sk_buff *skb;
const struct cpl_act_establish *rpl;
unsigned int opcode;
@@ -503,47 +512,49 @@ nomem:
static int c4iw_uld_state_change(void *handle, enum cxgb4_state new_state)
{
- struct c4iw_dev *dev = handle;
+ struct uld_ctx *ctx = handle;
PDBG("%s new_state %u\n", __func__, new_state);
switch (new_state) {
case CXGB4_STATE_UP:
- printk(KERN_INFO MOD "%s: Up\n", pci_name(dev->rdev.lldi.pdev));
- if (!dev->registered) {
- int ret;
- ret = c4iw_register_device(dev);
- if (ret)
+ printk(KERN_INFO MOD "%s: Up\n", pci_name(ctx->lldi.pdev));
+ if (!ctx->dev) {
+ int ret = 0;
+
+ ctx->dev = c4iw_alloc(&ctx->lldi);
+ if (!IS_ERR(ctx->dev))
+ ret = c4iw_register_device(ctx->dev);
+ if (IS_ERR(ctx->dev) || ret)
printk(KERN_ERR MOD
"%s: RDMA registration failed: %d\n",
- pci_name(dev->rdev.lldi.pdev), ret);
+ pci_name(ctx->lldi.pdev), ret);
}
break;
case CXGB4_STATE_DOWN:
printk(KERN_INFO MOD "%s: Down\n",
- pci_name(dev->rdev.lldi.pdev));
- if (dev->registered)
- c4iw_unregister_device(dev);
+ pci_name(ctx->lldi.pdev));
+ if (ctx->dev)
+ c4iw_remove(ctx);
break;
case CXGB4_STATE_START_RECOVERY:
printk(KERN_INFO MOD "%s: Fatal Error\n",
- pci_name(dev->rdev.lldi.pdev));
- dev->rdev.flags |= T4_FATAL_ERROR;
- if (dev->registered) {
+ pci_name(ctx->lldi.pdev));
+ if (ctx->dev) {
struct ib_event event;
+ ctx->dev->rdev.flags |= T4_FATAL_ERROR;
memset(&event, 0, sizeof event);
event.event = IB_EVENT_DEVICE_FATAL;
- event.device = &dev->ibdev;
+ event.device = &ctx->dev->ibdev;
ib_dispatch_event(&event);
- c4iw_unregister_device(dev);
+ c4iw_remove(ctx);
}
break;
case CXGB4_STATE_DETACH:
printk(KERN_INFO MOD "%s: Detach\n",
- pci_name(dev->rdev.lldi.pdev));
- mutex_lock(&dev_mutex);
- c4iw_remove(dev);
- mutex_unlock(&dev_mutex);
+ pci_name(ctx->lldi.pdev));
+ if (ctx->dev)
+ c4iw_remove(ctx);
break;
}
return 0;
@@ -576,11 +587,13 @@ static int __init c4iw_init_module(void)
static void __exit c4iw_exit_module(void)
{
- struct c4iw_dev *dev, *tmp;
+ struct uld_ctx *ctx, *tmp;
mutex_lock(&dev_mutex);
- list_for_each_entry_safe(dev, tmp, &dev_list, entry) {
- c4iw_remove(dev);
+ list_for_each_entry_safe(ctx, tmp, &uld_ctx_list, entry) {
+ if (ctx->dev)
+ c4iw_remove(ctx);
+ kfree(ctx);
}
mutex_unlock(&dev_mutex);
cxgb4_unregister_uld(CXGB4_ULD_RDMA);
diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
index 9f6166f5926..35d2a5dd9bb 100644
--- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
+++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h
@@ -131,42 +131,58 @@ static inline int c4iw_num_stags(struct c4iw_rdev *rdev)
#define C4IW_WR_TO (10*HZ)
+enum {
+ REPLY_READY = 0,
+};
+
struct c4iw_wr_wait {
wait_queue_head_t wait;
- int done;
+ unsigned long status;
int ret;
};
static inline void c4iw_init_wr_wait(struct c4iw_wr_wait *wr_waitp)
{
wr_waitp->ret = 0;
- wr_waitp->done = 0;
+ wr_waitp->status = 0;
init_waitqueue_head(&wr_waitp->wait);
}
+static inline void c4iw_wake_up(struct c4iw_wr_wait *wr_waitp, int ret)
+{
+ wr_waitp->ret = ret;
+ set_bit(REPLY_READY, &wr_waitp->status);
+ wake_up(&wr_waitp->wait);
+}
+
static inline int c4iw_wait_for_reply(struct c4iw_rdev *rdev,
struct c4iw_wr_wait *wr_waitp,
u32 hwtid, u32 qpid,
const char *func)
{
unsigned to = C4IW_WR_TO;
- do {
+ int ret;
- wait_event_timeout(wr_waitp->wait, wr_waitp->done, to);
- if (!wr_waitp->done) {
+ do {
+ ret = wait_event_timeout(wr_waitp->wait,
+ test_and_clear_bit(REPLY_READY, &wr_waitp->status), to);
+ if (!ret) {
printk(KERN_ERR MOD "%s - Device %s not responding - "
"tid %u qpid %u\n", func,
pci_name(rdev->lldi.pdev), hwtid, qpid);
+ if (c4iw_fatal_error(rdev)) {
+ wr_waitp->ret = -EIO;
+ break;
+ }
to = to << 2;
}
- } while (!wr_waitp->done);
+ } while (!ret);
if (wr_waitp->ret)
- printk(KERN_WARNING MOD "%s: FW reply %d tid %u qpid %u\n",
- pci_name(rdev->lldi.pdev), wr_waitp->ret, hwtid, qpid);
+ PDBG("%s: FW reply %d tid %u qpid %u\n",
+ pci_name(rdev->lldi.pdev), wr_waitp->ret, hwtid, qpid);
return wr_waitp->ret;
}
-
struct c4iw_dev {
struct ib_device ibdev;
struct c4iw_rdev rdev;
@@ -175,9 +191,7 @@ struct c4iw_dev {
struct idr qpidr;
struct idr mmidr;
spinlock_t lock;
- struct list_head entry;
struct dentry *debugfs_root;
- u8 registered;
};
static inline struct c4iw_dev *to_c4iw_dev(struct ib_device *ibdev)
diff --git a/drivers/infiniband/hw/cxgb4/provider.c b/drivers/infiniband/hw/cxgb4/provider.c
index f66dd8bf512..5b9e4220ca0 100644
--- a/drivers/infiniband/hw/cxgb4/provider.c
+++ b/drivers/infiniband/hw/cxgb4/provider.c
@@ -516,7 +516,6 @@ int c4iw_register_device(struct c4iw_dev *dev)
if (ret)
goto bail2;
}
- dev->registered = 1;
return 0;
bail2:
ib_unregister_device(&dev->ibdev);
@@ -535,6 +534,5 @@ void c4iw_unregister_device(struct c4iw_dev *dev)
c4iw_class_attributes[i]);
ib_unregister_device(&dev->ibdev);
kfree(dev->ibdev.iwcm);
- dev->registered = 0;
return;
}
diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c
index 70a5a3c646d..3b773b05a89 100644
--- a/drivers/infiniband/hw/cxgb4/qp.c
+++ b/drivers/infiniband/hw/cxgb4/qp.c
@@ -214,7 +214,7 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
V_FW_RI_RES_WR_HOSTFCMODE(0) | /* no host cidx updates */
V_FW_RI_RES_WR_CPRIO(0) | /* don't keep in chip cache */
V_FW_RI_RES_WR_PCIECHN(0) | /* set by uP at ri_init time */
- t4_sq_onchip(&wq->sq) ? F_FW_RI_RES_WR_ONCHIP : 0 |
+ (t4_sq_onchip(&wq->sq) ? F_FW_RI_RES_WR_ONCHIP : 0) |
V_FW_RI_RES_WR_IQID(scq->cqid));
res->u.sqrq.dcaen_to_eqsize = cpu_to_be32(
V_FW_RI_RES_WR_DCAEN(0) |
@@ -1210,7 +1210,6 @@ int c4iw_modify_qp(struct c4iw_dev *rhp, struct c4iw_qp *qhp,
if (ret) {
if (internal)
c4iw_get_ep(&qhp->ep->com);
- disconnect = abort = 1;
goto err;
}
break;
diff --git a/drivers/infiniband/hw/cxgb4/t4.h b/drivers/infiniband/hw/cxgb4/t4.h
index 24af12fc822..c0221eec881 100644
--- a/drivers/infiniband/hw/cxgb4/t4.h
+++ b/drivers/infiniband/hw/cxgb4/t4.h
@@ -269,11 +269,8 @@ struct t4_swsqe {
static inline pgprot_t t4_pgprot_wc(pgprot_t prot)
{
-#if defined(__i386__) || defined(__x86_64__)
+#if defined(__i386__) || defined(__x86_64__) || defined(CONFIG_PPC64)
return pgprot_writecombine(prot);
-#elif defined(CONFIG_PPC64)
- return __pgprot((pgprot_val(prot) | _PAGE_NO_CACHE) &
- ~(pgprot_t)_PAGE_GUARDED);
#else
return pgprot_noncached(prot);
#endif
diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c
index 58c0e417bc3..be24ac72611 100644
--- a/drivers/infiniband/hw/ipath/ipath_driver.c
+++ b/drivers/infiniband/hw/ipath/ipath_driver.c
@@ -398,7 +398,6 @@ static int __devinit ipath_init_one(struct pci_dev *pdev,
struct ipath_devdata *dd;
unsigned long long addr;
u32 bar0 = 0, bar1 = 0;
- u8 rev;
dd = ipath_alloc_devdata(pdev);
if (IS_ERR(dd)) {
@@ -540,13 +539,7 @@ static int __devinit ipath_init_one(struct pci_dev *pdev,
goto bail_regions;
}
- ret = pci_read_config_byte(pdev, PCI_REVISION_ID, &rev);
- if (ret) {
- ipath_dev_err(dd, "Failed to read PCI revision ID unit "
- "%u: err %d\n", dd->ipath_unit, -ret);
- goto bail_regions; /* shouldn't ever happen */
- }
- dd->ipath_pcirev = rev;
+ dd->ipath_pcirev = pdev->revision;
#if defined(__powerpc__)
/* There isn't a generic way to specify writethrough mappings */
diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c
index 33c7eedaba6..e74cdf9ef47 100644
--- a/drivers/infiniband/hw/nes/nes_cm.c
+++ b/drivers/infiniband/hw/nes/nes_cm.c
@@ -2563,7 +2563,7 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp)
u16 last_ae;
u8 original_hw_tcp_state;
u8 original_ibqp_state;
- enum iw_cm_event_status disconn_status = IW_CM_EVENT_STATUS_OK;
+ int disconn_status = 0;
int issue_disconn = 0;
int issue_close = 0;
int issue_flush = 0;
@@ -2605,7 +2605,7 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp)
(last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET))) {
issue_disconn = 1;
if (last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET)
- disconn_status = IW_CM_EVENT_STATUS_RESET;
+ disconn_status = -ECONNRESET;
}
if (((original_hw_tcp_state == NES_AEQE_TCP_STATE_CLOSED) ||
@@ -2666,7 +2666,7 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp)
cm_id->provider_data = nesqp;
/* Send up the close complete event */
cm_event.event = IW_CM_EVENT_CLOSE;
- cm_event.status = IW_CM_EVENT_STATUS_OK;
+ cm_event.status = 0;
cm_event.provider_data = cm_id->provider_data;
cm_event.local_addr = cm_id->local_addr;
cm_event.remote_addr = cm_id->remote_addr;
@@ -2966,7 +2966,7 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
nes_add_ref(&nesqp->ibqp);
cm_event.event = IW_CM_EVENT_ESTABLISHED;
- cm_event.status = IW_CM_EVENT_STATUS_ACCEPTED;
+ cm_event.status = 0;
cm_event.provider_data = (void *)nesqp;
cm_event.local_addr = cm_id->local_addr;
cm_event.remote_addr = cm_id->remote_addr;
@@ -3377,7 +3377,7 @@ static void cm_event_connected(struct nes_cm_event *event)
/* notify OF layer we successfully created the requested connection */
cm_event.event = IW_CM_EVENT_CONNECT_REPLY;
- cm_event.status = IW_CM_EVENT_STATUS_ACCEPTED;
+ cm_event.status = 0;
cm_event.provider_data = cm_id->provider_data;
cm_event.local_addr.sin_family = AF_INET;
cm_event.local_addr.sin_port = cm_id->local_addr.sin_port;
@@ -3484,7 +3484,7 @@ static void cm_event_reset(struct nes_cm_event *event)
nesqp->cm_id = NULL;
/* cm_id->provider_data = NULL; */
cm_event.event = IW_CM_EVENT_DISCONNECT;
- cm_event.status = IW_CM_EVENT_STATUS_RESET;
+ cm_event.status = -ECONNRESET;
cm_event.provider_data = cm_id->provider_data;
cm_event.local_addr = cm_id->local_addr;
cm_event.remote_addr = cm_id->remote_addr;
@@ -3495,7 +3495,7 @@ static void cm_event_reset(struct nes_cm_event *event)
ret = cm_id->event_handler(cm_id, &cm_event);
atomic_inc(&cm_closes);
cm_event.event = IW_CM_EVENT_CLOSE;
- cm_event.status = IW_CM_EVENT_STATUS_OK;
+ cm_event.status = 0;
cm_event.provider_data = cm_id->provider_data;
cm_event.local_addr = cm_id->local_addr;
cm_event.remote_addr = cm_id->remote_addr;
@@ -3534,7 +3534,7 @@ static void cm_event_mpa_req(struct nes_cm_event *event)
cm_node, cm_id, jiffies);
cm_event.event = IW_CM_EVENT_CONNECT_REQUEST;
- cm_event.status = IW_CM_EVENT_STATUS_OK;
+ cm_event.status = 0;
cm_event.provider_data = (void *)cm_node;
cm_event.local_addr.sin_family = AF_INET;
diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c
index 26d8018c0a7..95ca93ceeda 100644
--- a/drivers/infiniband/hw/nes/nes_verbs.c
+++ b/drivers/infiniband/hw/nes/nes_verbs.c
@@ -1484,7 +1484,7 @@ static int nes_destroy_qp(struct ib_qp *ibqp)
(nesqp->ibqp_state == IB_QPS_RTR)) && (nesqp->cm_id)) {
cm_id = nesqp->cm_id;
cm_event.event = IW_CM_EVENT_CONNECT_REPLY;
- cm_event.status = IW_CM_EVENT_STATUS_TIMEOUT;
+ cm_event.status = -ETIMEDOUT;
cm_event.local_addr = cm_id->local_addr;
cm_event.remote_addr = cm_id->remote_addr;
cm_event.private_data = NULL;
diff --git a/drivers/infiniband/hw/qib/qib_iba6120.c b/drivers/infiniband/hw/qib/qib_iba6120.c
index 7de4b7ebffc..d8ca0a0b970 100644
--- a/drivers/infiniband/hw/qib/qib_iba6120.c
+++ b/drivers/infiniband/hw/qib/qib_iba6120.c
@@ -1799,7 +1799,7 @@ static int qib_6120_setup_reset(struct qib_devdata *dd)
/*
* Keep chip from being accessed until we are ready. Use
* writeq() directly, to allow the write even though QIB_PRESENT
- * isn't' set.
+ * isn't set.
*/
dd->flags &= ~(QIB_INITTED | QIB_PRESENT);
dd->int_counter = 0; /* so we check interrupts work again */
diff --git a/drivers/infiniband/hw/qib/qib_iba7220.c b/drivers/infiniband/hw/qib/qib_iba7220.c
index 74fe0360bec..c765a2eb04c 100644
--- a/drivers/infiniband/hw/qib/qib_iba7220.c
+++ b/drivers/infiniband/hw/qib/qib_iba7220.c
@@ -2111,7 +2111,7 @@ static int qib_setup_7220_reset(struct qib_devdata *dd)
/*
* Keep chip from being accessed until we are ready. Use
* writeq() directly, to allow the write even though QIB_PRESENT
- * isn't' set.
+ * isn't set.
*/
dd->flags &= ~(QIB_INITTED | QIB_PRESENT);
dd->int_counter = 0; /* so we check interrupts work again */
diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c
index 55de3cf3441..9f53e68a096 100644
--- a/drivers/infiniband/hw/qib/qib_iba7322.c
+++ b/drivers/infiniband/hw/qib/qib_iba7322.c
@@ -3299,7 +3299,7 @@ static int qib_do_7322_reset(struct qib_devdata *dd)
/*
* Keep chip from being accessed until we are ready. Use
* writeq() directly, to allow the write even though QIB_PRESENT
- * isn't' set.
+ * isn't set.
*/
dd->flags &= ~(QIB_INITTED | QIB_PRESENT | QIB_BADINTR);
dd->flags |= QIB_DOING_RESET;
@@ -7534,7 +7534,8 @@ static int serdes_7322_init_new(struct qib_pportdata *ppd)
ibsd_wr_allchans(ppd, 4, (1 << 10), BMASK(10, 10));
tstart = get_jiffies_64();
while (chan_done &&
- !time_after64(tstart, tstart + msecs_to_jiffies(500))) {
+ !time_after64(get_jiffies_64(),
+ tstart + msecs_to_jiffies(500))) {
msleep(20);
for (chan = 0; chan < SERDES_CHANS; ++chan) {
rxcaldone = ahb_mod(ppd->dd, IBSD(ppd->hw_pidx),
diff --git a/drivers/infiniband/hw/qib/qib_pcie.c b/drivers/infiniband/hw/qib/qib_pcie.c
index 48b6674cbc4..891cc2ff5f0 100644
--- a/drivers/infiniband/hw/qib/qib_pcie.c
+++ b/drivers/infiniband/hw/qib/qib_pcie.c
@@ -526,11 +526,8 @@ static int qib_tune_pcie_coalesce(struct qib_devdata *dd)
*/
devid = parent->device;
if (devid >= 0x25e2 && devid <= 0x25fa) {
- u8 rev;
-
/* 5000 P/V/X/Z */
- pci_read_config_byte(parent, PCI_REVISION_ID, &rev);
- if (rev <= 0xb2)
+ if (parent->revision <= 0xb2)
bits = 1U << 10;
else
bits = 7U << 10;
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 7f42d3a454d..88d8e4cb419 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -39,13 +39,13 @@ struct evdev {
};
struct evdev_client {
- int head;
- int tail;
+ unsigned int head;
+ unsigned int tail;
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
- int bufsize;
+ unsigned int bufsize;
struct input_event buffer[];
};
@@ -55,16 +55,25 @@ static DEFINE_MUTEX(evdev_table_mutex);
static void evdev_pass_event(struct evdev_client *client,
struct input_event *event)
{
- /*
- * Interrupts are disabled, just acquire the lock.
- * Make sure we don't leave with the client buffer
- * "empty" by having client->head == client->tail.
- */
+ /* Interrupts are disabled, just acquire the lock. */
spin_lock(&client->buffer_lock);
- do {
- client->buffer[client->head++] = *event;
- client->head &= client->bufsize - 1;
- } while (client->head == client->tail);
+
+ client->buffer[client->head++] = *event;
+ client->head &= client->bufsize - 1;
+
+ if (unlikely(client->head == client->tail)) {
+ /*
+ * This effectively "drops" all unconsumed events, leaving
+ * EV_SYN/SYN_DROPPED plus the newest event in the queue.
+ */
+ client->tail = (client->head - 2) & (client->bufsize - 1);
+
+ client->buffer[client->tail].time = event->time;
+ client->buffer[client->tail].type = EV_SYN;
+ client->buffer[client->tail].code = SYN_DROPPED;
+ client->buffer[client->tail].value = 0;
+ }
+
spin_unlock(&client->buffer_lock);
if (event->type == EV_SYN)
diff --git a/drivers/input/input.c b/drivers/input/input.c
index d6e8bd8a851..ebbceedc92f 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -1746,6 +1746,42 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int
}
EXPORT_SYMBOL(input_set_capability);
+static unsigned int input_estimate_events_per_packet(struct input_dev *dev)
+{
+ int mt_slots;
+ int i;
+ unsigned int events;
+
+ if (dev->mtsize) {
+ mt_slots = dev->mtsize;
+ } 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,
+ clamp(mt_slots, 2, 32);
+ } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
+ mt_slots = 2;
+ } else {
+ mt_slots = 0;
+ }
+
+ events = mt_slots + 1; /* count SYN_MT_REPORT and SYN_REPORT */
+
+ for (i = 0; i < ABS_CNT; i++) {
+ if (test_bit(i, dev->absbit)) {
+ if (input_is_mt_axis(i))
+ events += mt_slots;
+ else
+ events++;
+ }
+ }
+
+ for (i = 0; i < REL_CNT; i++)
+ if (test_bit(i, dev->relbit))
+ events++;
+
+ return events;
+}
+
#define INPUT_CLEANSE_BITMASK(dev, type, bits) \
do { \
if (!test_bit(EV_##type, dev->evbit)) \
@@ -1793,6 +1829,10 @@ 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);
+
/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
diff --git a/drivers/input/keyboard/atakbd.c b/drivers/input/keyboard/atakbd.c
index 1839194ea98..10bcd4ae540 100644
--- a/drivers/input/keyboard/atakbd.c
+++ b/drivers/input/keyboard/atakbd.c
@@ -223,8 +223,9 @@ static int __init atakbd_init(void)
return -ENODEV;
// need to init core driver if not already done so
- if (atari_keyb_init())
- return -ENODEV;
+ error = atari_keyb_init();
+ if (error)
+ return error;
atakbd_dev = input_allocate_device();
if (!atakbd_dev)
diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c
index 09bef79d9da..a26922cf0e8 100644
--- a/drivers/input/keyboard/twl4030_keypad.c
+++ b/drivers/input/keyboard/twl4030_keypad.c
@@ -332,18 +332,20 @@ static int __devinit twl4030_kp_program(struct twl4030_keypad *kp)
static int __devinit twl4030_kp_probe(struct platform_device *pdev)
{
struct twl4030_keypad_data *pdata = pdev->dev.platform_data;
- const struct matrix_keymap_data *keymap_data = pdata->keymap_data;
+ const struct matrix_keymap_data *keymap_data;
struct twl4030_keypad *kp;
struct input_dev *input;
u8 reg;
int error;
- if (!pdata || !pdata->rows || !pdata->cols ||
+ if (!pdata || !pdata->rows || !pdata->cols || !pdata->keymap_data ||
pdata->rows > TWL4030_MAX_ROWS || pdata->cols > TWL4030_MAX_COLS) {
dev_err(&pdev->dev, "Invalid platform_data\n");
return -EINVAL;
}
+ keymap_data = pdata->keymap_data;
+
kp = kzalloc(sizeof(*kp), GFP_KERNEL);
input = input_allocate_device();
if (!kp || !input) {
diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c
index 7077f9bf5ea..62bae99424e 100644
--- a/drivers/input/misc/xen-kbdfront.c
+++ b/drivers/input/misc/xen-kbdfront.c
@@ -303,7 +303,7 @@ static void xenkbd_backend_changed(struct xenbus_device *dev,
enum xenbus_state backend_state)
{
struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
- int val;
+ int ret, val;
switch (backend_state) {
case XenbusStateInitialising:
@@ -316,6 +316,17 @@ static void xenkbd_backend_changed(struct xenbus_device *dev,
case XenbusStateInitWait:
InitWait:
+ ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+ "feature-abs-pointer", "%d", &val);
+ if (ret < 0)
+ val = 0;
+ if (val) {
+ ret = xenbus_printf(XBT_NIL, info->xbdev->nodename,
+ "request-abs-pointer", "1");
+ if (ret)
+ pr_warning("xenkbd: can't request abs-pointer");
+ }
+
xenbus_switch_state(dev, XenbusStateConnected);
break;
diff --git a/drivers/input/mouse/atarimouse.c b/drivers/input/mouse/atarimouse.c
index adf45b3040e..5c4a692bf73 100644
--- a/drivers/input/mouse/atarimouse.c
+++ b/drivers/input/mouse/atarimouse.c
@@ -77,15 +77,15 @@ static void atamouse_interrupt(char *buf)
#endif
/* only relative events get here */
- dx = buf[1];
- dy = -buf[2];
+ dx = buf[1];
+ dy = buf[2];
input_report_rel(atamouse_dev, REL_X, dx);
input_report_rel(atamouse_dev, REL_Y, dy);
- input_report_key(atamouse_dev, BTN_LEFT, buttons & 0x1);
+ input_report_key(atamouse_dev, BTN_LEFT, buttons & 0x4);
input_report_key(atamouse_dev, BTN_MIDDLE, buttons & 0x2);
- input_report_key(atamouse_dev, BTN_RIGHT, buttons & 0x4);
+ input_report_key(atamouse_dev, BTN_RIGHT, buttons & 0x1);
input_sync(atamouse_dev);
@@ -108,7 +108,7 @@ static int atamouse_open(struct input_dev *dev)
static void atamouse_close(struct input_dev *dev)
{
ikbd_mouse_disable();
- atari_mouse_interrupt_hook = NULL;
+ atari_input_mouse_interrupt_hook = NULL;
}
static int __init atamouse_init(void)
@@ -118,8 +118,9 @@ static int __init atamouse_init(void)
if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ST_MFP))
return -ENODEV;
- if (!atari_keyb_init())
- return -ENODEV;
+ error = atari_keyb_init();
+ if (error)
+ return error;
atamouse_dev = input_allocate_device();
if (!atamouse_dev)
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index c24946f5125..1de1c19dad3 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -281,17 +281,24 @@ struct ser_req {
u8 command;
u8 ref_off;
u16 scratch;
- __be16 sample;
struct spi_message msg;
struct spi_transfer xfer[6];
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ __be16 sample ____cacheline_aligned;
};
struct ads7845_ser_req {
u8 command[3];
- u8 pwrdown[3];
- u8 sample[3];
struct spi_message msg;
struct spi_transfer xfer[2];
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ u8 sample[3] ____cacheline_aligned;
};
static int ads7846_read12_ser(struct device *dev, unsigned command)
diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c
index efa06882de0..45f93d0f559 100644
--- a/drivers/input/touchscreen/h3600_ts_input.c
+++ b/drivers/input/touchscreen/h3600_ts_input.c
@@ -399,31 +399,34 @@ static int h3600ts_connect(struct serio *serio, struct serio_driver *drv)
IRQF_SHARED | IRQF_DISABLED, "h3600_action", &ts->dev)) {
printk(KERN_ERR "h3600ts.c: Could not allocate Action Button IRQ!\n");
err = -EBUSY;
- goto fail2;
+ goto fail1;
}
if (request_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, npower_button_handler,
IRQF_SHARED | IRQF_DISABLED, "h3600_suspend", &ts->dev)) {
printk(KERN_ERR "h3600ts.c: Could not allocate Power Button IRQ!\n");
err = -EBUSY;
- goto fail3;
+ goto fail2;
}
serio_set_drvdata(serio, ts);
err = serio_open(serio, drv);
if (err)
- return err;
+ goto fail3;
//h3600_flite_control(1, 25); /* default brightness */
- input_register_device(ts->dev);
+ err = input_register_device(ts->dev);
+ if (err)
+ goto fail4;
return 0;
-fail3: free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts->dev);
+fail4: serio_close(serio);
+fail3: serio_set_drvdata(serio, NULL);
+ free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts->dev);
fail2: free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts->dev);
-fail1: serio_set_drvdata(serio, NULL);
- input_free_device(input_dev);
+fail1: input_free_device(input_dev);
kfree(ts);
return err;
}
diff --git a/drivers/input/touchscreen/wm831x-ts.c b/drivers/input/touchscreen/wm831x-ts.c
index 6ae054f8e0a..9175d49d254 100644
--- a/drivers/input/touchscreen/wm831x-ts.c
+++ b/drivers/input/touchscreen/wm831x-ts.c
@@ -68,8 +68,23 @@ struct wm831x_ts {
unsigned int pd_irq;
bool pressure;
bool pen_down;
+ struct work_struct pd_data_work;
};
+static void wm831x_pd_data_work(struct work_struct *work)
+{
+ struct wm831x_ts *wm831x_ts =
+ container_of(work, struct wm831x_ts, pd_data_work);
+
+ if (wm831x_ts->pen_down) {
+ enable_irq(wm831x_ts->data_irq);
+ dev_dbg(wm831x_ts->wm831x->dev, "IRQ PD->DATA done\n");
+ } else {
+ enable_irq(wm831x_ts->pd_irq);
+ dev_dbg(wm831x_ts->wm831x->dev, "IRQ DATA->PD done\n");
+ }
+}
+
static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data)
{
struct wm831x_ts *wm831x_ts = irq_data;
@@ -110,6 +125,9 @@ static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data)
}
if (!wm831x_ts->pen_down) {
+ /* Switch from data to pen down */
+ dev_dbg(wm831x->dev, "IRQ DATA->PD\n");
+
disable_irq_nosync(wm831x_ts->data_irq);
/* Don't need data any more */
@@ -128,6 +146,10 @@ static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data)
ABS_PRESSURE, 0);
input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 0);
+
+ schedule_work(&wm831x_ts->pd_data_work);
+ } else {
+ input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1);
}
input_sync(wm831x_ts->input_dev);
@@ -141,6 +163,11 @@ static irqreturn_t wm831x_ts_pen_down_irq(int irq, void *irq_data)
struct wm831x *wm831x = wm831x_ts->wm831x;
int ena = 0;
+ if (wm831x_ts->pen_down)
+ return IRQ_HANDLED;
+
+ disable_irq_nosync(wm831x_ts->pd_irq);
+
/* Start collecting data */
if (wm831x_ts->pressure)
ena |= WM831X_TCH_Z_ENA;
@@ -149,14 +176,14 @@ static irqreturn_t wm831x_ts_pen_down_irq(int irq, void *irq_data)
WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA,
WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | ena);
- input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1);
- input_sync(wm831x_ts->input_dev);
-
wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
WM831X_TCHPD_EINT, WM831X_TCHPD_EINT);
wm831x_ts->pen_down = true;
- enable_irq(wm831x_ts->data_irq);
+
+ /* Switch from pen down to data */
+ dev_dbg(wm831x->dev, "IRQ PD->DATA\n");
+ schedule_work(&wm831x_ts->pd_data_work);
return IRQ_HANDLED;
}
@@ -182,13 +209,28 @@ static void wm831x_ts_input_close(struct input_dev *idev)
struct wm831x_ts *wm831x_ts = input_get_drvdata(idev);
struct wm831x *wm831x = wm831x_ts->wm831x;
+ /* Shut the controller down, disabling all other functionality too */
wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
- WM831X_TCH_ENA | WM831X_TCH_CVT_ENA |
- WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA |
- WM831X_TCH_Z_ENA, 0);
+ WM831X_TCH_ENA | WM831X_TCH_X_ENA |
+ WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, 0);
- if (wm831x_ts->pen_down)
+ /* Make sure any pending IRQs are done, the above will prevent
+ * new ones firing.
+ */
+ synchronize_irq(wm831x_ts->data_irq);
+ synchronize_irq(wm831x_ts->pd_irq);
+
+ /* Make sure the IRQ completion work is quiesced */
+ flush_work_sync(&wm831x_ts->pd_data_work);
+
+ /* If we ended up with the pen down then make sure we revert back
+ * to pen detection state for the next time we start up.
+ */
+ if (wm831x_ts->pen_down) {
disable_irq(wm831x_ts->data_irq);
+ enable_irq(wm831x_ts->pd_irq);
+ wm831x_ts->pen_down = false;
+ }
}
static __devinit int wm831x_ts_probe(struct platform_device *pdev)
@@ -198,7 +240,7 @@ static __devinit int wm831x_ts_probe(struct platform_device *pdev)
struct wm831x_pdata *core_pdata = dev_get_platdata(pdev->dev.parent);
struct wm831x_touch_pdata *pdata = NULL;
struct input_dev *input_dev;
- int error;
+ int error, irqf;
if (core_pdata)
pdata = core_pdata->touch;
@@ -212,6 +254,7 @@ static __devinit int wm831x_ts_probe(struct platform_device *pdev)
wm831x_ts->wm831x = wm831x;
wm831x_ts->input_dev = input_dev;
+ INIT_WORK(&wm831x_ts->pd_data_work, wm831x_pd_data_work);
/*
* If we have a direct IRQ use it, otherwise use the interrupt
@@ -270,9 +313,14 @@ static __devinit int wm831x_ts_probe(struct platform_device *pdev)
wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
WM831X_TCH_RATE_MASK, 6);
+ if (pdata && pdata->data_irqf)
+ irqf = pdata->data_irqf;
+ else
+ irqf = IRQF_TRIGGER_HIGH;
+
error = request_threaded_irq(wm831x_ts->data_irq,
NULL, wm831x_ts_data_irq,
- IRQF_ONESHOT,
+ irqf | IRQF_ONESHOT,
"Touchscreen data", wm831x_ts);
if (error) {
dev_err(&pdev->dev, "Failed to request data IRQ %d: %d\n",
@@ -281,9 +329,14 @@ static __devinit int wm831x_ts_probe(struct platform_device *pdev)
}
disable_irq(wm831x_ts->data_irq);
+ if (pdata && pdata->pd_irqf)
+ irqf = pdata->pd_irqf;
+ else
+ irqf = IRQF_TRIGGER_HIGH;
+
error = request_threaded_irq(wm831x_ts->pd_irq,
NULL, wm831x_ts_pen_down_irq,
- IRQF_ONESHOT,
+ irqf | IRQF_ONESHOT,
"Touchscreen pen down", wm831x_ts);
if (error) {
dev_err(&pdev->dev, "Failed to request pen down IRQ %d: %d\n",
diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c
index e7089a1f6cb..b37e6186d0f 100644
--- a/drivers/leds/leds-lm3530.c
+++ b/drivers/leds/leds-lm3530.c
@@ -349,6 +349,7 @@ static const struct i2c_device_id lm3530_id[] = {
{LM3530_NAME, 0},
{}
};
+MODULE_DEVICE_TABLE(i2c, lm3530_id);
static struct i2c_driver lm3530_i2c_driver = {
.probe = lm3530_probe,
diff --git a/drivers/leds/leds-regulator.c b/drivers/leds/leds-regulator.c
index 3790816643b..8497f56f8e4 100644
--- a/drivers/leds/leds-regulator.c
+++ b/drivers/leds/leds-regulator.c
@@ -178,6 +178,10 @@ static int __devinit regulator_led_probe(struct platform_device *pdev)
led->cdev.flags |= LED_CORE_SUSPENDRESUME;
led->vcc = vcc;
+ /* to handle correctly an already enabled regulator */
+ if (regulator_is_enabled(led->vcc))
+ led->enabled = 1;
+
mutex_init(&led->mutex);
INIT_WORK(&led->work, led_work);
diff --git a/drivers/lguest/Kconfig b/drivers/lguest/Kconfig
index 0aaa0597a62..34ae49dc557 100644
--- a/drivers/lguest/Kconfig
+++ b/drivers/lguest/Kconfig
@@ -5,8 +5,10 @@ config LGUEST
---help---
This is a very simple module which allows you to run
multiple instances of the same Linux kernel, using the
- "lguest" command found in the Documentation/lguest directory.
+ "lguest" command found in the Documentation/virtual/lguest
+ directory.
+
Note that "lguest" is pronounced to rhyme with "fell quest",
- not "rustyvisor". See Documentation/lguest/lguest.txt.
+ not "rustyvisor". See Documentation/virtual/lguest/lguest.txt.
If unsure, say N. If curious, say M. If masochistic, say Y.
diff --git a/drivers/lguest/Makefile b/drivers/lguest/Makefile
index 7d463c26124..8ac947c7e7c 100644
--- a/drivers/lguest/Makefile
+++ b/drivers/lguest/Makefile
@@ -18,7 +18,7 @@ Mastery: PREFIX=M
Beer:
@for f in Preparation Guest Drivers Launcher Host Switcher Mastery; do echo "{==- $$f -==}"; make -s $$f; done; echo "{==-==}"
Preparation Preparation! Guest Drivers Launcher Host Switcher Mastery:
- @sh ../../Documentation/lguest/extract $(PREFIX) `find ../../* -name '*.[chS]' -wholename '*lguest*'`
+ @sh ../../Documentation/virtual/lguest/extract $(PREFIX) `find ../../* -name '*.[chS]' -wholename '*lguest*'`
Puppy:
@clear
@printf " __ \n (___()'\`;\n /, /\`\n \\\\\\\"--\\\\\\ \n"
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index 8b021eb0d48..6cccd60c594 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -40,7 +40,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/device.h>
-#include <linux/sysdev.h>
+#include <linux/syscore_ops.h>
#include <linux/freezer.h>
#include <linux/syscalls.h>
#include <linux/suspend.h>
@@ -2527,12 +2527,9 @@ void pmu_blink(int n)
#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
int pmu_sys_suspended;
-static int pmu_sys_suspend(struct sys_device *sysdev, pm_message_t state)
+static int pmu_syscore_suspend(void)
{
- if (state.event != PM_EVENT_SUSPEND || pmu_sys_suspended)
- return 0;
-
- /* Suspend PMU event interrupts */\
+ /* Suspend PMU event interrupts */
pmu_suspend();
pmu_sys_suspended = 1;
@@ -2544,12 +2541,12 @@ static int pmu_sys_suspend(struct sys_device *sysdev, pm_message_t state)
return 0;
}
-static int pmu_sys_resume(struct sys_device *sysdev)
+static void pmu_syscore_resume(void)
{
struct adb_request req;
if (!pmu_sys_suspended)
- return 0;
+ return;
/* Tell PMU we are ready */
pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2);
@@ -2562,50 +2559,21 @@ static int pmu_sys_resume(struct sys_device *sysdev)
/* Resume PMU event interrupts */
pmu_resume();
pmu_sys_suspended = 0;
-
- return 0;
}
-#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */
-
-static struct sysdev_class pmu_sysclass = {
- .name = "pmu",
-};
-
-static struct sys_device device_pmu = {
- .cls = &pmu_sysclass,
-};
-
-static struct sysdev_driver driver_pmu = {
-#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32)
- .suspend = &pmu_sys_suspend,
- .resume = &pmu_sys_resume,
-#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */
+static struct syscore_ops pmu_syscore_ops = {
+ .suspend = pmu_syscore_suspend,
+ .resume = pmu_syscore_resume,
};
-static int __init init_pmu_sysfs(void)
+static int pmu_syscore_register(void)
{
- int rc;
+ register_syscore_ops(&pmu_syscore_ops);
- rc = sysdev_class_register(&pmu_sysclass);
- if (rc) {
- printk(KERN_ERR "Failed registering PMU sys class\n");
- return -ENODEV;
- }
- rc = sysdev_register(&device_pmu);
- if (rc) {
- printk(KERN_ERR "Failed registering PMU sys device\n");
- return -ENODEV;
- }
- rc = sysdev_driver_register(&pmu_sysclass, &driver_pmu);
- if (rc) {
- printk(KERN_ERR "Failed registering PMU sys driver\n");
- return -ENODEV;
- }
return 0;
}
-
-subsys_initcall(init_pmu_sysfs);
+subsys_initcall(pmu_syscore_register);
+#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */
EXPORT_SYMBOL(pmu_request);
EXPORT_SYMBOL(pmu_queue_request);
diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index 5ef136cdba9..e5d8904fc8f 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -390,13 +390,6 @@ static int raid_is_congested(struct dm_target_callbacks *cb, int bits)
return md_raid5_congested(&rs->md, bits);
}
-static void raid_unplug(struct dm_target_callbacks *cb)
-{
- struct raid_set *rs = container_of(cb, struct raid_set, callbacks);
-
- md_raid5_kick_device(rs->md.private);
-}
-
/*
* Construct a RAID4/5/6 mapping:
* Args:
@@ -487,7 +480,6 @@ static int raid_ctr(struct dm_target *ti, unsigned argc, char **argv)
}
rs->callbacks.congested_fn = raid_is_congested;
- rs->callbacks.unplug_fn = raid_unplug;
dm_table_add_target_callbacks(ti->table, &rs->callbacks);
return 0;
diff --git a/drivers/md/md.c b/drivers/md/md.c
index b12b3776c0c..7d6f7f18a92 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -447,48 +447,59 @@ EXPORT_SYMBOL(md_flush_request);
/* Support for plugging.
* This mirrors the plugging support in request_queue, but does not
- * require having a whole queue
+ * require having a whole queue or request structures.
+ * We allocate an md_plug_cb for each md device and each thread it gets
+ * plugged on. This links tot the private plug_handle structure in the
+ * personality data where we keep a count of the number of outstanding
+ * plugs so other code can see if a plug is active.
*/
-static void plugger_work(struct work_struct *work)
-{
- struct plug_handle *plug =
- container_of(work, struct plug_handle, unplug_work);
- plug->unplug_fn(plug);
-}
-static void plugger_timeout(unsigned long data)
-{
- struct plug_handle *plug = (void *)data;
- kblockd_schedule_work(NULL, &plug->unplug_work);
-}
-void plugger_init(struct plug_handle *plug,
- void (*unplug_fn)(struct plug_handle *))
-{
- plug->unplug_flag = 0;
- plug->unplug_fn = unplug_fn;
- init_timer(&plug->unplug_timer);
- plug->unplug_timer.function = plugger_timeout;
- plug->unplug_timer.data = (unsigned long)plug;
- INIT_WORK(&plug->unplug_work, plugger_work);
-}
-EXPORT_SYMBOL_GPL(plugger_init);
+struct md_plug_cb {
+ struct blk_plug_cb cb;
+ mddev_t *mddev;
+};
-void plugger_set_plug(struct plug_handle *plug)
+static void plugger_unplug(struct blk_plug_cb *cb)
{
- if (!test_and_set_bit(PLUGGED_FLAG, &plug->unplug_flag))
- mod_timer(&plug->unplug_timer, jiffies + msecs_to_jiffies(3)+1);
+ struct md_plug_cb *mdcb = container_of(cb, struct md_plug_cb, cb);
+ if (atomic_dec_and_test(&mdcb->mddev->plug_cnt))
+ md_wakeup_thread(mdcb->mddev->thread);
+ kfree(mdcb);
}
-EXPORT_SYMBOL_GPL(plugger_set_plug);
-int plugger_remove_plug(struct plug_handle *plug)
+/* Check that an unplug wakeup will come shortly.
+ * If not, wakeup the md thread immediately
+ */
+int mddev_check_plugged(mddev_t *mddev)
{
- if (test_and_clear_bit(PLUGGED_FLAG, &plug->unplug_flag)) {
- del_timer(&plug->unplug_timer);
- return 1;
- } else
+ struct blk_plug *plug = current->plug;
+ struct md_plug_cb *mdcb;
+
+ if (!plug)
return 0;
-}
-EXPORT_SYMBOL_GPL(plugger_remove_plug);
+ list_for_each_entry(mdcb, &plug->cb_list, cb.list) {
+ if (mdcb->cb.callback == plugger_unplug &&
+ mdcb->mddev == mddev) {
+ /* Already on the list, move to top */
+ if (mdcb != list_first_entry(&plug->cb_list,
+ struct md_plug_cb,
+ cb.list))
+ list_move(&mdcb->cb.list, &plug->cb_list);
+ return 1;
+ }
+ }
+ /* Not currently on the callback list */
+ mdcb = kmalloc(sizeof(*mdcb), GFP_ATOMIC);
+ if (!mdcb)
+ return 0;
+
+ mdcb->mddev = mddev;
+ mdcb->cb.callback = plugger_unplug;
+ atomic_inc(&mddev->plug_cnt);
+ list_add(&mdcb->cb.list, &plug->cb_list);
+ return 1;
+}
+EXPORT_SYMBOL_GPL(mddev_check_plugged);
static inline mddev_t *mddev_get(mddev_t *mddev)
{
@@ -538,6 +549,7 @@ void mddev_init(mddev_t *mddev)
atomic_set(&mddev->active, 1);
atomic_set(&mddev->openers, 0);
atomic_set(&mddev->active_io, 0);
+ atomic_set(&mddev->plug_cnt, 0);
spin_lock_init(&mddev->write_lock);
atomic_set(&mddev->flush_pending, 0);
init_waitqueue_head(&mddev->sb_wait);
@@ -3158,6 +3170,7 @@ level_store(mddev_t *mddev, const char *buf, size_t len)
mddev->layout = mddev->new_layout;
mddev->chunk_sectors = mddev->new_chunk_sectors;
mddev->delta_disks = 0;
+ mddev->degraded = 0;
if (mddev->pers->sync_request == NULL) {
/* this is now an array without redundancy, so
* it must always be in_sync
@@ -4723,7 +4736,6 @@ static void md_clean(mddev_t *mddev)
mddev->bitmap_info.chunksize = 0;
mddev->bitmap_info.daemon_sleep = 0;
mddev->bitmap_info.max_write_behind = 0;
- mddev->plug = NULL;
}
static void __md_stop_writes(mddev_t *mddev)
@@ -6688,12 +6700,6 @@ int md_allow_write(mddev_t *mddev)
}
EXPORT_SYMBOL_GPL(md_allow_write);
-void md_unplug(mddev_t *mddev)
-{
- if (mddev->plug)
- mddev->plug->unplug_fn(mddev->plug);
-}
-
#define SYNC_MARKS 10
#define SYNC_MARK_STEP (3*HZ)
void md_do_sync(mddev_t *mddev)
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 52b407369e1..0b1fd3f1d85 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -29,26 +29,6 @@
typedef struct mddev_s mddev_t;
typedef struct mdk_rdev_s mdk_rdev_t;
-/* generic plugging support - like that provided with request_queue,
- * but does not require a request_queue
- */
-struct plug_handle {
- void (*unplug_fn)(struct plug_handle *);
- struct timer_list unplug_timer;
- struct work_struct unplug_work;
- unsigned long unplug_flag;
-};
-#define PLUGGED_FLAG 1
-void plugger_init(struct plug_handle *plug,
- void (*unplug_fn)(struct plug_handle *));
-void plugger_set_plug(struct plug_handle *plug);
-int plugger_remove_plug(struct plug_handle *plug);
-static inline void plugger_flush(struct plug_handle *plug)
-{
- del_timer_sync(&plug->unplug_timer);
- cancel_work_sync(&plug->unplug_work);
-}
-
/*
* MD's 'extended' device
*/
@@ -199,6 +179,9 @@ struct mddev_s
int delta_disks, new_level, new_layout;
int new_chunk_sectors;
+ atomic_t plug_cnt; /* If device is expecting
+ * more bios soon.
+ */
struct mdk_thread_s *thread; /* management thread */
struct mdk_thread_s *sync_thread; /* doing resync or reconstruct */
sector_t curr_resync; /* last block scheduled */
@@ -336,7 +319,6 @@ struct mddev_s
struct list_head all_mddevs;
struct attribute_group *to_remove;
- struct plug_handle *plug; /* if used by personality */
struct bio_set *bio_set;
@@ -516,7 +498,6 @@ extern int md_integrity_register(mddev_t *mddev);
extern void md_integrity_add_rdev(mdk_rdev_t *rdev, mddev_t *mddev);
extern int strict_strtoul_scaled(const char *cp, unsigned long *res, int scale);
extern void restore_bitmap_write_access(struct file *file);
-extern void md_unplug(mddev_t *mddev);
extern void mddev_init(mddev_t *mddev);
extern int md_run(mddev_t *mddev);
@@ -530,4 +511,5 @@ extern struct bio *bio_clone_mddev(struct bio *bio, gfp_t gfp_mask,
mddev_t *mddev);
extern struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs,
mddev_t *mddev);
+extern int mddev_check_plugged(mddev_t *mddev);
#endif /* _MD_MD_H */
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index c2a21ae56d9..2b7a7ff401d 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -565,12 +565,6 @@ static void flush_pending_writes(conf_t *conf)
spin_unlock_irq(&conf->device_lock);
}
-static void md_kick_device(mddev_t *mddev)
-{
- blk_flush_plug(current);
- md_wakeup_thread(mddev->thread);
-}
-
/* Barriers....
* Sometimes we need to suspend IO while we do something else,
* either some resync/recovery, or reconfigure the array.
@@ -600,7 +594,7 @@ static void raise_barrier(conf_t *conf)
/* Wait until no block IO is waiting */
wait_event_lock_irq(conf->wait_barrier, !conf->nr_waiting,
- conf->resync_lock, md_kick_device(conf->mddev));
+ conf->resync_lock, );
/* block any new IO from starting */
conf->barrier++;
@@ -608,7 +602,7 @@ static void raise_barrier(conf_t *conf)
/* Now wait for all pending IO to complete */
wait_event_lock_irq(conf->wait_barrier,
!conf->nr_pending && conf->barrier < RESYNC_DEPTH,
- conf->resync_lock, md_kick_device(conf->mddev));
+ conf->resync_lock, );
spin_unlock_irq(&conf->resync_lock);
}
@@ -630,7 +624,7 @@ static void wait_barrier(conf_t *conf)
conf->nr_waiting++;
wait_event_lock_irq(conf->wait_barrier, !conf->barrier,
conf->resync_lock,
- md_kick_device(conf->mddev));
+ );
conf->nr_waiting--;
}
conf->nr_pending++;
@@ -666,8 +660,7 @@ static void freeze_array(conf_t *conf)
wait_event_lock_irq(conf->wait_barrier,
conf->nr_pending == conf->nr_queued+1,
conf->resync_lock,
- ({ flush_pending_writes(conf);
- md_kick_device(conf->mddev); }));
+ flush_pending_writes(conf));
spin_unlock_irq(&conf->resync_lock);
}
static void unfreeze_array(conf_t *conf)
@@ -729,6 +722,7 @@ static int make_request(mddev_t *mddev, struct bio * bio)
const unsigned long do_sync = (bio->bi_rw & REQ_SYNC);
const unsigned long do_flush_fua = (bio->bi_rw & (REQ_FLUSH | REQ_FUA));
mdk_rdev_t *blocked_rdev;
+ int plugged;
/*
* Register the new request and wait if the reconstruction
@@ -820,6 +814,8 @@ static int make_request(mddev_t *mddev, struct bio * bio)
* inc refcount on their rdev. Record them by setting
* bios[x] to bio
*/
+ plugged = mddev_check_plugged(mddev);
+
disks = conf->raid_disks;
retry_write:
blocked_rdev = NULL;
@@ -925,7 +921,7 @@ static int make_request(mddev_t *mddev, struct bio * bio)
/* In case raid1d snuck in to freeze_array */
wake_up(&conf->wait_barrier);
- if (do_sync || !bitmap)
+ if (do_sync || !bitmap || !plugged)
md_wakeup_thread(mddev->thread);
return 0;
@@ -1516,13 +1512,16 @@ static void raid1d(mddev_t *mddev)
conf_t *conf = mddev->private;
struct list_head *head = &conf->retry_list;
mdk_rdev_t *rdev;
+ struct blk_plug plug;
md_check_recovery(mddev);
-
+
+ blk_start_plug(&plug);
for (;;) {
char b[BDEVNAME_SIZE];
- flush_pending_writes(conf);
+ if (atomic_read(&mddev->plug_cnt) == 0)
+ flush_pending_writes(conf);
spin_lock_irqsave(&conf->device_lock, flags);
if (list_empty(head)) {
@@ -1593,6 +1592,7 @@ static void raid1d(mddev_t *mddev)
}
cond_resched();
}
+ blk_finish_plug(&plug);
}
@@ -2039,7 +2039,6 @@ static int stop(mddev_t *mddev)
md_unregister_thread(mddev->thread);
mddev->thread = NULL;
- blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
if (conf->r1bio_pool)
mempool_destroy(conf->r1bio_pool);
kfree(conf->mirrors);
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 2da83d56659..8e9462626ec 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -634,12 +634,6 @@ static void flush_pending_writes(conf_t *conf)
spin_unlock_irq(&conf->device_lock);
}
-static void md_kick_device(mddev_t *mddev)
-{
- blk_flush_plug(current);
- md_wakeup_thread(mddev->thread);
-}
-
/* Barriers....
* Sometimes we need to suspend IO while we do something else,
* either some resync/recovery, or reconfigure the array.
@@ -669,15 +663,15 @@ static void raise_barrier(conf_t *conf, int force)
/* Wait until no block IO is waiting (unless 'force') */
wait_event_lock_irq(conf->wait_barrier, force || !conf->nr_waiting,
- conf->resync_lock, md_kick_device(conf->mddev));
+ conf->resync_lock, );
/* block any new IO from starting */
conf->barrier++;
- /* No wait for all pending IO to complete */
+ /* Now wait for all pending IO to complete */
wait_event_lock_irq(conf->wait_barrier,
!conf->nr_pending && conf->barrier < RESYNC_DEPTH,
- conf->resync_lock, md_kick_device(conf->mddev));
+ conf->resync_lock, );
spin_unlock_irq(&conf->resync_lock);
}
@@ -698,7 +692,7 @@ static void wait_barrier(conf_t *conf)
conf->nr_waiting++;
wait_event_lock_irq(conf->wait_barrier, !conf->barrier,
conf->resync_lock,
- md_kick_device(conf->mddev));
+ );
conf->nr_waiting--;
}
conf->nr_pending++;
@@ -734,8 +728,8 @@ static void freeze_array(conf_t *conf)
wait_event_lock_irq(conf->wait_barrier,
conf->nr_pending == conf->nr_queued+1,
conf->resync_lock,
- ({ flush_pending_writes(conf);
- md_kick_device(conf->mddev); }));
+ flush_pending_writes(conf));
+
spin_unlock_irq(&conf->resync_lock);
}
@@ -762,6 +756,7 @@ static int make_request(mddev_t *mddev, struct bio * bio)
const unsigned long do_fua = (bio->bi_rw & REQ_FUA);
unsigned long flags;
mdk_rdev_t *blocked_rdev;
+ int plugged;
if (unlikely(bio->bi_rw & REQ_FLUSH)) {
md_flush_request(mddev, bio);
@@ -870,6 +865,8 @@ static int make_request(mddev_t *mddev, struct bio * bio)
* inc refcount on their rdev. Record them by setting
* bios[x] to bio
*/
+ plugged = mddev_check_plugged(mddev);
+
raid10_find_phys(conf, r10_bio);
retry_write:
blocked_rdev = NULL;
@@ -946,9 +943,8 @@ static int make_request(mddev_t *mddev, struct bio * bio)
/* In case raid10d snuck in to freeze_array */
wake_up(&conf->wait_barrier);
- if (do_sync || !mddev->bitmap)
+ if (do_sync || !mddev->bitmap || !plugged)
md_wakeup_thread(mddev->thread);
-
return 0;
}
@@ -1640,9 +1636,11 @@ static void raid10d(mddev_t *mddev)
conf_t *conf = mddev->private;
struct list_head *head = &conf->retry_list;
mdk_rdev_t *rdev;
+ struct blk_plug plug;
md_check_recovery(mddev);
+ blk_start_plug(&plug);
for (;;) {
char b[BDEVNAME_SIZE];
@@ -1716,6 +1714,7 @@ static void raid10d(mddev_t *mddev)
}
cond_resched();
}
+ blk_finish_plug(&plug);
}
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index e867ee42b15..49bf5f89143 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -27,12 +27,12 @@
*
* We group bitmap updates into batches. Each batch has a number.
* We may write out several batches at once, but that isn't very important.
- * conf->bm_write is the number of the last batch successfully written.
- * conf->bm_flush is the number of the last batch that was closed to
+ * conf->seq_write is the number of the last batch successfully written.
+ * conf->seq_flush is the number of the last batch that was closed to
* new additions.
* When we discover that we will need to write to any block in a stripe
* (in add_stripe_bio) we update the in-memory bitmap and record in sh->bm_seq
- * the number of the batch it will be in. This is bm_flush+1.
+ * the number of the batch it will be in. This is seq_flush+1.
* When we are ready to do a write, if that batch hasn't been written yet,
* we plug the array and queue the stripe for later.
* When an unplug happens, we increment bm_flush, thus closing the current
@@ -199,14 +199,12 @@ static void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh)
BUG_ON(!list_empty(&sh->lru));
BUG_ON(atomic_read(&conf->active_stripes)==0);
if (test_bit(STRIPE_HANDLE, &sh->state)) {
- if (test_bit(STRIPE_DELAYED, &sh->state)) {
+ if (test_bit(STRIPE_DELAYED, &sh->state))
list_add_tail(&sh->lru, &conf->delayed_list);
- plugger_set_plug(&conf->plug);
- } else if (test_bit(STRIPE_BIT_DELAY, &sh->state) &&
- sh->bm_seq - conf->seq_write > 0) {
+ else if (test_bit(STRIPE_BIT_DELAY, &sh->state) &&
+ sh->bm_seq - conf->seq_write > 0)
list_add_tail(&sh->lru, &conf->bitmap_list);
- plugger_set_plug(&conf->plug);
- } else {
+ else {
clear_bit(STRIPE_BIT_DELAY, &sh->state);
list_add_tail(&sh->lru, &conf->handle_list);
}
@@ -461,7 +459,7 @@ get_active_stripe(raid5_conf_t *conf, sector_t sector,
< (conf->max_nr_stripes *3/4)
|| !conf->inactive_blocked),
conf->device_lock,
- md_raid5_kick_device(conf));
+ );
conf->inactive_blocked = 0;
} else
init_stripe(sh, sector, previous);
@@ -1470,7 +1468,7 @@ static int resize_stripes(raid5_conf_t *conf, int newsize)
wait_event_lock_irq(conf->wait_for_stripe,
!list_empty(&conf->inactive_list),
conf->device_lock,
- blk_flush_plug(current));
+ );
osh = get_free_stripe(conf);
spin_unlock_irq(&conf->device_lock);
atomic_set(&nsh->count, 1);
@@ -3623,8 +3621,7 @@ static void raid5_activate_delayed(raid5_conf_t *conf)
atomic_inc(&conf->preread_active_stripes);
list_add_tail(&sh->lru, &conf->hold_list);
}
- } else
- plugger_set_plug(&conf->plug);
+ }
}
static void activate_bit_delay(raid5_conf_t *conf)
@@ -3641,21 +3638,6 @@ static void activate_bit_delay(raid5_conf_t *conf)
}
}
-void md_raid5_kick_device(raid5_conf_t *conf)
-{
- blk_flush_plug(current);
- raid5_activate_delayed(conf);
- md_wakeup_thread(conf->mddev->thread);
-}
-EXPORT_SYMBOL_GPL(md_raid5_kick_device);
-
-static void raid5_unplug(struct plug_handle *plug)
-{
- raid5_conf_t *conf = container_of(plug, raid5_conf_t, plug);
-
- md_raid5_kick_device(conf);
-}
-
int md_raid5_congested(mddev_t *mddev, int bits)
{
raid5_conf_t *conf = mddev->private;
@@ -3945,6 +3927,7 @@ static int make_request(mddev_t *mddev, struct bio * bi)
struct stripe_head *sh;
const int rw = bio_data_dir(bi);
int remaining;
+ int plugged;
if (unlikely(bi->bi_rw & REQ_FLUSH)) {
md_flush_request(mddev, bi);
@@ -3963,6 +3946,7 @@ static int make_request(mddev_t *mddev, struct bio * bi)
bi->bi_next = NULL;
bi->bi_phys_segments = 1; /* over-loaded to count active stripes */
+ plugged = mddev_check_plugged(mddev);
for (;logical_sector < last_sector; logical_sector += STRIPE_SECTORS) {
DEFINE_WAIT(w);
int disks, data_disks;
@@ -4057,7 +4041,7 @@ static int make_request(mddev_t *mddev, struct bio * bi)
* add failed due to overlap. Flush everything
* and wait a while
*/
- md_raid5_kick_device(conf);
+ md_wakeup_thread(mddev->thread);
release_stripe(sh);
schedule();
goto retry;
@@ -4077,6 +4061,9 @@ static int make_request(mddev_t *mddev, struct bio * bi)
}
}
+ if (!plugged)
+ md_wakeup_thread(mddev->thread);
+
spin_lock_irq(&conf->device_lock);
remaining = raid5_dec_bi_phys_segments(bi);
spin_unlock_irq(&conf->device_lock);
@@ -4478,24 +4465,30 @@ static void raid5d(mddev_t *mddev)
struct stripe_head *sh;
raid5_conf_t *conf = mddev->private;
int handled;
+ struct blk_plug plug;
pr_debug("+++ raid5d active\n");
md_check_recovery(mddev);
+ blk_start_plug(&plug);
handled = 0;
spin_lock_irq(&conf->device_lock);
while (1) {
struct bio *bio;
- if (conf->seq_flush != conf->seq_write) {
- int seq = conf->seq_flush;
+ if (atomic_read(&mddev->plug_cnt) == 0 &&
+ !list_empty(&conf->bitmap_list)) {
+ /* Now is a good time to flush some bitmap updates */
+ conf->seq_flush++;
spin_unlock_irq(&conf->device_lock);
bitmap_unplug(mddev->bitmap);
spin_lock_irq(&conf->device_lock);
- conf->seq_write = seq;
+ conf->seq_write = conf->seq_flush;
activate_bit_delay(conf);
}
+ if (atomic_read(&mddev->plug_cnt) == 0)
+ raid5_activate_delayed(conf);
while ((bio = remove_bio_from_retry(conf))) {
int ok;
@@ -4525,6 +4518,7 @@ static void raid5d(mddev_t *mddev)
spin_unlock_irq(&conf->device_lock);
async_tx_issue_pending_all();
+ blk_finish_plug(&plug);
pr_debug("--- raid5d inactive\n");
}
@@ -5141,8 +5135,6 @@ static int run(mddev_t *mddev)
mdname(mddev));
md_set_array_sectors(mddev, raid5_size(mddev, 0, 0));
- plugger_init(&conf->plug, raid5_unplug);
- mddev->plug = &conf->plug;
if (mddev->queue) {
int chunk_size;
/* read-ahead size must cover two whole stripes, which
@@ -5159,7 +5151,6 @@ static int run(mddev_t *mddev)
mddev->queue->backing_dev_info.congested_data = mddev;
mddev->queue->backing_dev_info.congested_fn = raid5_congested;
- mddev->queue->queue_lock = &conf->device_lock;
chunk_size = mddev->chunk_sectors << 9;
blk_queue_io_min(mddev->queue, chunk_size);
@@ -5192,7 +5183,6 @@ static int stop(mddev_t *mddev)
mddev->thread = NULL;
if (mddev->queue)
mddev->queue->backing_dev_info.congested_fn = NULL;
- plugger_flush(&conf->plug); /* the unplug fn references 'conf'*/
free_conf(conf);
mddev->private = NULL;
mddev->to_remove = &raid5_attrs_group;
@@ -5688,6 +5678,7 @@ static void raid5_quiesce(mddev_t *mddev, int state)
static void *raid45_takeover_raid0(mddev_t *mddev, int level)
{
struct raid0_private_data *raid0_priv = mddev->private;
+ sector_t sectors;
/* for raid0 takeover only one zone is supported */
if (raid0_priv->nr_strip_zones > 1) {
@@ -5696,6 +5687,9 @@ static void *raid45_takeover_raid0(mddev_t *mddev, int level)
return ERR_PTR(-EINVAL);
}
+ sectors = raid0_priv->strip_zone[0].zone_end;
+ sector_div(sectors, raid0_priv->strip_zone[0].nb_dev);
+ mddev->dev_sectors = sectors;
mddev->new_level = level;
mddev->new_layout = ALGORITHM_PARITY_N;
mddev->new_chunk_sectors = mddev->chunk_sectors;
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index 8d563a4f022..3ca77a2613b 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -400,8 +400,6 @@ struct raid5_private_data {
* Cleared when a sync completes.
*/
- struct plug_handle plug;
-
/* per cpu variables */
struct raid5_percpu {
struct page *spare_page; /* Used when checking P/Q in raid6 */
diff --git a/drivers/media/common/tuners/tda18271-common.c b/drivers/media/common/tuners/tda18271-common.c
index 5466d47db89..aae40e52af5 100644
--- a/drivers/media/common/tuners/tda18271-common.c
+++ b/drivers/media/common/tuners/tda18271-common.c
@@ -533,16 +533,7 @@ int tda18271_calc_main_pll(struct dvb_frontend *fe, u32 freq)
if (tda_fail(ret))
goto fail;
- regs[R_MPD] = (0x77 & pd);
-
- switch (priv->mode) {
- case TDA18271_ANALOG:
- regs[R_MPD] &= ~0x08;
- break;
- case TDA18271_DIGITAL:
- regs[R_MPD] |= 0x08;
- break;
- }
+ regs[R_MPD] = (0x7f & pd);
div = ((d * (freq / 1000)) << 7) / 125;
diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c
index 9ad4454a148..d884f5eee73 100644
--- a/drivers/media/common/tuners/tda18271-fe.c
+++ b/drivers/media/common/tuners/tda18271-fe.c
@@ -579,8 +579,8 @@ static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq)
#define RF3 2
u32 rf_default[3];
u32 rf_freq[3];
- u8 prog_cal[3];
- u8 prog_tab[3];
+ s32 prog_cal[3];
+ s32 prog_tab[3];
i = tda18271_lookup_rf_band(fe, &freq, NULL);
@@ -602,32 +602,33 @@ static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq)
return bcal;
tda18271_calc_rf_cal(fe, &rf_freq[rf]);
- prog_tab[rf] = regs[R_EB14];
+ prog_tab[rf] = (s32)regs[R_EB14];
if (1 == bcal)
- prog_cal[rf] = tda18271_calibrate_rf(fe, rf_freq[rf]);
+ prog_cal[rf] =
+ (s32)tda18271_calibrate_rf(fe, rf_freq[rf]);
else
prog_cal[rf] = prog_tab[rf];
switch (rf) {
case RF1:
map[i].rf_a1 = 0;
- map[i].rf_b1 = (s32)(prog_cal[RF1] - prog_tab[RF1]);
+ map[i].rf_b1 = (prog_cal[RF1] - prog_tab[RF1]);
map[i].rf1 = rf_freq[RF1] / 1000;
break;
case RF2:
- dividend = (s32)(prog_cal[RF2] - prog_tab[RF2]) -
- (s32)(prog_cal[RF1] + prog_tab[RF1]);
+ dividend = (prog_cal[RF2] - prog_tab[RF2] -
+ prog_cal[RF1] + prog_tab[RF1]);
divisor = (s32)(rf_freq[RF2] - rf_freq[RF1]) / 1000;
map[i].rf_a1 = (dividend / divisor);
map[i].rf2 = rf_freq[RF2] / 1000;
break;
case RF3:
- dividend = (s32)(prog_cal[RF3] - prog_tab[RF3]) -
- (s32)(prog_cal[RF2] + prog_tab[RF2]);
+ dividend = (prog_cal[RF3] - prog_tab[RF3] -
+ prog_cal[RF2] + prog_tab[RF2]);
divisor = (s32)(rf_freq[RF3] - rf_freq[RF2]) / 1000;
map[i].rf_a2 = (dividend / divisor);
- map[i].rf_b2 = (s32)(prog_cal[RF2] - prog_tab[RF2]);
+ map[i].rf_b2 = (prog_cal[RF2] - prog_tab[RF2]);
map[i].rf3 = rf_freq[RF3] / 1000;
break;
default:
diff --git a/drivers/media/common/tuners/tda18271-maps.c b/drivers/media/common/tuners/tda18271-maps.c
index e7f84c705da..3d5b6ab7e33 100644
--- a/drivers/media/common/tuners/tda18271-maps.c
+++ b/drivers/media/common/tuners/tda18271-maps.c
@@ -229,8 +229,7 @@ static struct tda18271_map tda18271c2_km[] = {
static struct tda18271_map tda18271_rf_band[] = {
{ .rfmax = 47900, .val = 0x00 },
{ .rfmax = 61100, .val = 0x01 },
-/* { .rfmax = 152600, .val = 0x02 }, */
- { .rfmax = 121200, .val = 0x02 },
+ { .rfmax = 152600, .val = 0x02 },
{ .rfmax = 164700, .val = 0x03 },
{ .rfmax = 203500, .val = 0x04 },
{ .rfmax = 457800, .val = 0x05 },
@@ -448,7 +447,7 @@ static struct tda18271_map tda18271c2_rf_cal[] = {
{ .rfmax = 150000, .val = 0xb0 },
{ .rfmax = 151000, .val = 0xb1 },
{ .rfmax = 152000, .val = 0xb7 },
- { .rfmax = 153000, .val = 0xbd },
+ { .rfmax = 152600, .val = 0xbd },
{ .rfmax = 154000, .val = 0x20 },
{ .rfmax = 155000, .val = 0x22 },
{ .rfmax = 156000, .val = 0x24 },
@@ -459,7 +458,7 @@ static struct tda18271_map tda18271c2_rf_cal[] = {
{ .rfmax = 161000, .val = 0x2d },
{ .rfmax = 163000, .val = 0x2e },
{ .rfmax = 164000, .val = 0x2f },
- { .rfmax = 165000, .val = 0x30 },
+ { .rfmax = 164700, .val = 0x30 },
{ .rfmax = 166000, .val = 0x11 },
{ .rfmax = 167000, .val = 0x12 },
{ .rfmax = 168000, .val = 0x13 },
@@ -510,7 +509,8 @@ static struct tda18271_map tda18271c2_rf_cal[] = {
{ .rfmax = 236000, .val = 0x1b },
{ .rfmax = 237000, .val = 0x1c },
{ .rfmax = 240000, .val = 0x1d },
- { .rfmax = 242000, .val = 0x1f },
+ { .rfmax = 242000, .val = 0x1e },
+ { .rfmax = 244000, .val = 0x1f },
{ .rfmax = 247000, .val = 0x20 },
{ .rfmax = 249000, .val = 0x21 },
{ .rfmax = 252000, .val = 0x22 },
@@ -624,7 +624,7 @@ static struct tda18271_map tda18271c2_rf_cal[] = {
{ .rfmax = 453000, .val = 0x93 },
{ .rfmax = 454000, .val = 0x94 },
{ .rfmax = 456000, .val = 0x96 },
- { .rfmax = 457000, .val = 0x98 },
+ { .rfmax = 457800, .val = 0x98 },
{ .rfmax = 461000, .val = 0x11 },
{ .rfmax = 468000, .val = 0x12 },
{ .rfmax = 472000, .val = 0x13 },
diff --git a/drivers/media/dvb/b2c2/flexcop-pci.c b/drivers/media/dvb/b2c2/flexcop-pci.c
index 955254090a0..03f96d6ca89 100644
--- a/drivers/media/dvb/b2c2/flexcop-pci.c
+++ b/drivers/media/dvb/b2c2/flexcop-pci.c
@@ -38,7 +38,7 @@ MODULE_PARM_DESC(debug,
DEBSTATUS);
#define DRIVER_VERSION "0.1"
-#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV PCI Driver"
+#define DRIVER_NAME "flexcop-pci"
#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de>"
struct flexcop_pci {
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index fe4f894183f..c545039287a 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -356,13 +356,15 @@ config DVB_USB_LME2510
select DVB_TDA826X if !DVB_FE_CUSTOMISE
select DVB_STV0288 if !DVB_FE_CUSTOMISE
select DVB_IX2505V if !DVB_FE_CUSTOMISE
+ select DVB_STV0299 if !DVB_FE_CUSTOMISE
+ select DVB_PLL if !DVB_FE_CUSTOMISE
help
Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 .
config DVB_USB_TECHNISAT_USB2
tristate "Technisat DVB-S/S2 USB2.0 support"
depends on DVB_USB
- select DVB_STB0899 if !DVB_FE_CUSTOMISE
- select DVB_STB6100 if !DVB_FE_CUSTOMISE
+ select DVB_STV090x if !DVB_FE_CUSTOMISE
+ select DVB_STV6110x if !DVB_FE_CUSTOMISE
help
Say Y here to support the Technisat USB2 DVB-S/S2 device
diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c
index 97af266d7f1..65214af5cd7 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c
@@ -2162,7 +2162,7 @@ struct dibx000_agc_config dib7090_agc_config[2] = {
.agc1_pt3 = 98,
.agc1_slope1 = 0,
.agc1_slope2 = 167,
- .agc1_pt1 = 98,
+ .agc2_pt1 = 98,
.agc2_pt2 = 255,
.agc2_slope1 = 104,
.agc2_slope2 = 0,
@@ -2440,11 +2440,11 @@ static int tfe7090pvr_frontend0_attach(struct dvb_usb_adapter *adap)
dib0700_set_i2c_speed(adap->dev, 340);
adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x90, &tfe7090pvr_dib7000p_config[0]);
- dib7090_slave_reset(adap->fe);
-
if (adap->fe == NULL)
return -ENODEV;
+ dib7090_slave_reset(adap->fe);
+
return 0;
}
diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c
index ccc2d1af49d..6927c726ce3 100644
--- a/drivers/media/dvb/ngene/ngene-core.c
+++ b/drivers/media/dvb/ngene/ngene-core.c
@@ -1520,6 +1520,7 @@ static int init_channel(struct ngene_channel *chan)
if (dev->ci.en && (io & NGENE_IO_TSOUT)) {
dvb_ca_en50221_init(adapter, dev->ci.en, 0, 1);
set_transfer(chan, 1);
+ chan->dev->channel[2].DataFormatFlags = DF_SWAP32;
set_transfer(&chan->dev->channel[2], 1);
dvb_register_device(adapter, &chan->ci_dev,
&ngene_dvbdev_ci, (void *) chan,
diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c
index 23640ed44d8..056138f63c7 100644
--- a/drivers/media/media-entity.c
+++ b/drivers/media/media-entity.c
@@ -378,7 +378,6 @@ EXPORT_SYMBOL_GPL(media_entity_create_link);
static int __media_entity_setup_link_notify(struct media_link *link, u32 flags)
{
- const u32 mask = MEDIA_LNK_FL_ENABLED;
int ret;
/* Notify both entities. */
@@ -395,7 +394,7 @@ static int __media_entity_setup_link_notify(struct media_link *link, u32 flags)
return ret;
}
- link->flags = (link->flags & ~mask) | (flags & mask);
+ link->flags = flags;
link->reverse->flags = link->flags;
return 0;
@@ -417,6 +416,7 @@ static int __media_entity_setup_link_notify(struct media_link *link, u32 flags)
*/
int __media_entity_setup_link(struct media_link *link, u32 flags)
{
+ const u32 mask = MEDIA_LNK_FL_ENABLED;
struct media_device *mdev;
struct media_entity *source, *sink;
int ret = -EBUSY;
@@ -424,6 +424,10 @@ int __media_entity_setup_link(struct media_link *link, u32 flags)
if (link == NULL)
return -EINVAL;
+ /* The non-modifiable link flags must not be modified. */
+ if ((link->flags & ~mask) != (flags & ~mask))
+ return -EINVAL;
+
if (link->flags & MEDIA_LNK_FL_IMMUTABLE)
return link->flags == flags ? 0 : -EINVAL;
diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c
index dc3f04c52d5..87bad7678d9 100644
--- a/drivers/media/radio/radio-sf16fmr2.c
+++ b/drivers/media/radio/radio-sf16fmr2.c
@@ -170,7 +170,7 @@ static int fmr2_setfreq(struct fmr2 *dev)
return 0;
}
-/* !!! not tested, in my card this does't work !!! */
+/* !!! not tested, in my card this doesn't work !!! */
static int fmr2_setvolume(struct fmr2 *dev)
{
int vol[16] = { 0x021, 0x084, 0x090, 0x104,
diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c
index 585680ffbfb..b1193dfc508 100644
--- a/drivers/media/radio/saa7706h.c
+++ b/drivers/media/radio/saa7706h.c
@@ -376,7 +376,7 @@ static int __devinit saa7706h_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%02x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kmalloc(sizeof(struct saa7706h_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct saa7706h_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c
index 7c0d77751f6..0991e197367 100644
--- a/drivers/media/radio/tef6862.c
+++ b/drivers/media/radio/tef6862.c
@@ -176,7 +176,7 @@ static int __devinit tef6862_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%02x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kmalloc(sizeof(struct tef6862_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tef6862_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
state->freq = TEF6862_LO_FREQ;
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index ebd68edf5b2..8fc0f081b47 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -46,7 +46,7 @@
#define MOD_AUTHOR "Jarod Wilson <jarod@wilsonet.com>"
#define MOD_DESC "Driver for SoundGraph iMON MultiMedia IR/Display"
#define MOD_NAME "imon"
-#define MOD_VERSION "0.9.2"
+#define MOD_VERSION "0.9.3"
#define DISPLAY_MINOR_BASE 144
#define DEVICE_NAME "lcd%d"
@@ -460,8 +460,9 @@ static int display_close(struct inode *inode, struct file *file)
}
/**
- * Sends a packet to the device -- this function must be called
- * with ictx->lock held.
+ * Sends a packet to the device -- this function must be called with
+ * ictx->lock held, or its unlock/lock sequence while waiting for tx
+ * to complete can/will lead to a deadlock.
*/
static int send_packet(struct imon_context *ictx)
{
@@ -991,12 +992,21 @@ static void imon_touch_display_timeout(unsigned long data)
* the iMON remotes, and those used by the Windows MCE remotes (which is
* really just RC-6), but only one or the other at a time, as the signals
* are decoded onboard the receiver.
+ *
+ * This function gets called two different ways, one way is from
+ * rc_register_device, for initial protocol selection/setup, and the other is
+ * via a userspace-initiated protocol change request, either by direct sysfs
+ * prodding or by something like ir-keytable. In the rc_register_device case,
+ * the imon context lock is already held, but when initiated from userspace,
+ * it is not, so we must acquire it prior to calling send_packet, which
+ * requires that the lock is held.
*/
static int imon_ir_change_protocol(struct rc_dev *rc, u64 rc_type)
{
int retval;
struct imon_context *ictx = rc->priv;
struct device *dev = ictx->dev;
+ bool unlock = false;
unsigned char ir_proto_packet[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86 };
@@ -1029,6 +1039,11 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 rc_type)
memcpy(ictx->usb_tx_buf, &ir_proto_packet, sizeof(ir_proto_packet));
+ if (!mutex_is_locked(&ictx->lock)) {
+ unlock = true;
+ mutex_lock(&ictx->lock);
+ }
+
retval = send_packet(ictx);
if (retval)
goto out;
@@ -1037,6 +1052,9 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 rc_type)
ictx->pad_mouse = false;
out:
+ if (unlock)
+ mutex_unlock(&ictx->lock);
+
return retval;
}
@@ -2134,6 +2152,7 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf)
goto rdev_setup_failed;
}
+ mutex_unlock(&ictx->lock);
return ictx;
rdev_setup_failed:
@@ -2205,6 +2224,7 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf,
goto urb_submit_failed;
}
+ mutex_unlock(&ictx->lock);
return ictx;
urb_submit_failed:
@@ -2299,6 +2319,8 @@ static int __devinit imon_probe(struct usb_interface *interface,
usb_set_intfdata(interface, ictx);
if (ifnum == 0) {
+ mutex_lock(&ictx->lock);
+
if (product == 0xffdc && ictx->rf_device) {
sysfs_err = sysfs_create_group(&interface->dev.kobj,
&imon_rf_attr_group);
@@ -2309,13 +2331,14 @@ static int __devinit imon_probe(struct usb_interface *interface,
if (ictx->display_supported)
imon_init_display(ictx, interface);
+
+ mutex_unlock(&ictx->lock);
}
dev_info(dev, "iMON device (%04x:%04x, intf%d) on "
"usb<%d:%d> initialized\n", vendor, product, ifnum,
usbdev->bus->busnum, usbdev->devnum);
- mutex_unlock(&ictx->lock);
mutex_unlock(&driver_lock);
return 0;
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index accaf6c9789..43908a70bd8 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -36,6 +36,7 @@
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
+#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/bitops.h>
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index 044fb7a382d..0c273ec465c 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -220,6 +220,8 @@ static struct usb_device_id mceusb_dev_table[] = {
{ USB_DEVICE(VENDOR_PHILIPS, 0x206c) },
/* Philips/Spinel plus IR transceiver for ASUS */
{ USB_DEVICE(VENDOR_PHILIPS, 0x2088) },
+ /* Philips IR transceiver (Dell branded) */
+ { USB_DEVICE(VENDOR_PHILIPS, 0x2093) },
/* Realtek MCE IR Receiver and card reader */
{ USB_DEVICE(VENDOR_REALTEK, 0x0161),
.driver_info = MULTIFUNCTION },
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index f53f9c68d38..a2706648e36 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -707,7 +707,8 @@ static void ir_close(struct input_dev *idev)
{
struct rc_dev *rdev = input_get_drvdata(idev);
- rdev->close(rdev);
+ if (rdev)
+ rdev->close(rdev);
}
/* class for /sys/class/rc */
@@ -733,6 +734,7 @@ static struct {
{ RC_TYPE_SONY, "sony" },
{ RC_TYPE_RC5_SZ, "rc-5-sz" },
{ RC_TYPE_LIRC, "lirc" },
+ { RC_TYPE_OTHER, "other" },
};
#define PROTO_NONE "none"
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 4498b944dec..00f51dd121f 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -875,7 +875,7 @@ config MX3_VIDEO
config VIDEO_MX3
tristate "i.MX3x Camera Sensor Interface driver"
depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA
- select VIDEOBUF_DMA_CONTIG
+ select VIDEOBUF2_DMA_CONTIG
select MX3_VIDEO
---help---
This is a v4l2 driver for the i.MX3x Camera Sensor Interface
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index c6e2ca3b114..6fbc356113c 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -350,9 +350,17 @@ void cx18_streams_cleanup(struct cx18 *cx, int unregister)
/* No struct video_device, but can have buffers allocated */
if (type == CX18_ENC_STREAM_TYPE_IDX) {
+ /* If the module params didn't inhibit IDX ... */
if (cx->stream_buffers[type] != 0) {
cx->stream_buffers[type] = 0;
- cx18_stream_free(&cx->streams[type]);
+ /*
+ * Before calling cx18_stream_free(),
+ * check if the IDX stream was actually set up.
+ * Needed, since the cx18_probe() error path
+ * exits through here as well as normal clean up
+ */
+ if (cx->streams[type].buffers != 0)
+ cx18_stream_free(&cx->streams[type]);
}
continue;
}
diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig
index 3b6e7f28568..caab1bfb79e 100644
--- a/drivers/media/video/cx23885/Kconfig
+++ b/drivers/media/video/cx23885/Kconfig
@@ -22,6 +22,7 @@ config VIDEO_CX23885
select DVB_CX24116 if !DVB_FE_CUSTOMISE
select DVB_STV0900 if !DVB_FE_CUSTOMISE
select DVB_DS3000 if !DVB_FE_CUSTOMISE
+ select DVB_STV0367 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c
index c820e2f5352..3f442003623 100644
--- a/drivers/media/video/cx88/cx88-input.c
+++ b/drivers/media/video/cx88/cx88-input.c
@@ -524,7 +524,7 @@ void cx88_ir_irq(struct cx88_core *core)
for (todo = 32; todo > 0; todo -= bits) {
ev.pulse = samples & 0x80000000 ? false : true;
bits = min(todo, 32U - fls(ev.pulse ? samples : ~samples));
- ev.duration = (bits * NSEC_PER_SEC) / (1000 * ir_samplerate);
+ ev.duration = (bits * (NSEC_PER_SEC / 1000)) / ir_samplerate;
ir_raw_event_store_with_filter(ir->dev, &ev);
samples <<= bits;
}
diff --git a/drivers/media/video/imx074.c b/drivers/media/video/imx074.c
index 1a116911571..0382ea752e6 100644
--- a/drivers/media/video/imx074.c
+++ b/drivers/media/video/imx074.c
@@ -298,7 +298,7 @@ static unsigned long imx074_query_bus_param(struct soc_camera_device *icd)
static int imx074_set_bus_param(struct soc_camera_device *icd,
unsigned long flags)
{
- return -1;
+ return -EINVAL;
}
static struct soc_camera_ops imx074_ops = {
diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c
index 5e1c9a81984..303ffa7df4a 100644
--- a/drivers/media/video/m52790.c
+++ b/drivers/media/video/m52790.c
@@ -174,7 +174,7 @@ static int m52790_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kmalloc(sizeof(struct m52790_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct m52790_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
diff --git a/drivers/media/video/omap3isp/isp.c b/drivers/media/video/omap3isp/isp.c
index 503bd7922bd..472a69359e6 100644
--- a/drivers/media/video/omap3isp/isp.c
+++ b/drivers/media/video/omap3isp/isp.c
@@ -215,20 +215,21 @@ static u32 isp_set_xclk(struct isp_device *isp, u32 xclk, u8 xclksel)
}
switch (xclksel) {
- case 0:
+ case ISP_XCLK_A:
isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
ISPTCTRL_CTRL_DIVA_MASK,
divisor << ISPTCTRL_CTRL_DIVA_SHIFT);
dev_dbg(isp->dev, "isp_set_xclk(): cam_xclka set to %d Hz\n",
currentxclk);
break;
- case 1:
+ case ISP_XCLK_B:
isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
ISPTCTRL_CTRL_DIVB_MASK,
divisor << ISPTCTRL_CTRL_DIVB_SHIFT);
dev_dbg(isp->dev, "isp_set_xclk(): cam_xclkb set to %d Hz\n",
currentxclk);
break;
+ case ISP_XCLK_NONE:
default:
omap3isp_put(isp);
dev_dbg(isp->dev, "ISP_ERR: isp_set_xclk(): Invalid requested "
@@ -237,13 +238,13 @@ static u32 isp_set_xclk(struct isp_device *isp, u32 xclk, u8 xclksel)
}
/* Do we go from stable whatever to clock? */
- if (divisor >= 2 && isp->xclk_divisor[xclksel] < 2)
+ if (divisor >= 2 && isp->xclk_divisor[xclksel - 1] < 2)
omap3isp_get(isp);
/* Stopping the clock. */
- else if (divisor < 2 && isp->xclk_divisor[xclksel] >= 2)
+ else if (divisor < 2 && isp->xclk_divisor[xclksel - 1] >= 2)
omap3isp_put(isp);
- isp->xclk_divisor[xclksel] = divisor;
+ isp->xclk_divisor[xclksel - 1] = divisor;
omap3isp_put(isp);
@@ -285,7 +286,8 @@ static void isp_power_settings(struct isp_device *isp, int idle)
*/
void omap3isp_configure_bridge(struct isp_device *isp,
enum ccdc_input_entity input,
- const struct isp_parallel_platform_data *pdata)
+ const struct isp_parallel_platform_data *pdata,
+ unsigned int shift)
{
u32 ispctrl_val;
@@ -298,9 +300,9 @@ void omap3isp_configure_bridge(struct isp_device *isp,
switch (input) {
case CCDC_INPUT_PARALLEL:
ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL;
- ispctrl_val |= pdata->data_lane_shift << ISPCTRL_SHIFT_SHIFT;
ispctrl_val |= pdata->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT;
ispctrl_val |= pdata->bridge << ISPCTRL_PAR_BRIDGE_SHIFT;
+ shift += pdata->data_lane_shift * 2;
break;
case CCDC_INPUT_CSI2A:
@@ -319,6 +321,8 @@ void omap3isp_configure_bridge(struct isp_device *isp,
return;
}
+ ispctrl_val |= ((shift/2) << ISPCTRL_SHIFT_SHIFT) & ISPCTRL_SHIFT_MASK;
+
ispctrl_val &= ~ISPCTRL_SYNC_DETECT_MASK;
ispctrl_val |= ISPCTRL_SYNC_DETECT_VSRISE;
@@ -658,6 +662,8 @@ int omap3isp_pipeline_pm_use(struct media_entity *entity, int use)
/* Apply power change to connected non-nodes. */
ret = isp_pipeline_pm_power(entity, change);
+ if (ret < 0)
+ entity->use_count -= change;
mutex_unlock(&entity->parent->graph_mutex);
@@ -872,6 +878,9 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe)
}
}
+ if (failure < 0)
+ isp->needs_reset = true;
+
return failure;
}
@@ -884,7 +893,8 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe)
* single-shot or continuous mode.
*
* Return 0 if successful, or the return value of the failed video::s_stream
- * operation otherwise.
+ * operation otherwise. The pipeline state is not updated when the operation
+ * fails, except when stopping the pipeline.
*/
int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe,
enum isp_pipeline_stream_state state)
@@ -895,7 +905,9 @@ int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe,
ret = isp_pipeline_disable(pipe);
else
ret = isp_pipeline_enable(pipe, state);
- pipe->stream_state = state;
+
+ if (ret == 0 || state == ISP_PIPELINE_STREAM_STOPPED)
+ pipe->stream_state = state;
return ret;
}
@@ -1481,6 +1493,10 @@ void omap3isp_put(struct isp_device *isp)
if (--isp->ref_count == 0) {
isp_disable_interrupts(isp);
isp_save_ctx(isp);
+ if (isp->needs_reset) {
+ isp_reset(isp);
+ isp->needs_reset = false;
+ }
isp_disable_clocks(isp);
}
mutex_unlock(&isp->isp_mutex);
diff --git a/drivers/media/video/omap3isp/isp.h b/drivers/media/video/omap3isp/isp.h
index cf5214e95a9..2620c405f5e 100644
--- a/drivers/media/video/omap3isp/isp.h
+++ b/drivers/media/video/omap3isp/isp.h
@@ -132,7 +132,6 @@ struct isp_reg {
/**
* struct isp_parallel_platform_data - Parallel interface platform data
- * @width: Parallel bus width in bits (8, 10, 11 or 12)
* @data_lane_shift: Data lane shifter
* 0 - CAMEXT[13:0] -> CAM[13:0]
* 1 - CAMEXT[13:2] -> CAM[11:0]
@@ -146,7 +145,6 @@ struct isp_reg {
* ISPCTRL_PAR_BRIDGE_BENDIAN - Big endian
*/
struct isp_parallel_platform_data {
- unsigned int width;
unsigned int data_lane_shift:2;
unsigned int clk_pol:1;
unsigned int bridge:4;
@@ -262,6 +260,7 @@ struct isp_device {
/* ISP Obj */
spinlock_t stat_lock; /* common lock for statistic drivers */
struct mutex isp_mutex; /* For handling ref_count field */
+ bool needs_reset;
int has_context;
int ref_count;
unsigned int autoidle;
@@ -311,11 +310,12 @@ int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe,
enum isp_pipeline_stream_state state);
void omap3isp_configure_bridge(struct isp_device *isp,
enum ccdc_input_entity input,
- const struct isp_parallel_platform_data *pdata);
+ const struct isp_parallel_platform_data *pdata,
+ unsigned int shift);
-#define ISP_XCLK_NONE -1
-#define ISP_XCLK_A 0
-#define ISP_XCLK_B 1
+#define ISP_XCLK_NONE 0
+#define ISP_XCLK_A 1
+#define ISP_XCLK_B 2
struct isp_device *omap3isp_get(struct isp_device *isp);
void omap3isp_put(struct isp_device *isp);
diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c
index 5ff9d14ce71..39d501bda63 100644
--- a/drivers/media/video/omap3isp/ispccdc.c
+++ b/drivers/media/video/omap3isp/ispccdc.c
@@ -43,6 +43,12 @@ __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
static const unsigned int ccdc_fmts[] = {
V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_MBUS_FMT_Y10_1X10,
+ V4L2_MBUS_FMT_Y12_1X12,
+ V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_MBUS_FMT_SGBRG8_1X8,
V4L2_MBUS_FMT_SGRBG10_1X10,
V4L2_MBUS_FMT_SRGGB10_1X10,
V4L2_MBUS_FMT_SBGGR10_1X10,
@@ -1110,21 +1116,38 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
struct isp_parallel_platform_data *pdata = NULL;
struct v4l2_subdev *sensor;
struct v4l2_mbus_framefmt *format;
+ const struct isp_format_info *fmt_info;
+ struct v4l2_subdev_format fmt_src;
+ unsigned int depth_out;
+ unsigned int depth_in = 0;
struct media_pad *pad;
unsigned long flags;
+ unsigned int shift;
u32 syn_mode;
u32 ccdc_pattern;
- if (ccdc->input == CCDC_INPUT_PARALLEL) {
- pad = media_entity_remote_source(&ccdc->pads[CCDC_PAD_SINK]);
- sensor = media_entity_to_v4l2_subdev(pad->entity);
+ pad = media_entity_remote_source(&ccdc->pads[CCDC_PAD_SINK]);
+ sensor = media_entity_to_v4l2_subdev(pad->entity);
+ if (ccdc->input == CCDC_INPUT_PARALLEL)
pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv)
->bus.parallel;
+
+ /* Compute shift value for lane shifter to configure the bridge. */
+ fmt_src.pad = pad->index;
+ fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ if (!v4l2_subdev_call(sensor, pad, get_fmt, NULL, &fmt_src)) {
+ fmt_info = omap3isp_video_format_info(fmt_src.format.code);
+ depth_in = fmt_info->bpp;
}
- omap3isp_configure_bridge(isp, ccdc->input, pdata);
+ fmt_info = omap3isp_video_format_info
+ (isp->isp_ccdc.formats[CCDC_PAD_SINK].code);
+ depth_out = fmt_info->bpp;
+
+ shift = depth_in - depth_out;
+ omap3isp_configure_bridge(isp, ccdc->input, pdata, shift);
- ccdc->syncif.datsz = pdata ? pdata->width : 10;
+ ccdc->syncif.datsz = depth_out;
ccdc_config_sync_if(ccdc, &ccdc->syncif);
/* CCDC_PAD_SINK */
@@ -1338,7 +1361,7 @@ static int ccdc_sbl_wait_idle(struct isp_ccdc_device *ccdc,
* @ccdc: Pointer to ISP CCDC device.
* @event: Pointing which event trigger handler
*
- * Return 1 when the event and stopping request combination is satisfyied,
+ * Return 1 when the event and stopping request combination is satisfied,
* zero otherwise.
*/
static int __ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event)
@@ -1618,7 +1641,7 @@ static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer)
ccdc_set_outaddr(ccdc, buffer->isp_addr);
- /* We now have a buffer queued on the output, restart the pipeline in
+ /* We now have a buffer queued on the output, restart the pipeline
* on the next CCDC interrupt if running in continuous mode (or when
* starting the stream).
*/
diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c
index 2b16988a501..aba537af87e 100644
--- a/drivers/media/video/omap3isp/isppreview.c
+++ b/drivers/media/video/omap3isp/isppreview.c
@@ -755,7 +755,7 @@ static struct preview_update update_attrs[] = {
* @configs - pointer to update config structure.
* @config - return pointer to appropriate structure field.
* @bit - for which feature to return pointers.
- * Return size of coresponding prev_params member
+ * Return size of corresponding prev_params member
*/
static u32
__preview_get_ptrs(struct prev_params *params, void **param,
diff --git a/drivers/media/video/omap3isp/ispqueue.c b/drivers/media/video/omap3isp/ispqueue.c
index 8fddc5806b0..9c317148205 100644
--- a/drivers/media/video/omap3isp/ispqueue.c
+++ b/drivers/media/video/omap3isp/ispqueue.c
@@ -339,7 +339,7 @@ static int isp_video_buffer_prepare_user(struct isp_video_buffer *buf)
up_read(&current->mm->mmap_sem);
if (ret != buf->npages) {
- buf->npages = ret;
+ buf->npages = ret < 0 ? 0 : ret;
isp_video_buffer_cleanup(buf);
return -EFAULT;
}
@@ -408,8 +408,8 @@ done:
* isp_video_buffer_prepare_vm_flags - Get VMA flags for a userspace address
*
* This function locates the VMAs for the buffer's userspace address and checks
- * that their flags match. The onlflag that we need to care for at the moment is
- * VM_PFNMAP.
+ * that their flags match. The only flag that we need to care for at the moment
+ * is VM_PFNMAP.
*
* The buffer vm_flags field is set to the first VMA flags.
*
diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c
index 653f88ba56d..0bb0f8cd36f 100644
--- a/drivers/media/video/omap3isp/ispresizer.c
+++ b/drivers/media/video/omap3isp/ispresizer.c
@@ -714,19 +714,50 @@ static void resizer_print_status(struct isp_res_device *res)
* iw and ih are the input width and height after cropping. Those equations need
* to be satisfied exactly for the resizer to work correctly.
*
- * Reverting the equations, we can compute the resizing ratios with
+ * The equations can't be easily reverted, as the >> 8 operation is not linear.
+ * In addition, not all input sizes can be achieved for a given output size. To
+ * get the highest input size lower than or equal to the requested input size,
+ * we need to compute the highest resizing ratio that satisfies the following
+ * inequality (taking the 4-tap mode width equation as an example)
+ *
+ * iw >= (32 * sph + (ow - 1) * hrsz + 16) >> 8 - 7
+ *
+ * (where iw is the requested input width) which can be rewritten as
+ *
+ * iw - 7 >= (32 * sph + (ow - 1) * hrsz + 16) >> 8
+ * (iw - 7) << 8 >= 32 * sph + (ow - 1) * hrsz + 16 - b
+ * ((iw - 7) << 8) + b >= 32 * sph + (ow - 1) * hrsz + 16
+ *
+ * where b is the value of the 8 least significant bits of the right hand side
+ * expression of the last inequality. The highest resizing ratio value will be
+ * achieved when b is equal to its maximum value of 255. That resizing ratio
+ * value will still satisfy the original inequality, as b will disappear when
+ * the expression will be shifted right by 8.
+ *
+ * The reverted the equations thus become
*
* - 8-phase, 4-tap mode
- * hrsz = ((iw - 7) * 256 - 16 - 32 * sph) / (ow - 1)
- * vrsz = ((ih - 4) * 256 - 16 - 32 * spv) / (oh - 1)
+ * hrsz = ((iw - 7) * 256 + 255 - 16 - 32 * sph) / (ow - 1)
+ * vrsz = ((ih - 4) * 256 + 255 - 16 - 32 * spv) / (oh - 1)
* - 4-phase, 7-tap mode
- * hrsz = ((iw - 7) * 256 - 32 - 64 * sph) / (ow - 1)
- * vrsz = ((ih - 7) * 256 - 32 - 64 * spv) / (oh - 1)
+ * hrsz = ((iw - 7) * 256 + 255 - 32 - 64 * sph) / (ow - 1)
+ * vrsz = ((ih - 7) * 256 + 255 - 32 - 64 * spv) / (oh - 1)
*
- * The ratios are integer values, and must be rounded down to ensure that the
- * cropped input size is not bigger than the uncropped input size. As the ratio
- * in 7-tap mode is always smaller than the ratio in 4-tap mode, we can use the
- * 7-tap mode equations to compute a ratio approximation.
+ * The ratios are integer values, and are rounded down to ensure that the
+ * cropped input size is not bigger than the uncropped input size.
+ *
+ * As the number of phases/taps, used to select the correct equations to compute
+ * the ratio, depends on the ratio, we start with the 4-tap mode equations to
+ * compute an approximation of the ratio, and switch to the 7-tap mode equations
+ * if the approximation is higher than the ratio threshold.
+ *
+ * As the 7-tap mode equations will return a ratio smaller than or equal to the
+ * 4-tap mode equations, the resulting ratio could become lower than or equal to
+ * the ratio threshold. This 'equations loop' isn't an issue as long as the
+ * correct equations are used to compute the final input size. Starting with the
+ * 4-tap mode equations ensure that, in case of values resulting in a 'ratio
+ * loop', the smallest of the ratio values will be used, never exceeding the
+ * requested input size.
*
* We first clamp the output size according to the hardware capabilitie to avoid
* auto-cropping the input more than required to satisfy the TRM equations. The
@@ -775,6 +806,8 @@ static void resizer_calc_ratios(struct isp_res_device *res,
unsigned int max_width;
unsigned int max_height;
unsigned int width_alignment;
+ unsigned int width;
+ unsigned int height;
/*
* Clamp the output height based on the hardware capabilities and
@@ -786,19 +819,22 @@ static void resizer_calc_ratios(struct isp_res_device *res,
max_height = min_t(unsigned int, max_height, MAX_OUT_HEIGHT);
output->height = clamp(output->height, min_height, max_height);
- ratio->vert = ((input->height - 7) * 256 - 32 - 64 * spv)
+ ratio->vert = ((input->height - 4) * 256 + 255 - 16 - 32 * spv)
/ (output->height - 1);
+ if (ratio->vert > MID_RESIZE_VALUE)
+ ratio->vert = ((input->height - 7) * 256 + 255 - 32 - 64 * spv)
+ / (output->height - 1);
ratio->vert = clamp_t(unsigned int, ratio->vert,
MIN_RESIZE_VALUE, MAX_RESIZE_VALUE);
if (ratio->vert <= MID_RESIZE_VALUE) {
upscaled_height = (output->height - 1) * ratio->vert
+ 32 * spv + 16;
- input->height = (upscaled_height >> 8) + 4;
+ height = (upscaled_height >> 8) + 4;
} else {
upscaled_height = (output->height - 1) * ratio->vert
+ 64 * spv + 32;
- input->height = (upscaled_height >> 8) + 7;
+ height = (upscaled_height >> 8) + 7;
}
/*
@@ -854,20 +890,29 @@ static void resizer_calc_ratios(struct isp_res_device *res,
max_width & ~(width_alignment - 1));
output->width = ALIGN(output->width, width_alignment);
- ratio->horz = ((input->width - 7) * 256 - 32 - 64 * sph)
+ ratio->horz = ((input->width - 7) * 256 + 255 - 16 - 32 * sph)
/ (output->width - 1);
+ if (ratio->horz > MID_RESIZE_VALUE)
+ ratio->horz = ((input->width - 7) * 256 + 255 - 32 - 64 * sph)
+ / (output->width - 1);
ratio->horz = clamp_t(unsigned int, ratio->horz,
MIN_RESIZE_VALUE, MAX_RESIZE_VALUE);
if (ratio->horz <= MID_RESIZE_VALUE) {
upscaled_width = (output->width - 1) * ratio->horz
+ 32 * sph + 16;
- input->width = (upscaled_width >> 8) + 7;
+ width = (upscaled_width >> 8) + 7;
} else {
upscaled_width = (output->width - 1) * ratio->horz
+ 64 * sph + 32;
- input->width = (upscaled_width >> 8) + 7;
+ width = (upscaled_width >> 8) + 7;
}
+
+ /* Center the new crop rectangle. */
+ input->left += (input->width - width) / 2;
+ input->top += (input->height - height) / 2;
+ input->width = width;
+ input->height = height;
}
/*
diff --git a/drivers/media/video/omap3isp/ispstat.h b/drivers/media/video/omap3isp/ispstat.h
index 820950c9ef4..d86da94fa50 100644
--- a/drivers/media/video/omap3isp/ispstat.h
+++ b/drivers/media/video/omap3isp/ispstat.h
@@ -131,9 +131,9 @@ struct ispstat {
struct ispstat_generic_config {
/*
* Fields must be in the same order as in:
- * - isph3a_aewb_config
- * - isph3a_af_config
- * - isphist_config
+ * - omap3isp_h3a_aewb_config
+ * - omap3isp_h3a_af_config
+ * - omap3isp_hist_config
*/
u32 buf_size;
u16 config_counter;
diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c
index 208a7ec739d..9cd8f1aa567 100644
--- a/drivers/media/video/omap3isp/ispvideo.c
+++ b/drivers/media/video/omap3isp/ispvideo.c
@@ -47,29 +47,59 @@
static struct isp_format_info formats[] = {
{ V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
- V4L2_MBUS_FMT_Y8_1X8, V4L2_PIX_FMT_GREY, 8, },
+ V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_PIX_FMT_GREY, 8, },
+ { V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y10_1X10,
+ V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_PIX_FMT_Y10, 10, },
+ { V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y10_1X10,
+ V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_PIX_FMT_Y12, 12, },
+ { V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_PIX_FMT_SBGGR8, 8, },
+ { V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
+ V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
+ V4L2_PIX_FMT_SGBRG8, 8, },
+ { V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_PIX_FMT_SGRBG8, 8, },
+ { V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_PIX_FMT_SRGGB8, 8, },
{ V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
- V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10DPCM8, 8, },
+ V4L2_MBUS_FMT_SGRBG10_1X10, 0,
+ V4L2_PIX_FMT_SGRBG10DPCM8, 8, },
{ V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10,
- V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10, 10, },
+ V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_PIX_FMT_SBGGR10, 10, },
{ V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10,
- V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10, 10, },
+ V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG8_1X8,
+ V4L2_PIX_FMT_SGBRG10, 10, },
{ V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10,
- V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10, 10, },
+ V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_PIX_FMT_SGRBG10, 10, },
{ V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10,
- V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10, 10, },
+ V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_PIX_FMT_SRGGB10, 10, },
{ V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10,
- V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12, 12, },
+ V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_PIX_FMT_SBGGR12, 12, },
{ V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10,
- V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12, 12, },
+ V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG8_1X8,
+ V4L2_PIX_FMT_SGBRG12, 12, },
{ V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10,
- V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12, 12, },
+ V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_PIX_FMT_SGRBG12, 12, },
{ V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10,
- V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12, 12, },
+ V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_PIX_FMT_SRGGB12, 12, },
{ V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16,
- V4L2_MBUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 16, },
+ V4L2_MBUS_FMT_UYVY8_1X16, 0,
+ V4L2_PIX_FMT_UYVY, 16, },
{ V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16,
- V4L2_MBUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 16, },
+ V4L2_MBUS_FMT_YUYV8_1X16, 0,
+ V4L2_PIX_FMT_YUYV, 16, },
};
const struct isp_format_info *
@@ -86,6 +116,37 @@ omap3isp_video_format_info(enum v4l2_mbus_pixelcode code)
}
/*
+ * Decide whether desired output pixel code can be obtained with
+ * the lane shifter by shifting the input pixel code.
+ * @in: input pixelcode to shifter
+ * @out: output pixelcode from shifter
+ * @additional_shift: # of bits the sensor's LSB is offset from CAMEXT[0]
+ *
+ * return true if the combination is possible
+ * return false otherwise
+ */
+static bool isp_video_is_shiftable(enum v4l2_mbus_pixelcode in,
+ enum v4l2_mbus_pixelcode out,
+ unsigned int additional_shift)
+{
+ const struct isp_format_info *in_info, *out_info;
+
+ if (in == out)
+ return true;
+
+ in_info = omap3isp_video_format_info(in);
+ out_info = omap3isp_video_format_info(out);
+
+ if ((in_info->flavor == 0) || (out_info->flavor == 0))
+ return false;
+
+ if (in_info->flavor != out_info->flavor)
+ return false;
+
+ return in_info->bpp - out_info->bpp + additional_shift <= 6;
+}
+
+/*
* isp_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format
* @video: ISP video instance
* @mbus: v4l2_mbus_framefmt format (input)
@@ -235,6 +296,7 @@ static int isp_video_validate_pipeline(struct isp_pipeline *pipe)
return -EPIPE;
while (1) {
+ unsigned int shifter_link;
/* Retrieve the sink format */
pad = &subdev->entity.pads[0];
if (!(pad->flags & MEDIA_PAD_FL_SINK))
@@ -263,6 +325,10 @@ static int isp_video_validate_pipeline(struct isp_pipeline *pipe)
return -ENOSPC;
}
+ /* If sink pad is on CCDC, the link has the lane shifter
+ * in the middle of it. */
+ shifter_link = subdev == &isp->isp_ccdc.subdev;
+
/* Retrieve the source format */
pad = media_entity_remote_source(pad);
if (pad == NULL ||
@@ -278,10 +344,24 @@ static int isp_video_validate_pipeline(struct isp_pipeline *pipe)
return -EPIPE;
/* Check if the two ends match */
- if (fmt_source.format.code != fmt_sink.format.code ||
- fmt_source.format.width != fmt_sink.format.width ||
+ if (fmt_source.format.width != fmt_sink.format.width ||
fmt_source.format.height != fmt_sink.format.height)
return -EPIPE;
+
+ if (shifter_link) {
+ unsigned int parallel_shift = 0;
+ if (isp->isp_ccdc.input == CCDC_INPUT_PARALLEL) {
+ struct isp_parallel_platform_data *pdata =
+ &((struct isp_v4l2_subdevs_group *)
+ subdev->host_priv)->bus.parallel;
+ parallel_shift = pdata->data_lane_shift * 2;
+ }
+ if (!isp_video_is_shiftable(fmt_source.format.code,
+ fmt_sink.format.code,
+ parallel_shift))
+ return -EPIPE;
+ } else if (fmt_source.format.code != fmt_sink.format.code)
+ return -EPIPE;
}
return 0;
diff --git a/drivers/media/video/omap3isp/ispvideo.h b/drivers/media/video/omap3isp/ispvideo.h
index 524a1acd090..911bea64e78 100644
--- a/drivers/media/video/omap3isp/ispvideo.h
+++ b/drivers/media/video/omap3isp/ispvideo.h
@@ -49,6 +49,8 @@ struct v4l2_pix_format;
* bits. Identical to @code if the format is 10 bits wide or less.
* @uncompressed: V4L2 media bus format code for the corresponding uncompressed
* format. Identical to @code if the format is not DPCM compressed.
+ * @flavor: V4L2 media bus format code for the same pixel layout but
+ * shifted to be 8 bits per pixel. =0 if format is not shiftable.
* @pixelformat: V4L2 pixel format FCC identifier
* @bpp: Bits per pixel
*/
@@ -56,6 +58,7 @@ struct isp_format_info {
enum v4l2_mbus_pixelcode code;
enum v4l2_mbus_pixelcode truncated;
enum v4l2_mbus_pixelcode uncompressed;
+ enum v4l2_mbus_pixelcode flavor;
u32 pixelformat;
unsigned int bpp;
};
diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c
index 95f8b4e11e4..d142b40ea64 100644
--- a/drivers/media/video/s5p-fimc/fimc-capture.c
+++ b/drivers/media/video/s5p-fimc/fimc-capture.c
@@ -527,7 +527,7 @@ static int fimc_cap_s_fmt_mplane(struct file *file, void *priv,
if (ret)
return ret;
- if (vb2_is_streaming(&fimc->vid_cap.vbq) || fimc_capture_active(fimc))
+ if (vb2_is_busy(&fimc->vid_cap.vbq) || fimc_capture_active(fimc))
return -EBUSY;
frame = &ctx->d_frame;
@@ -539,8 +539,10 @@ static int fimc_cap_s_fmt_mplane(struct file *file, void *priv,
return -EINVAL;
}
- for (i = 0; i < frame->fmt->colplanes; i++)
- frame->payload[i] = pix->plane_fmt[i].bytesperline * pix->height;
+ for (i = 0; i < frame->fmt->colplanes; i++) {
+ frame->payload[i] =
+ (pix->width * pix->height * frame->fmt->depth[i]) >> 3;
+ }
/* Output DMA frame pixel size and offsets. */
frame->f_width = pix->plane_fmt[0].bytesperline * 8
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c
index 6c919b38a3d..dc91a8511af 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.c
+++ b/drivers/media/video/s5p-fimc/fimc-core.c
@@ -361,10 +361,20 @@ static void fimc_capture_irq_handler(struct fimc_dev *fimc)
{
struct fimc_vid_cap *cap = &fimc->vid_cap;
struct fimc_vid_buffer *v_buf;
+ struct timeval *tv;
+ struct timespec ts;
if (!list_empty(&cap->active_buf_q) &&
test_bit(ST_CAPT_RUN, &fimc->state)) {
+ ktime_get_real_ts(&ts);
+
v_buf = active_queue_pop(cap);
+
+ tv = &v_buf->vb.v4l2_buf.timestamp;
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+ v_buf->vb.v4l2_buf.sequence = cap->frame_count++;
+
vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE);
}
@@ -758,7 +768,7 @@ static void fimc_unlock(struct vb2_queue *vq)
mutex_unlock(&ctx->fimc_dev->lock);
}
-struct vb2_ops fimc_qops = {
+static struct vb2_ops fimc_qops = {
.queue_setup = fimc_queue_setup,
.buf_prepare = fimc_buf_prepare,
.buf_queue = fimc_buf_queue,
@@ -927,23 +937,23 @@ int fimc_vidioc_try_fmt_mplane(struct file *file, void *priv,
pix->num_planes = fmt->memplanes;
pix->colorspace = V4L2_COLORSPACE_JPEG;
- for (i = 0; i < pix->num_planes; ++i) {
- int bpl = pix->plane_fmt[i].bytesperline;
- dbg("[%d] bpl: %d, depth: %d, w: %d, h: %d",
- i, bpl, fmt->depth[i], pix->width, pix->height);
+ for (i = 0; i < pix->num_planes; ++i) {
+ u32 bpl = pix->plane_fmt[i].bytesperline;
+ u32 *sizeimage = &pix->plane_fmt[i].sizeimage;
- if (!bpl || (bpl * 8 / fmt->depth[i]) > pix->width)
- bpl = (pix->width * fmt->depth[0]) >> 3;
+ if (fmt->colplanes > 1 && (bpl == 0 || bpl < pix->width))
+ bpl = pix->width; /* Planar */
- if (!pix->plane_fmt[i].sizeimage)
- pix->plane_fmt[i].sizeimage = pix->height * bpl;
+ if (fmt->colplanes == 1 && /* Packed */
+ (bpl == 0 || ((bpl * 8) / fmt->depth[i]) < pix->width))
+ bpl = (pix->width * fmt->depth[0]) / 8;
- pix->plane_fmt[i].bytesperline = bpl;
+ if (i == 0) /* Same bytesperline for each plane. */
+ mod_x = bpl;
- dbg("[%d]: bpl: %d, sizeimage: %d",
- i, pix->plane_fmt[i].bytesperline,
- pix->plane_fmt[i].sizeimage);
+ pix->plane_fmt[i].bytesperline = mod_x;
+ *sizeimage = (pix->width * pix->height * fmt->depth[i]) / 8;
}
return 0;
@@ -965,7 +975,7 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *priv,
vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
- if (vb2_is_streaming(vq)) {
+ if (vb2_is_busy(vq)) {
v4l2_err(&fimc->m2m.v4l2_dev, "queue (%d) busy\n", f->type);
return -EBUSY;
}
@@ -985,8 +995,10 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *priv,
if (!frame->fmt)
return -EINVAL;
- for (i = 0; i < frame->fmt->colplanes; i++)
- frame->payload[i] = pix->plane_fmt[i].bytesperline * pix->height;
+ for (i = 0; i < frame->fmt->colplanes; i++) {
+ frame->payload[i] =
+ (pix->width * pix->height * frame->fmt->depth[i]) / 8;
+ }
frame->f_width = pix->plane_fmt[0].bytesperline * 8 /
frame->fmt->depth[0];
@@ -1750,7 +1762,7 @@ static int __devexit fimc_remove(struct platform_device *pdev)
}
/* Image pixel limits, similar across several FIMC HW revisions. */
-static struct fimc_pix_limit s5p_pix_limit[3] = {
+static struct fimc_pix_limit s5p_pix_limit[4] = {
[0] = {
.scaler_en_w = 3264,
.scaler_dis_w = 8192,
@@ -1775,6 +1787,14 @@ static struct fimc_pix_limit s5p_pix_limit[3] = {
.out_rot_en_w = 1280,
.out_rot_dis_w = 1920,
},
+ [3] = {
+ .scaler_en_w = 1920,
+ .scaler_dis_w = 8192,
+ .in_rot_en_h = 1366,
+ .in_rot_dis_w = 8192,
+ .out_rot_en_w = 1366,
+ .out_rot_dis_w = 1920,
+ },
};
static struct samsung_fimc_variant fimc0_variant_s5p = {
@@ -1827,7 +1847,7 @@ static struct samsung_fimc_variant fimc2_variant_s5pv210 = {
.pix_limit = &s5p_pix_limit[2],
};
-static struct samsung_fimc_variant fimc0_variant_s5pv310 = {
+static struct samsung_fimc_variant fimc0_variant_exynos4 = {
.pix_hoff = 1,
.has_inp_rot = 1,
.has_out_rot = 1,
@@ -1840,7 +1860,7 @@ static struct samsung_fimc_variant fimc0_variant_s5pv310 = {
.pix_limit = &s5p_pix_limit[1],
};
-static struct samsung_fimc_variant fimc2_variant_s5pv310 = {
+static struct samsung_fimc_variant fimc2_variant_exynos4 = {
.pix_hoff = 1,
.has_cistatus2 = 1,
.has_mainscaler_ext = 1,
@@ -1848,7 +1868,7 @@ static struct samsung_fimc_variant fimc2_variant_s5pv310 = {
.min_out_pixsize = 16,
.hor_offs_align = 1,
.out_buf_count = 32,
- .pix_limit = &s5p_pix_limit[2],
+ .pix_limit = &s5p_pix_limit[3],
};
/* S5PC100 */
@@ -1874,12 +1894,12 @@ static struct samsung_fimc_driverdata fimc_drvdata_s5pv210 = {
};
/* S5PV310, S5PC210 */
-static struct samsung_fimc_driverdata fimc_drvdata_s5pv310 = {
+static struct samsung_fimc_driverdata fimc_drvdata_exynos4 = {
.variant = {
- [0] = &fimc0_variant_s5pv310,
- [1] = &fimc0_variant_s5pv310,
- [2] = &fimc0_variant_s5pv310,
- [3] = &fimc2_variant_s5pv310,
+ [0] = &fimc0_variant_exynos4,
+ [1] = &fimc0_variant_exynos4,
+ [2] = &fimc0_variant_exynos4,
+ [3] = &fimc2_variant_exynos4,
},
.num_entities = 4,
.lclk_frequency = 166000000UL,
@@ -1893,8 +1913,8 @@ static struct platform_device_id fimc_driver_ids[] = {
.name = "s5pv210-fimc",
.driver_data = (unsigned long)&fimc_drvdata_s5pv210,
}, {
- .name = "s5pv310-fimc",
- .driver_data = (unsigned long)&fimc_drvdata_s5pv310,
+ .name = "exynos4-fimc",
+ .driver_data = (unsigned long)&fimc_drvdata_exynos4,
},
{},
};
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
index 3fe54bf4114..134e86bf6d9 100644
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -922,7 +922,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
/* Try 2560x1920, 1280x960, 640x480, 320x240 */
mf.width = 2560 >> shift;
mf.height = 1920 >> shift;
- ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video,
+ ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video,
s_mbus_fmt, &mf);
if (ret < 0)
return ret;
@@ -1224,7 +1224,7 @@ static int client_s_fmt(struct soc_camera_device *icd,
struct v4l2_cropcap cap;
int ret;
- ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video,
+ ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video,
s_mbus_fmt, mf);
if (ret < 0)
return ret;
@@ -1254,7 +1254,7 @@ static int client_s_fmt(struct soc_camera_device *icd,
tmp_h = min(2 * tmp_h, max_height);
mf->width = tmp_w;
mf->height = tmp_h;
- ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video,
+ ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video,
s_mbus_fmt, mf);
dev_geo(dev, "Camera scaled to %ux%u\n",
mf->width, mf->height);
@@ -1658,7 +1658,7 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
mf.code = xlate->code;
mf.colorspace = pix->colorspace;
- ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video, try_mbus_fmt, &mf);
+ ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video, try_mbus_fmt, &mf);
if (ret < 0)
return ret;
@@ -1682,7 +1682,7 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
*/
mf.width = 2560;
mf.height = 1920;
- ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video,
+ ret = v4l2_device_call_until_err(sd->v4l2_dev, (long)icd, video,
try_mbus_fmt, &mf);
if (ret < 0) {
/* Shouldn't actually happen... */
diff --git a/drivers/media/video/sh_mobile_csi2.c b/drivers/media/video/sh_mobile_csi2.c
index dd1b81b1442..98b87481fa9 100644
--- a/drivers/media/video/sh_mobile_csi2.c
+++ b/drivers/media/video/sh_mobile_csi2.c
@@ -38,6 +38,8 @@ struct sh_csi2 {
void __iomem *base;
struct platform_device *pdev;
struct sh_csi2_client_config *client;
+ unsigned long (*query_bus_param)(struct soc_camera_device *);
+ int (*set_bus_param)(struct soc_camera_device *, unsigned long);
};
static int sh_csi2_try_fmt(struct v4l2_subdev *sd,
@@ -208,6 +210,7 @@ static int sh_csi2_notify(struct notifier_block *nb,
case BUS_NOTIFY_BOUND_DRIVER:
snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s%s",
dev_name(v4l2_dev->dev), ".mipi-csi");
+ priv->subdev.grp_id = (long)icd;
ret = v4l2_device_register_subdev(v4l2_dev, &priv->subdev);
dev_dbg(dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret);
if (ret < 0)
@@ -215,6 +218,8 @@ static int sh_csi2_notify(struct notifier_block *nb,
priv->client = pdata->clients + i;
+ priv->set_bus_param = icd->ops->set_bus_param;
+ priv->query_bus_param = icd->ops->query_bus_param;
icd->ops->set_bus_param = sh_csi2_set_bus_param;
icd->ops->query_bus_param = sh_csi2_query_bus_param;
@@ -226,8 +231,10 @@ static int sh_csi2_notify(struct notifier_block *nb,
priv->client = NULL;
/* Driver is about to be unbound */
- icd->ops->set_bus_param = NULL;
- icd->ops->query_bus_param = NULL;
+ icd->ops->set_bus_param = priv->set_bus_param;
+ icd->ops->query_bus_param = priv->query_bus_param;
+ priv->set_bus_param = NULL;
+ priv->query_bus_param = NULL;
v4l2_device_unregister_subdev(&priv->subdev);
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index 46284489e4e..ddb4c091ded 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -136,11 +136,50 @@ unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl,
}
EXPORT_SYMBOL(soc_camera_apply_sensor_flags);
+#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \
+ ((x) >> 24) & 0xff
+
+static int soc_camera_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ int ret;
+
+ dev_dbg(&icd->dev, "TRY_FMT(%c%c%c%c, %ux%u)\n",
+ pixfmtstr(pix->pixelformat), pix->width, pix->height);
+
+ pix->bytesperline = 0;
+ pix->sizeimage = 0;
+
+ ret = ici->ops->try_fmt(icd, f);
+ if (ret < 0)
+ return ret;
+
+ if (!pix->sizeimage) {
+ if (!pix->bytesperline) {
+ const struct soc_camera_format_xlate *xlate;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate)
+ return -EINVAL;
+
+ ret = soc_mbus_bytes_per_line(pix->width,
+ xlate->host_fmt);
+ if (ret > 0)
+ pix->bytesperline = ret;
+ }
+ if (pix->bytesperline)
+ pix->sizeimage = pix->bytesperline * pix->height;
+ }
+
+ return 0;
+}
+
static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
WARN_ON(priv != file->private_data);
@@ -149,7 +188,7 @@ static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv,
return -EINVAL;
/* limit format to hardware capabilities */
- return ici->ops->try_fmt(icd, f);
+ return soc_camera_try_fmt(icd, f);
}
static int soc_camera_enum_input(struct file *file, void *priv,
@@ -362,9 +401,6 @@ static void soc_camera_free_user_formats(struct soc_camera_device *icd)
icd->user_formats = NULL;
}
-#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \
- ((x) >> 24) & 0xff
-
/* Called with .vb_lock held, or from the first open(2), see comment there */
static int soc_camera_set_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
@@ -377,7 +413,7 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd,
pixfmtstr(pix->pixelformat), pix->width, pix->height);
/* We always call try_fmt() before set_fmt() or set_crop() */
- ret = ici->ops->try_fmt(icd, f);
+ ret = soc_camera_try_fmt(icd, f);
if (ret < 0)
return ret;
@@ -996,10 +1032,11 @@ static void soc_camera_free_i2c(struct soc_camera_device *icd)
{
struct i2c_client *client =
to_i2c_client(to_soc_camera_control(icd));
+ struct i2c_adapter *adap = client->adapter;
dev_set_drvdata(&icd->dev, NULL);
v4l2_device_unregister_subdev(i2c_get_clientdata(client));
i2c_unregister_device(client);
- i2c_put_adapter(client->adapter);
+ i2c_put_adapter(adap);
}
#else
#define soc_camera_init_i2c(icd, icl) (-ENODEV)
@@ -1071,6 +1108,9 @@ static int soc_camera_probe(struct device *dev)
}
}
+ sd = soc_camera_to_subdev(icd);
+ sd->grp_id = (long)icd;
+
/* At this point client .probe() should have run already */
ret = soc_camera_init_user_formats(icd);
if (ret < 0)
@@ -1092,7 +1132,6 @@ static int soc_camera_probe(struct device *dev)
goto evidstart;
/* Try to improve our guess of a reasonable window format */
- sd = soc_camera_to_subdev(icd);
if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {
icd->user_width = mf.width;
icd->user_height = mf.height;
diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c
index 5d4cf3b3d43..22fa8202d5c 100644
--- a/drivers/media/video/tda9840.c
+++ b/drivers/media/video/tda9840.c
@@ -171,7 +171,7 @@ static int tda9840_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
if (sd == NULL)
return -ENOMEM;
v4l2_i2c_subdev_init(sd, client, &tda9840_ops);
diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c
index 19621ed523e..827425c5b86 100644
--- a/drivers/media/video/tea6415c.c
+++ b/drivers/media/video/tea6415c.c
@@ -152,7 +152,7 @@ static int tea6415c_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
if (sd == NULL)
return -ENOMEM;
v4l2_i2c_subdev_init(sd, client, &tea6415c_ops);
diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c
index 5ea840401f2..f350b6c2450 100644
--- a/drivers/media/video/tea6420.c
+++ b/drivers/media/video/tea6420.c
@@ -125,7 +125,7 @@ static int tea6420_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
if (sd == NULL)
return -ENOMEM;
v4l2_i2c_subdev_init(sd, client, &tea6420_ops);
diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c
index f8138c75be8..1aab96a8820 100644
--- a/drivers/media/video/upd64031a.c
+++ b/drivers/media/video/upd64031a.c
@@ -230,7 +230,7 @@ static int upd64031a_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kmalloc(sizeof(struct upd64031a_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct upd64031a_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c
index 28e0e6b6ca8..9bbe61700fd 100644
--- a/drivers/media/video/upd64083.c
+++ b/drivers/media/video/upd64083.c
@@ -202,7 +202,7 @@ static int upd64083_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kmalloc(sizeof(struct upd64083_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct upd64083_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
index 498e6742579..6dc7196296b 100644
--- a/drivers/media/video/v4l2-dev.c
+++ b/drivers/media/video/v4l2-dev.c
@@ -389,7 +389,8 @@ static int v4l2_open(struct inode *inode, struct file *filp)
video_get(vdev);
mutex_unlock(&videodev_lock);
#if defined(CONFIG_MEDIA_CONTROLLER)
- if (vdev->v4l2_dev && vdev->v4l2_dev->mdev) {
+ if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
+ vdev->vfl_type != VFL_TYPE_SUBDEV) {
entity = media_entity_get(&vdev->entity);
if (!entity) {
ret = -EBUSY;
@@ -415,7 +416,8 @@ err:
/* decrease the refcount in case of an error */
if (ret) {
#if defined(CONFIG_MEDIA_CONTROLLER)
- if (vdev->v4l2_dev && vdev->v4l2_dev->mdev)
+ if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
+ vdev->vfl_type != VFL_TYPE_SUBDEV)
media_entity_put(entity);
#endif
video_put(vdev);
@@ -437,7 +439,8 @@ static int v4l2_release(struct inode *inode, struct file *filp)
mutex_unlock(vdev->lock);
}
#if defined(CONFIG_MEDIA_CONTROLLER)
- if (vdev->v4l2_dev && vdev->v4l2_dev->mdev)
+ if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
+ vdev->vfl_type != VFL_TYPE_SUBDEV)
media_entity_put(&vdev->entity);
#endif
/* decrease the refcount unconditionally since the release()
@@ -686,7 +689,8 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
#if defined(CONFIG_MEDIA_CONTROLLER)
/* Part 5: Register the entity. */
- if (vdev->v4l2_dev && vdev->v4l2_dev->mdev) {
+ if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
+ vdev->vfl_type != VFL_TYPE_SUBDEV) {
vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
vdev->entity.name = vdev->name;
vdev->entity.v4l.major = VIDEO_MAJOR;
@@ -733,7 +737,8 @@ void video_unregister_device(struct video_device *vdev)
return;
#if defined(CONFIG_MEDIA_CONTROLLER)
- if (vdev->v4l2_dev && vdev->v4l2_dev->mdev)
+ if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
+ vdev->vfl_type != VFL_TYPE_SUBDEV)
media_device_unregister_entity(&vdev->entity);
#endif
diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c
index 5aeaf876ba9..4aae501f02d 100644
--- a/drivers/media/video/v4l2-device.c
+++ b/drivers/media/video/v4l2-device.c
@@ -155,8 +155,10 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
sd->v4l2_dev = v4l2_dev;
if (sd->internal_ops && sd->internal_ops->registered) {
err = sd->internal_ops->registered(sd);
- if (err)
+ if (err) {
+ module_put(sd->owner);
return err;
+ }
}
/* This just returns 0 if either of the two args is NULL */
@@ -164,6 +166,7 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
if (err) {
if (sd->internal_ops && sd->internal_ops->unregistered)
sd->internal_ops->unregistered(sd);
+ module_put(sd->owner);
return err;
}
diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c
index 0b806449067..812729ebf09 100644
--- a/drivers/media/video/v4l2-subdev.c
+++ b/drivers/media/video/v4l2-subdev.c
@@ -155,25 +155,25 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
switch (cmd) {
case VIDIOC_QUERYCTRL:
- return v4l2_subdev_queryctrl(sd, arg);
+ return v4l2_queryctrl(sd->ctrl_handler, arg);
case VIDIOC_QUERYMENU:
- return v4l2_subdev_querymenu(sd, arg);
+ return v4l2_querymenu(sd->ctrl_handler, arg);
case VIDIOC_G_CTRL:
- return v4l2_subdev_g_ctrl(sd, arg);
+ return v4l2_g_ctrl(sd->ctrl_handler, arg);
case VIDIOC_S_CTRL:
- return v4l2_subdev_s_ctrl(sd, arg);
+ return v4l2_s_ctrl(sd->ctrl_handler, arg);
case VIDIOC_G_EXT_CTRLS:
- return v4l2_subdev_g_ext_ctrls(sd, arg);
+ return v4l2_g_ext_ctrls(sd->ctrl_handler, arg);
case VIDIOC_S_EXT_CTRLS:
- return v4l2_subdev_s_ext_ctrls(sd, arg);
+ return v4l2_s_ext_ctrls(sd->ctrl_handler, arg);
case VIDIOC_TRY_EXT_CTRLS:
- return v4l2_subdev_try_ext_ctrls(sd, arg);
+ return v4l2_try_ext_ctrls(sd->ctrl_handler, arg);
case VIDIOC_DQEVENT:
if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c
index c4742fc1552..c9691115f2d 100644
--- a/drivers/media/video/videobuf-dma-contig.c
+++ b/drivers/media/video/videobuf-dma-contig.c
@@ -300,7 +300,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
retval = remap_pfn_range(vma, vma->vm_start,
- PFN_DOWN(virt_to_phys(mem->vaddr)),
+ mem->dma_handle >> PAGE_SHIFT,
size, vma->vm_page_prot);
if (retval) {
dev_err(q->dev, "mmap: remap failed with error %d. ", retval);
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
index 6698c77e0f6..6ba1461d51e 100644
--- a/drivers/media/video/videobuf2-core.c
+++ b/drivers/media/video/videobuf2-core.c
@@ -37,6 +37,9 @@ module_param(debug, int, 0644);
#define call_qop(q, op, args...) \
(((q)->ops->op) ? ((q)->ops->op(args)) : 0)
+#define V4L2_BUFFER_STATE_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \
+ V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR)
+
/**
* __vb2_buf_mem_alloc() - allocate video memory for the given buffer
*/
@@ -51,7 +54,7 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb,
for (plane = 0; plane < vb->num_planes; ++plane) {
mem_priv = call_memop(q, plane, alloc, q->alloc_ctx[plane],
plane_sizes[plane]);
- if (!mem_priv)
+ if (IS_ERR_OR_NULL(mem_priv))
goto free;
/* Associate allocator private data with this plane */
@@ -284,7 +287,7 @@ static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
struct vb2_queue *q = vb->vb2_queue;
int ret = 0;
- /* Copy back data such as timestamp, input, etc. */
+ /* Copy back data such as timestamp, flags, input, etc. */
memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
b->input = vb->v4l2_buf.input;
b->reserved = vb->v4l2_buf.reserved;
@@ -313,7 +316,10 @@ static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
b->m.userptr = vb->v4l2_planes[0].m.userptr;
}
- b->flags = 0;
+ /*
+ * Clear any buffer state related flags.
+ */
+ b->flags &= ~V4L2_BUFFER_STATE_FLAGS;
switch (vb->state) {
case VB2_BUF_STATE_QUEUED:
@@ -519,6 +525,7 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
memset(plane_sizes, 0, sizeof(plane_sizes));
memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
+ q->memory = req->memory;
/*
* Ask the driver how many buffers and planes per buffer it requires.
@@ -560,8 +567,6 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
ret = num_buffers;
}
- q->memory = req->memory;
-
/*
* Return the number of successfully allocated buffers
* to the userspace.
@@ -715,6 +720,8 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b,
vb->v4l2_buf.field = b->field;
vb->v4l2_buf.timestamp = b->timestamp;
+ vb->v4l2_buf.input = b->input;
+ vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS;
return 0;
}
diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c
index 58205d59613..a790a5f8c06 100644
--- a/drivers/media/video/videobuf2-dma-contig.c
+++ b/drivers/media/video/videobuf2-dma-contig.c
@@ -46,7 +46,7 @@ static void *vb2_dma_contig_alloc(void *alloc_ctx, unsigned long size)
GFP_KERNEL);
if (!buf->vaddr) {
dev_err(conf->dev, "dma_alloc_coherent of size %ld failed\n",
- buf->size);
+ size);
kfree(buf);
return ERR_PTR(-ENOMEM);
}
diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h
index 1735c84ff75..fe902338539 100644
--- a/drivers/message/fusion/mptbase.h
+++ b/drivers/message/fusion/mptbase.h
@@ -76,8 +76,8 @@
#define COPYRIGHT "Copyright (c) 1999-2008 " MODULEAUTHOR
#endif
-#define MPT_LINUX_VERSION_COMMON "3.04.18"
-#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.18"
+#define MPT_LINUX_VERSION_COMMON "3.04.19"
+#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.19"
#define WHAT_MAGIC_STRING "@" "(" "#" ")"
#define show_mptmod_ver(s,ver) \
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c
index 66f94125de4..7596aecd507 100644
--- a/drivers/message/fusion/mptsas.c
+++ b/drivers/message/fusion/mptsas.c
@@ -5012,7 +5012,6 @@ mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply)
(ioc_stat & MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE)) {
VirtTarget *vtarget = NULL;
u8 id, channel;
- u32 log_info = le32_to_cpu(reply->IOCLogInfo);
id = sas_event_data->TargetID;
channel = sas_event_data->Bus;
@@ -5023,7 +5022,8 @@ mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply)
"LogInfo (0x%x) available for "
"INTERNAL_DEVICE_RESET"
"fw_id %d fw_channel %d\n", ioc->name,
- log_info, id, channel));
+ le32_to_cpu(reply->IOCLogInfo),
+ id, channel));
if (vtarget->raidVolume) {
devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT
"Skipping Raid Volume for inDMD\n",
diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c
index 0d9b82a4454..a1d4ee6671b 100644
--- a/drivers/message/fusion/mptscsih.c
+++ b/drivers/message/fusion/mptscsih.c
@@ -1415,11 +1415,8 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
dmfprintk(ioc, printk(MYIOC_s_DEBUG_FMT "qcmd: SCpnt=%p, done()=%p\n",
ioc->name, SCpnt, done));
- if (ioc->taskmgmt_quiesce_io) {
- dtmprintk(ioc, printk(MYIOC_s_WARN_FMT "qcmd: SCpnt=%p timeout + 60HZ\n",
- ioc->name, SCpnt));
+ if (ioc->taskmgmt_quiesce_io)
return SCSI_MLQUEUE_HOST_BUSY;
- }
/*
* Put together a MPT SCSI request...
@@ -1773,7 +1770,6 @@ mptscsih_abort(struct scsi_cmnd * SCpnt)
int scpnt_idx;
int retval;
VirtDevice *vdevice;
- ulong sn = SCpnt->serial_number;
MPT_ADAPTER *ioc;
/* If we can't locate our host adapter structure, return FAILED status.
@@ -1859,8 +1855,7 @@ mptscsih_abort(struct scsi_cmnd * SCpnt)
vdevice->vtarget->id, vdevice->lun,
ctx2abort, mptscsih_get_tm_timeout(ioc));
- if (SCPNT_TO_LOOKUP_IDX(ioc, SCpnt) == scpnt_idx &&
- SCpnt->serial_number == sn) {
+ if (SCPNT_TO_LOOKUP_IDX(ioc, SCpnt) == scpnt_idx) {
dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
"task abort: command still in active list! (sc=%p)\n",
ioc->name, SCpnt));
@@ -1873,9 +1868,9 @@ mptscsih_abort(struct scsi_cmnd * SCpnt)
}
out:
- printk(MYIOC_s_INFO_FMT "task abort: %s (rv=%04x) (sc=%p) (sn=%ld)\n",
+ printk(MYIOC_s_INFO_FMT "task abort: %s (rv=%04x) (sc=%p)\n",
ioc->name, ((retval == SUCCESS) ? "SUCCESS" : "FAILED"), retval,
- SCpnt, SCpnt->serial_number);
+ SCpnt);
return retval;
}
diff --git a/drivers/message/fusion/mptspi.c b/drivers/message/fusion/mptspi.c
index 6d9568d2ec5..8f61ba6aac2 100644
--- a/drivers/message/fusion/mptspi.c
+++ b/drivers/message/fusion/mptspi.c
@@ -867,6 +867,10 @@ static int mptspi_write_spi_device_pg1(struct scsi_target *starget,
struct _x_config_parms cfg;
struct _CONFIG_PAGE_HEADER hdr;
int err = -EBUSY;
+ u32 nego_parms;
+ u32 period;
+ struct scsi_device *sdev;
+ int i;
/* don't allow updating nego parameters on RAID devices */
if (starget->channel == 0 &&
@@ -904,6 +908,24 @@ static int mptspi_write_spi_device_pg1(struct scsi_target *starget,
pg1->Header.PageNumber = hdr.PageNumber;
pg1->Header.PageType = hdr.PageType;
+ nego_parms = le32_to_cpu(pg1->RequestedParameters);
+ period = (nego_parms & MPI_SCSIDEVPAGE1_RP_MIN_SYNC_PERIOD_MASK) >>
+ MPI_SCSIDEVPAGE1_RP_SHIFT_MIN_SYNC_PERIOD;
+ if (period == 8) {
+ /* Turn on inline data padding for TAPE when running U320 */
+ for (i = 0 ; i < 16; i++) {
+ sdev = scsi_device_lookup_by_target(starget, i);
+ if (sdev && sdev->type == TYPE_TAPE) {
+ sdev_printk(KERN_DEBUG, sdev, MYIOC_s_FMT
+ "IDP:ON\n", ioc->name);
+ nego_parms |= MPI_SCSIDEVPAGE1_RP_IDP;
+ pg1->RequestedParameters =
+ cpu_to_le32(nego_parms);
+ break;
+ }
+ }
+ }
+
mptspi_print_write_nego(hd, starget, le32_to_cpu(pg1->RequestedParameters));
if (mpt_config(ioc, &cfg)) {
diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c
index 643ad52e3ca..4796bbf0ae4 100644
--- a/drivers/message/i2o/i2o_block.c
+++ b/drivers/message/i2o/i2o_block.c
@@ -1000,7 +1000,6 @@ static struct i2o_block_device *i2o_block_device_alloc(void)
gd->major = I2O_MAJOR;
gd->queue = queue;
gd->fops = &i2o_block_fops;
- gd->events = DISK_EVENT_MEDIA_CHANGE;
gd->private_data = dev;
dev->gd = gd;
diff --git a/drivers/message/i2o/i2o_scsi.c b/drivers/message/i2o/i2o_scsi.c
index f003957e8e1..74fbe56321f 100644
--- a/drivers/message/i2o/i2o_scsi.c
+++ b/drivers/message/i2o/i2o_scsi.c
@@ -361,7 +361,7 @@ static int i2o_scsi_reply(struct i2o_controller *c, u32 m,
*/
error = le32_to_cpu(msg->body[0]);
- osm_debug("Completed %ld\n", cmd->serial_number);
+ osm_debug("Completed %0x%p\n", cmd);
cmd->result = error & 0xff;
/*
@@ -678,7 +678,7 @@ static int i2o_scsi_queuecommand_lck(struct scsi_cmnd *SCpnt,
/* Queue the message */
i2o_msg_post(c, msg);
- osm_debug("Issued %ld\n", SCpnt->serial_number);
+ osm_debug("Issued %0x%p\n", SCpnt);
return 0;
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c
index d4a851c6b5b..0b4d5b23bec 100644
--- a/drivers/mfd/asic3.c
+++ b/drivers/mfd/asic3.c
@@ -144,7 +144,7 @@ static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc)
int iter, i;
unsigned long flags;
- data->chip->irq_ack(irq_data);
+ data->chip->irq_ack(data);
for (iter = 0 ; iter < MAX_ASIC_ISR_LOOPS; iter++) {
u32 status;
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index d01574d9887..f4c8c844b91 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -55,6 +55,19 @@ int mfd_cell_disable(struct platform_device *pdev)
}
EXPORT_SYMBOL(mfd_cell_disable);
+static int mfd_platform_add_cell(struct platform_device *pdev,
+ const struct mfd_cell *cell)
+{
+ if (!cell)
+ return 0;
+
+ pdev->mfd_cell = kmemdup(cell, sizeof(*cell), GFP_KERNEL);
+ if (!pdev->mfd_cell)
+ return -ENOMEM;
+
+ return 0;
+}
+
static int mfd_add_device(struct device *parent, int id,
const struct mfd_cell *cell,
struct resource *mem_base,
@@ -75,7 +88,7 @@ static int mfd_add_device(struct device *parent, int id,
pdev->dev.parent = parent;
- ret = platform_device_add_data(pdev, cell, sizeof(*cell));
+ ret = mfd_platform_add_cell(pdev, cell);
if (ret)
goto fail_res;
@@ -123,7 +136,6 @@ static int mfd_add_device(struct device *parent, int id,
return 0;
-/* platform_device_del(pdev); */
fail_res:
kfree(res);
fail_device:
diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
index 53450f433f1..3ab9ffa00aa 100644
--- a/drivers/mfd/omap-usb-host.c
+++ b/drivers/mfd/omap-usb-host.c
@@ -25,7 +25,6 @@
#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
#include <linux/gpio.h>
-#include <linux/regulator/consumer.h>
#include <plat/usb.h>
#define USBHS_DRIVER_NAME "usbhs-omap"
@@ -700,8 +699,7 @@ static int usbhs_enable(struct device *dev)
dev_dbg(dev, "starting TI HSUSB Controller\n");
if (!pdata) {
dev_dbg(dev, "missing platform_data\n");
- ret = -ENODEV;
- goto end_enable;
+ return -ENODEV;
}
spin_lock_irqsave(&omap->lock, flags);
@@ -719,14 +717,14 @@ static int usbhs_enable(struct device *dev)
gpio_request(pdata->ehci_data->reset_gpio_port[0],
"USB1 PHY reset");
gpio_direction_output
- (pdata->ehci_data->reset_gpio_port[0], 1);
+ (pdata->ehci_data->reset_gpio_port[0], 0);
}
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) {
gpio_request(pdata->ehci_data->reset_gpio_port[1],
"USB2 PHY reset");
gpio_direction_output
- (pdata->ehci_data->reset_gpio_port[1], 1);
+ (pdata->ehci_data->reset_gpio_port[1], 0);
}
/* Hold the PHY in RESET for enough time till DIR is high */
@@ -906,16 +904,17 @@ static int usbhs_enable(struct device *dev)
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
gpio_set_value
- (pdata->ehci_data->reset_gpio_port[0], 0);
+ (pdata->ehci_data->reset_gpio_port[0], 1);
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
gpio_set_value
- (pdata->ehci_data->reset_gpio_port[1], 0);
+ (pdata->ehci_data->reset_gpio_port[1], 1);
}
end_count:
omap->count++;
- goto end_enable;
+ spin_unlock_irqrestore(&omap->lock, flags);
+ return 0;
err_tll:
if (pdata->ehci_data->phy_reset) {
@@ -931,8 +930,6 @@ err_tll:
clk_disable(omap->usbhost_fs_fck);
clk_disable(omap->usbhost_hs_fck);
clk_disable(omap->usbhost_ick);
-
-end_enable:
spin_unlock_irqrestore(&omap->lock, flags);
return ret;
}
diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c
index 16422de0823..2c0d4d16491 100644
--- a/drivers/mfd/twl4030-power.c
+++ b/drivers/mfd/twl4030-power.c
@@ -447,12 +447,13 @@ static int __init load_twl4030_script(struct twl4030_script *tscript,
if (err)
goto out;
}
- if (tscript->flags & TWL4030_SLEEP_SCRIPT)
+ if (tscript->flags & TWL4030_SLEEP_SCRIPT) {
if (order)
pr_warning("TWL4030: Bad order of scripts (sleep "\
"script before wakeup) Leads to boot"\
"failure on some boards\n");
err = twl4030_config_sleep_sequence(address);
+ }
out:
return err;
}
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 4e007c6a4b4..d80dcdee88f 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -481,5 +481,6 @@ source "drivers/misc/cb710/Kconfig"
source "drivers/misc/iwmc3200top/Kconfig"
source "drivers/misc/ti-st/Kconfig"
source "drivers/misc/lis3lv02d/Kconfig"
+source "drivers/misc/carma/Kconfig"
endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index f5468602961..848e8464faa 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -44,3 +44,4 @@ obj-$(CONFIG_PCH_PHUB) += pch_phub.o
obj-y += ti-st/
obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o
obj-y += lis3lv02d/
+obj-y += carma/
diff --git a/drivers/misc/carma/Kconfig b/drivers/misc/carma/Kconfig
new file mode 100644
index 00000000000..c90370ed712
--- /dev/null
+++ b/drivers/misc/carma/Kconfig
@@ -0,0 +1,17 @@
+config CARMA_FPGA
+ tristate "CARMA DATA-FPGA Access Driver"
+ depends on FSL_SOC && PPC_83xx && MEDIA_SUPPORT && HAS_DMA && FSL_DMA
+ select VIDEOBUF_DMA_SG
+ default n
+ help
+ Say Y here to include support for communicating with the data
+ processing FPGAs on the OVRO CARMA board.
+
+config CARMA_FPGA_PROGRAM
+ tristate "CARMA DATA-FPGA Programmer"
+ depends on FSL_SOC && PPC_83xx && MEDIA_SUPPORT && HAS_DMA && FSL_DMA
+ select VIDEOBUF_DMA_SG
+ default n
+ help
+ Say Y here to include support for programming the data processing
+ FPGAs on the OVRO CARMA board.
diff --git a/drivers/misc/carma/Makefile b/drivers/misc/carma/Makefile
new file mode 100644
index 00000000000..ff36ac2ce53
--- /dev/null
+++ b/drivers/misc/carma/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_CARMA_FPGA) += carma-fpga.o
+obj-$(CONFIG_CARMA_FPGA_PROGRAM) += carma-fpga-program.o
diff --git a/drivers/misc/carma/carma-fpga-program.c b/drivers/misc/carma/carma-fpga-program.c
new file mode 100644
index 00000000000..7ce6065dc20
--- /dev/null
+++ b/drivers/misc/carma/carma-fpga-program.c
@@ -0,0 +1,1141 @@
+/*
+ * CARMA Board DATA-FPGA Programmer
+ *
+ * Copyright (c) 2009-2011 Ira W. Snyder <iws@ovro.caltech.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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/of_platform.h>
+#include <linux/completion.h>
+#include <linux/miscdevice.h>
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/highmem.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/slab.h>
+#include <linux/kref.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+
+#include <media/videobuf-dma-sg.h>
+
+/* MPC8349EMDS specific get_immrbase() */
+#include <sysdev/fsl_soc.h>
+
+static const char drv_name[] = "carma-fpga-program";
+
+/*
+ * Firmware images are always this exact size
+ *
+ * 12849552 bytes for a CARMA Digitizer Board (EP2S90 FPGAs)
+ * 18662880 bytes for a CARMA Correlator Board (EP2S130 FPGAs)
+ */
+#define FW_SIZE_EP2S90 12849552
+#define FW_SIZE_EP2S130 18662880
+
+struct fpga_dev {
+ struct miscdevice miscdev;
+
+ /* Reference count */
+ struct kref ref;
+
+ /* Device Registers */
+ struct device *dev;
+ void __iomem *regs;
+ void __iomem *immr;
+
+ /* Freescale DMA Device */
+ struct dma_chan *chan;
+
+ /* Interrupts */
+ int irq, status;
+ struct completion completion;
+
+ /* FPGA Bitfile */
+ struct mutex lock;
+
+ struct videobuf_dmabuf vb;
+ bool vb_allocated;
+
+ /* max size and written bytes */
+ size_t fw_size;
+ size_t bytes;
+};
+
+/*
+ * FPGA Bitfile Helpers
+ */
+
+/**
+ * fpga_drop_firmware_data() - drop the bitfile image from memory
+ * @priv: the driver's private data structure
+ *
+ * LOCKING: must hold priv->lock
+ */
+static void fpga_drop_firmware_data(struct fpga_dev *priv)
+{
+ videobuf_dma_free(&priv->vb);
+ priv->vb_allocated = false;
+ priv->bytes = 0;
+}
+
+/*
+ * Private Data Reference Count
+ */
+
+static void fpga_dev_remove(struct kref *ref)
+{
+ struct fpga_dev *priv = container_of(ref, struct fpga_dev, ref);
+
+ /* free any firmware image that was not programmed */
+ fpga_drop_firmware_data(priv);
+
+ mutex_destroy(&priv->lock);
+ kfree(priv);
+}
+
+/*
+ * LED Trigger (could be a seperate module)
+ */
+
+/*
+ * NOTE: this whole thing does have the problem that whenever the led's are
+ * NOTE: first set to use the fpga trigger, they could be in the wrong state
+ */
+
+DEFINE_LED_TRIGGER(ledtrig_fpga);
+
+static void ledtrig_fpga_programmed(bool enabled)
+{
+ if (enabled)
+ led_trigger_event(ledtrig_fpga, LED_FULL);
+ else
+ led_trigger_event(ledtrig_fpga, LED_OFF);
+}
+
+/*
+ * FPGA Register Helpers
+ */
+
+/* Register Definitions */
+#define FPGA_CONFIG_CONTROL 0x40
+#define FPGA_CONFIG_STATUS 0x44
+#define FPGA_CONFIG_FIFO_SIZE 0x48
+#define FPGA_CONFIG_FIFO_USED 0x4C
+#define FPGA_CONFIG_TOTAL_BYTE_COUNT 0x50
+#define FPGA_CONFIG_CUR_BYTE_COUNT 0x54
+
+#define FPGA_FIFO_ADDRESS 0x3000
+
+static int fpga_fifo_size(void __iomem *regs)
+{
+ return ioread32be(regs + FPGA_CONFIG_FIFO_SIZE);
+}
+
+#define CFG_STATUS_ERR_MASK 0xfffe
+
+static int fpga_config_error(void __iomem *regs)
+{
+ return ioread32be(regs + FPGA_CONFIG_STATUS) & CFG_STATUS_ERR_MASK;
+}
+
+static int fpga_fifo_empty(void __iomem *regs)
+{
+ return ioread32be(regs + FPGA_CONFIG_FIFO_USED) == 0;
+}
+
+static void fpga_fifo_write(void __iomem *regs, u32 val)
+{
+ iowrite32be(val, regs + FPGA_FIFO_ADDRESS);
+}
+
+static void fpga_set_byte_count(void __iomem *regs, u32 count)
+{
+ iowrite32be(count, regs + FPGA_CONFIG_TOTAL_BYTE_COUNT);
+}
+
+#define CFG_CTL_ENABLE (1 << 0)
+#define CFG_CTL_RESET (1 << 1)
+#define CFG_CTL_DMA (1 << 2)
+
+static void fpga_programmer_enable(struct fpga_dev *priv, bool dma)
+{
+ u32 val;
+
+ val = (dma) ? (CFG_CTL_ENABLE | CFG_CTL_DMA) : CFG_CTL_ENABLE;
+ iowrite32be(val, priv->regs + FPGA_CONFIG_CONTROL);
+}
+
+static void fpga_programmer_disable(struct fpga_dev *priv)
+{
+ iowrite32be(0x0, priv->regs + FPGA_CONFIG_CONTROL);
+}
+
+static void fpga_dump_registers(struct fpga_dev *priv)
+{
+ u32 control, status, size, used, total, curr;
+
+ /* good status: do nothing */
+ if (priv->status == 0)
+ return;
+
+ /* Dump all status registers */
+ control = ioread32be(priv->regs + FPGA_CONFIG_CONTROL);
+ status = ioread32be(priv->regs + FPGA_CONFIG_STATUS);
+ size = ioread32be(priv->regs + FPGA_CONFIG_FIFO_SIZE);
+ used = ioread32be(priv->regs + FPGA_CONFIG_FIFO_USED);
+ total = ioread32be(priv->regs + FPGA_CONFIG_TOTAL_BYTE_COUNT);
+ curr = ioread32be(priv->regs + FPGA_CONFIG_CUR_BYTE_COUNT);
+
+ dev_err(priv->dev, "Configuration failed, dumping status registers\n");
+ dev_err(priv->dev, "Control: 0x%.8x\n", control);
+ dev_err(priv->dev, "Status: 0x%.8x\n", status);
+ dev_err(priv->dev, "FIFO Size: 0x%.8x\n", size);
+ dev_err(priv->dev, "FIFO Used: 0x%.8x\n", used);
+ dev_err(priv->dev, "FIFO Total: 0x%.8x\n", total);
+ dev_err(priv->dev, "FIFO Curr: 0x%.8x\n", curr);
+}
+
+/*
+ * FPGA Power Supply Code
+ */
+
+#define CTL_PWR_CONTROL 0x2006
+#define CTL_PWR_STATUS 0x200A
+#define CTL_PWR_FAIL 0x200B
+
+#define PWR_CONTROL_ENABLE 0x01
+
+#define PWR_STATUS_ERROR_MASK 0x10
+#define PWR_STATUS_GOOD 0x0f
+
+/*
+ * Determine if the FPGA power is good for all supplies
+ */
+static bool fpga_power_good(struct fpga_dev *priv)
+{
+ u8 val;
+
+ val = ioread8(priv->regs + CTL_PWR_STATUS);
+ if (val & PWR_STATUS_ERROR_MASK)
+ return false;
+
+ return val == PWR_STATUS_GOOD;
+}
+
+/*
+ * Disable the FPGA power supplies
+ */
+static void fpga_disable_power_supplies(struct fpga_dev *priv)
+{
+ unsigned long start;
+ u8 val;
+
+ iowrite8(0x0, priv->regs + CTL_PWR_CONTROL);
+
+ /*
+ * Wait 500ms for the power rails to discharge
+ *
+ * Without this delay, the CTL-CPLD state machine can get into a
+ * state where it is waiting for the power-goods to assert, but they
+ * never do. This only happens when enabling and disabling the
+ * power sequencer very rapidly.
+ *
+ * The loop below will also wait for the power goods to de-assert,
+ * but testing has shown that they are always disabled by the time
+ * the sleep completes. However, omitting the sleep and only waiting
+ * for the power-goods to de-assert was not sufficient to ensure
+ * that the power sequencer would not wedge itself.
+ */
+ msleep(500);
+
+ start = jiffies;
+ while (time_before(jiffies, start + HZ)) {
+ val = ioread8(priv->regs + CTL_PWR_STATUS);
+ if (!(val & PWR_STATUS_GOOD))
+ break;
+
+ usleep_range(5000, 10000);
+ }
+
+ val = ioread8(priv->regs + CTL_PWR_STATUS);
+ if (val & PWR_STATUS_GOOD) {
+ dev_err(priv->dev, "power disable failed: "
+ "power goods: status 0x%.2x\n", val);
+ }
+
+ if (val & PWR_STATUS_ERROR_MASK) {
+ dev_err(priv->dev, "power disable failed: "
+ "alarm bit set: status 0x%.2x\n", val);
+ }
+}
+
+/**
+ * fpga_enable_power_supplies() - enable the DATA-FPGA power supplies
+ * @priv: the driver's private data structure
+ *
+ * Enable the DATA-FPGA power supplies, waiting up to 1 second for
+ * them to enable successfully.
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static int fpga_enable_power_supplies(struct fpga_dev *priv)
+{
+ unsigned long start = jiffies;
+
+ if (fpga_power_good(priv)) {
+ dev_dbg(priv->dev, "power was already good\n");
+ return 0;
+ }
+
+ iowrite8(PWR_CONTROL_ENABLE, priv->regs + CTL_PWR_CONTROL);
+ while (time_before(jiffies, start + HZ)) {
+ if (fpga_power_good(priv))
+ return 0;
+
+ usleep_range(5000, 10000);
+ }
+
+ return fpga_power_good(priv) ? 0 : -ETIMEDOUT;
+}
+
+/*
+ * Determine if the FPGA power supplies are all enabled
+ */
+static bool fpga_power_enabled(struct fpga_dev *priv)
+{
+ u8 val;
+
+ val = ioread8(priv->regs + CTL_PWR_CONTROL);
+ if (val & PWR_CONTROL_ENABLE)
+ return true;
+
+ return false;
+}
+
+/*
+ * Determine if the FPGA's are programmed and running correctly
+ */
+static bool fpga_running(struct fpga_dev *priv)
+{
+ if (!fpga_power_good(priv))
+ return false;
+
+ /* Check the config done bit */
+ return ioread32be(priv->regs + FPGA_CONFIG_STATUS) & (1 << 18);
+}
+
+/*
+ * FPGA Programming Code
+ */
+
+/**
+ * fpga_program_block() - put a block of data into the programmer's FIFO
+ * @priv: the driver's private data structure
+ * @buf: the data to program
+ * @count: the length of data to program (must be a multiple of 4 bytes)
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static int fpga_program_block(struct fpga_dev *priv, void *buf, size_t count)
+{
+ u32 *data = buf;
+ int size = fpga_fifo_size(priv->regs);
+ int i, len;
+ unsigned long timeout;
+
+ /* enforce correct data length for the FIFO */
+ BUG_ON(count % 4 != 0);
+
+ while (count > 0) {
+
+ /* Get the size of the block to write (maximum is FIFO_SIZE) */
+ len = min_t(size_t, count, size);
+ timeout = jiffies + HZ / 4;
+
+ /* Write the block */
+ for (i = 0; i < len / 4; i++)
+ fpga_fifo_write(priv->regs, data[i]);
+
+ /* Update the amounts left */
+ count -= len;
+ data += len / 4;
+
+ /* Wait for the fifo to empty */
+ while (true) {
+
+ if (fpga_fifo_empty(priv->regs)) {
+ break;
+ } else {
+ dev_dbg(priv->dev, "Fifo not empty\n");
+ cpu_relax();
+ }
+
+ if (fpga_config_error(priv->regs)) {
+ dev_err(priv->dev, "Error detected\n");
+ return -EIO;
+ }
+
+ if (time_after(jiffies, timeout)) {
+ dev_err(priv->dev, "Fifo drain timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ usleep_range(5000, 10000);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * fpga_program_cpu() - program the DATA-FPGA's using the CPU
+ * @priv: the driver's private data structure
+ *
+ * This is useful when the DMA programming method fails. It is possible to
+ * wedge the Freescale DMA controller such that the DMA programming method
+ * always fails. This method has always succeeded.
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static noinline int fpga_program_cpu(struct fpga_dev *priv)
+{
+ int ret;
+
+ /* Disable the programmer */
+ fpga_programmer_disable(priv);
+
+ /* Set the total byte count */
+ fpga_set_byte_count(priv->regs, priv->bytes);
+ dev_dbg(priv->dev, "total byte count %u bytes\n", priv->bytes);
+
+ /* Enable the controller for programming */
+ fpga_programmer_enable(priv, false);
+ dev_dbg(priv->dev, "enabled the controller\n");
+
+ /* Write each chunk of the FPGA bitfile to FPGA programmer */
+ ret = fpga_program_block(priv, priv->vb.vaddr, priv->bytes);
+ if (ret)
+ goto out_disable_controller;
+
+ /* Wait for the interrupt handler to signal that programming finished */
+ ret = wait_for_completion_timeout(&priv->completion, 2 * HZ);
+ if (!ret) {
+ dev_err(priv->dev, "Timed out waiting for completion\n");
+ ret = -ETIMEDOUT;
+ goto out_disable_controller;
+ }
+
+ /* Retrieve the status from the interrupt handler */
+ ret = priv->status;
+
+out_disable_controller:
+ fpga_programmer_disable(priv);
+ return ret;
+}
+
+#define FIFO_DMA_ADDRESS 0xf0003000
+#define FIFO_MAX_LEN 4096
+
+/**
+ * fpga_program_dma() - program the DATA-FPGA's using the DMA engine
+ * @priv: the driver's private data structure
+ *
+ * Program the DATA-FPGA's using the Freescale DMA engine. This requires that
+ * the engine is programmed such that the hardware DMA request lines can
+ * control the entire DMA transaction. The system controller FPGA then
+ * completely offloads the programming from the CPU.
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static noinline int fpga_program_dma(struct fpga_dev *priv)
+{
+ struct videobuf_dmabuf *vb = &priv->vb;
+ struct dma_chan *chan = priv->chan;
+ struct dma_async_tx_descriptor *tx;
+ size_t num_pages, len, avail = 0;
+ struct dma_slave_config config;
+ struct scatterlist *sg;
+ struct sg_table table;
+ dma_cookie_t cookie;
+ int ret, i;
+
+ /* Disable the programmer */
+ fpga_programmer_disable(priv);
+
+ /* Allocate a scatterlist for the DMA destination */
+ num_pages = DIV_ROUND_UP(priv->bytes, FIFO_MAX_LEN);
+ ret = sg_alloc_table(&table, num_pages, GFP_KERNEL);
+ if (ret) {
+ dev_err(priv->dev, "Unable to allocate dst scatterlist\n");
+ ret = -ENOMEM;
+ goto out_return;
+ }
+
+ /*
+ * This is an ugly hack
+ *
+ * We fill in a scatterlist as if it were mapped for DMA. This is
+ * necessary because there exists no better structure for this
+ * inside the kernel code.
+ *
+ * As an added bonus, we can use the DMAEngine API for all of this,
+ * rather than inventing another extremely similar API.
+ */
+ avail = priv->bytes;
+ for_each_sg(table.sgl, sg, num_pages, i) {
+ len = min_t(size_t, avail, FIFO_MAX_LEN);
+ sg_dma_address(sg) = FIFO_DMA_ADDRESS;
+ sg_dma_len(sg) = len;
+
+ avail -= len;
+ }
+
+ /* Map the buffer for DMA */
+ ret = videobuf_dma_map(priv->dev, &priv->vb);
+ if (ret) {
+ dev_err(priv->dev, "Unable to map buffer for DMA\n");
+ goto out_free_table;
+ }
+
+ /*
+ * Configure the DMA channel to transfer FIFO_SIZE / 2 bytes per
+ * transaction, and then put it under external control
+ */
+ memset(&config, 0, sizeof(config));
+ config.direction = DMA_TO_DEVICE;
+ config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ config.dst_maxburst = fpga_fifo_size(priv->regs) / 2 / 4;
+ ret = chan->device->device_control(chan, DMA_SLAVE_CONFIG,
+ (unsigned long)&config);
+ if (ret) {
+ dev_err(priv->dev, "DMA slave configuration failed\n");
+ goto out_dma_unmap;
+ }
+
+ ret = chan->device->device_control(chan, FSLDMA_EXTERNAL_START, 1);
+ if (ret) {
+ dev_err(priv->dev, "DMA external control setup failed\n");
+ goto out_dma_unmap;
+ }
+
+ /* setup and submit the DMA transaction */
+ tx = chan->device->device_prep_dma_sg(chan,
+ table.sgl, num_pages,
+ vb->sglist, vb->sglen, 0);
+ if (!tx) {
+ dev_err(priv->dev, "Unable to prep DMA transaction\n");
+ ret = -ENOMEM;
+ goto out_dma_unmap;
+ }
+
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(priv->dev, "Unable to submit DMA transaction\n");
+ ret = -ENOMEM;
+ goto out_dma_unmap;
+ }
+
+ dma_async_memcpy_issue_pending(chan);
+
+ /* Set the total byte count */
+ fpga_set_byte_count(priv->regs, priv->bytes);
+ dev_dbg(priv->dev, "total byte count %u bytes\n", priv->bytes);
+
+ /* Enable the controller for DMA programming */
+ fpga_programmer_enable(priv, true);
+ dev_dbg(priv->dev, "enabled the controller\n");
+
+ /* Wait for the interrupt handler to signal that programming finished */
+ ret = wait_for_completion_timeout(&priv->completion, 2 * HZ);
+ if (!ret) {
+ dev_err(priv->dev, "Timed out waiting for completion\n");
+ ret = -ETIMEDOUT;
+ goto out_disable_controller;
+ }
+
+ /* Retrieve the status from the interrupt handler */
+ ret = priv->status;
+
+out_disable_controller:
+ fpga_programmer_disable(priv);
+out_dma_unmap:
+ videobuf_dma_unmap(priv->dev, vb);
+out_free_table:
+ sg_free_table(&table);
+out_return:
+ return ret;
+}
+
+/*
+ * Interrupt Handling
+ */
+
+static irqreturn_t fpga_irq(int irq, void *dev_id)
+{
+ struct fpga_dev *priv = dev_id;
+
+ /* Save the status */
+ priv->status = fpga_config_error(priv->regs) ? -EIO : 0;
+ dev_dbg(priv->dev, "INTERRUPT status %d\n", priv->status);
+ fpga_dump_registers(priv);
+
+ /* Disabling the programmer clears the interrupt */
+ fpga_programmer_disable(priv);
+
+ /* Notify any waiters */
+ complete(&priv->completion);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * SYSFS Helpers
+ */
+
+/**
+ * fpga_do_stop() - deconfigure (reset) the DATA-FPGA's
+ * @priv: the driver's private data structure
+ *
+ * LOCKING: must hold priv->lock
+ */
+static int fpga_do_stop(struct fpga_dev *priv)
+{
+ u32 val;
+
+ /* Set the led to unprogrammed */
+ ledtrig_fpga_programmed(false);
+
+ /* Pulse the config line to reset the FPGA's */
+ val = CFG_CTL_ENABLE | CFG_CTL_RESET;
+ iowrite32be(val, priv->regs + FPGA_CONFIG_CONTROL);
+ iowrite32be(0x0, priv->regs + FPGA_CONFIG_CONTROL);
+
+ return 0;
+}
+
+static noinline int fpga_do_program(struct fpga_dev *priv)
+{
+ int ret;
+
+ if (priv->bytes != priv->fw_size) {
+ dev_err(priv->dev, "Incorrect bitfile size: got %zu bytes, "
+ "should be %zu bytes\n",
+ priv->bytes, priv->fw_size);
+ return -EINVAL;
+ }
+
+ if (!fpga_power_enabled(priv)) {
+ dev_err(priv->dev, "Power not enabled\n");
+ return -EINVAL;
+ }
+
+ if (!fpga_power_good(priv)) {
+ dev_err(priv->dev, "Power not good\n");
+ return -EINVAL;
+ }
+
+ /* Set the LED to unprogrammed */
+ ledtrig_fpga_programmed(false);
+
+ /* Try to program the FPGA's using DMA */
+ ret = fpga_program_dma(priv);
+
+ /* If DMA failed or doesn't exist, try with CPU */
+ if (ret) {
+ dev_warn(priv->dev, "Falling back to CPU programming\n");
+ ret = fpga_program_cpu(priv);
+ }
+
+ if (ret) {
+ dev_err(priv->dev, "Unable to program FPGA's\n");
+ return ret;
+ }
+
+ /* Drop the firmware bitfile from memory */
+ fpga_drop_firmware_data(priv);
+
+ dev_dbg(priv->dev, "FPGA programming successful\n");
+ ledtrig_fpga_programmed(true);
+
+ return 0;
+}
+
+/*
+ * File Operations
+ */
+
+static int fpga_open(struct inode *inode, struct file *filp)
+{
+ /*
+ * The miscdevice layer puts our struct miscdevice into the
+ * filp->private_data field. We use this to find our private
+ * data and then overwrite it with our own private structure.
+ */
+ struct fpga_dev *priv = container_of(filp->private_data,
+ struct fpga_dev, miscdev);
+ unsigned int nr_pages;
+ int ret;
+
+ /* We only allow one process at a time */
+ ret = mutex_lock_interruptible(&priv->lock);
+ if (ret)
+ return ret;
+
+ filp->private_data = priv;
+ kref_get(&priv->ref);
+
+ /* Truncation: drop any existing data */
+ if (filp->f_flags & O_TRUNC)
+ priv->bytes = 0;
+
+ /* Check if we have already allocated a buffer */
+ if (priv->vb_allocated)
+ return 0;
+
+ /* Allocate a buffer to hold enough data for the bitfile */
+ nr_pages = DIV_ROUND_UP(priv->fw_size, PAGE_SIZE);
+ ret = videobuf_dma_init_kernel(&priv->vb, DMA_TO_DEVICE, nr_pages);
+ if (ret) {
+ dev_err(priv->dev, "unable to allocate data buffer\n");
+ mutex_unlock(&priv->lock);
+ kref_put(&priv->ref, fpga_dev_remove);
+ return ret;
+ }
+
+ priv->vb_allocated = true;
+ return 0;
+}
+
+static int fpga_release(struct inode *inode, struct file *filp)
+{
+ struct fpga_dev *priv = filp->private_data;
+
+ mutex_unlock(&priv->lock);
+ kref_put(&priv->ref, fpga_dev_remove);
+ return 0;
+}
+
+static ssize_t fpga_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct fpga_dev *priv = filp->private_data;
+
+ /* FPGA bitfiles have an exact size: disallow anything else */
+ if (priv->bytes >= priv->fw_size)
+ return -ENOSPC;
+
+ count = min_t(size_t, priv->fw_size - priv->bytes, count);
+ if (copy_from_user(priv->vb.vaddr + priv->bytes, buf, count))
+ return -EFAULT;
+
+ priv->bytes += count;
+ return count;
+}
+
+static ssize_t fpga_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *f_pos)
+{
+ struct fpga_dev *priv = filp->private_data;
+
+ count = min_t(size_t, priv->bytes - *f_pos, count);
+ if (copy_to_user(buf, priv->vb.vaddr + *f_pos, count))
+ return -EFAULT;
+
+ *f_pos += count;
+ return count;
+}
+
+static loff_t fpga_llseek(struct file *filp, loff_t offset, int origin)
+{
+ struct fpga_dev *priv = filp->private_data;
+ loff_t newpos;
+
+ /* only read-only opens are allowed to seek */
+ if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
+ return -EINVAL;
+
+ switch (origin) {
+ case SEEK_SET: /* seek relative to the beginning of the file */
+ newpos = offset;
+ break;
+ case SEEK_CUR: /* seek relative to current position in the file */
+ newpos = filp->f_pos + offset;
+ break;
+ case SEEK_END: /* seek relative to the end of the file */
+ newpos = priv->fw_size - offset;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* check for sanity */
+ if (newpos > priv->fw_size)
+ return -EINVAL;
+
+ filp->f_pos = newpos;
+ return newpos;
+}
+
+static const struct file_operations fpga_fops = {
+ .open = fpga_open,
+ .release = fpga_release,
+ .write = fpga_write,
+ .read = fpga_read,
+ .llseek = fpga_llseek,
+};
+
+/*
+ * Device Attributes
+ */
+
+static ssize_t pfail_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fpga_dev *priv = dev_get_drvdata(dev);
+ u8 val;
+
+ val = ioread8(priv->regs + CTL_PWR_FAIL);
+ return snprintf(buf, PAGE_SIZE, "0x%.2x\n", val);
+}
+
+static ssize_t pgood_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fpga_dev *priv = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%d\n", fpga_power_good(priv));
+}
+
+static ssize_t penable_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fpga_dev *priv = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%d\n", fpga_power_enabled(priv));
+}
+
+static ssize_t penable_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fpga_dev *priv = dev_get_drvdata(dev);
+ unsigned long val;
+ int ret;
+
+ if (strict_strtoul(buf, 0, &val))
+ return -EINVAL;
+
+ if (val) {
+ ret = fpga_enable_power_supplies(priv);
+ if (ret)
+ return ret;
+ } else {
+ fpga_do_stop(priv);
+ fpga_disable_power_supplies(priv);
+ }
+
+ return count;
+}
+
+static ssize_t program_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fpga_dev *priv = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%d\n", fpga_running(priv));
+}
+
+static ssize_t program_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fpga_dev *priv = dev_get_drvdata(dev);
+ unsigned long val;
+ int ret;
+
+ if (strict_strtoul(buf, 0, &val))
+ return -EINVAL;
+
+ /* We can't have an image writer and be programming simultaneously */
+ if (mutex_lock_interruptible(&priv->lock))
+ return -ERESTARTSYS;
+
+ /* Program or Reset the FPGA's */
+ ret = val ? fpga_do_program(priv) : fpga_do_stop(priv);
+ if (ret)
+ goto out_unlock;
+
+ /* Success */
+ ret = count;
+
+out_unlock:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static DEVICE_ATTR(power_fail, S_IRUGO, pfail_show, NULL);
+static DEVICE_ATTR(power_good, S_IRUGO, pgood_show, NULL);
+static DEVICE_ATTR(power_enable, S_IRUGO | S_IWUSR,
+ penable_show, penable_store);
+
+static DEVICE_ATTR(program, S_IRUGO | S_IWUSR,
+ program_show, program_store);
+
+static struct attribute *fpga_attributes[] = {
+ &dev_attr_power_fail.attr,
+ &dev_attr_power_good.attr,
+ &dev_attr_power_enable.attr,
+ &dev_attr_program.attr,
+ NULL,
+};
+
+static const struct attribute_group fpga_attr_group = {
+ .attrs = fpga_attributes,
+};
+
+/*
+ * OpenFirmware Device Subsystem
+ */
+
+#define SYS_REG_VERSION 0x00
+#define SYS_REG_GEOGRAPHIC 0x10
+
+static bool dma_filter(struct dma_chan *chan, void *data)
+{
+ /*
+ * DMA Channel #0 is the only acceptable device
+ *
+ * This probably won't survive an unload/load cycle of the Freescale
+ * DMAEngine driver, but that won't be a problem
+ */
+ return chan->chan_id == 0 && chan->device->dev_id == 0;
+}
+
+static int fpga_of_remove(struct platform_device *op)
+{
+ struct fpga_dev *priv = dev_get_drvdata(&op->dev);
+ struct device *this_device = priv->miscdev.this_device;
+
+ sysfs_remove_group(&this_device->kobj, &fpga_attr_group);
+ misc_deregister(&priv->miscdev);
+
+ free_irq(priv->irq, priv);
+ irq_dispose_mapping(priv->irq);
+
+ /* make sure the power supplies are off */
+ fpga_disable_power_supplies(priv);
+
+ /* unmap registers */
+ iounmap(priv->immr);
+ iounmap(priv->regs);
+
+ dma_release_channel(priv->chan);
+
+ /* drop our reference to the private data structure */
+ kref_put(&priv->ref, fpga_dev_remove);
+ return 0;
+}
+
+/* CTL-CPLD Version Register */
+#define CTL_CPLD_VERSION 0x2000
+
+static int fpga_of_probe(struct platform_device *op,
+ const struct of_device_id *match)
+{
+ struct device_node *of_node = op->dev.of_node;
+ struct device *this_device;
+ struct fpga_dev *priv;
+ dma_cap_mask_t mask;
+ u32 ver;
+ int ret;
+
+ /* Allocate private data */
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&op->dev, "Unable to allocate private data\n");
+ ret = -ENOMEM;
+ goto out_return;
+ }
+
+ /* Setup the miscdevice */
+ priv->miscdev.minor = MISC_DYNAMIC_MINOR;
+ priv->miscdev.name = drv_name;
+ priv->miscdev.fops = &fpga_fops;
+
+ kref_init(&priv->ref);
+
+ dev_set_drvdata(&op->dev, priv);
+ priv->dev = &op->dev;
+ mutex_init(&priv->lock);
+ init_completion(&priv->completion);
+ videobuf_dma_init(&priv->vb);
+
+ 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);
+
+ /* Get control of DMA channel #0 */
+ priv->chan = dma_request_channel(mask, dma_filter, NULL);
+ if (!priv->chan) {
+ dev_err(&op->dev, "Unable to acquire DMA channel #0\n");
+ ret = -ENODEV;
+ goto out_free_priv;
+ }
+
+ /* Remap the registers for use */
+ priv->regs = of_iomap(of_node, 0);
+ if (!priv->regs) {
+ dev_err(&op->dev, "Unable to ioremap registers\n");
+ ret = -ENOMEM;
+ goto out_dma_release_channel;
+ }
+
+ /* Remap the IMMR for use */
+ priv->immr = ioremap(get_immrbase(), 0x100000);
+ if (!priv->immr) {
+ dev_err(&op->dev, "Unable to ioremap IMMR\n");
+ ret = -ENOMEM;
+ goto out_unmap_regs;
+ }
+
+ /*
+ * Check that external DMA is configured
+ *
+ * U-Boot does this for us, but we should check it and bail out if
+ * there is a problem. Failing to have this register setup correctly
+ * will cause the DMA controller to transfer a single cacheline
+ * worth of data, then wedge itself.
+ */
+ if ((ioread32be(priv->immr + 0x114) & 0xE00) != 0xE00) {
+ dev_err(&op->dev, "External DMA control not configured\n");
+ ret = -ENODEV;
+ goto out_unmap_immr;
+ }
+
+ /*
+ * Check the CTL-CPLD version
+ *
+ * This driver uses the CTL-CPLD DATA-FPGA power sequencer, and we
+ * don't want to run on any version of the CTL-CPLD that does not use
+ * a compatible register layout.
+ *
+ * v2: changed register layout, added power sequencer
+ * v3: added glitch filter on the i2c overcurrent/overtemp outputs
+ */
+ ver = ioread8(priv->regs + CTL_CPLD_VERSION);
+ if (ver != 0x02 && ver != 0x03) {
+ dev_err(&op->dev, "CTL-CPLD is not version 0x02 or 0x03!\n");
+ ret = -ENODEV;
+ goto out_unmap_immr;
+ }
+
+ /* Set the exact size that the firmware image should be */
+ ver = ioread32be(priv->regs + SYS_REG_VERSION);
+ priv->fw_size = (ver & (1 << 18)) ? FW_SIZE_EP2S130 : FW_SIZE_EP2S90;
+
+ /* Find the correct IRQ number */
+ priv->irq = irq_of_parse_and_map(of_node, 0);
+ if (priv->irq == NO_IRQ) {
+ dev_err(&op->dev, "Unable to find IRQ line\n");
+ ret = -ENODEV;
+ goto out_unmap_immr;
+ }
+
+ /* Request the IRQ */
+ ret = request_irq(priv->irq, fpga_irq, IRQF_SHARED, drv_name, priv);
+ if (ret) {
+ dev_err(&op->dev, "Unable to request IRQ %d\n", priv->irq);
+ ret = -ENODEV;
+ goto out_irq_dispose_mapping;
+ }
+
+ /* Reset and stop the FPGA's, just in case */
+ fpga_do_stop(priv);
+
+ /* Register the miscdevice */
+ ret = misc_register(&priv->miscdev);
+ if (ret) {
+ dev_err(&op->dev, "Unable to register miscdevice\n");
+ goto out_free_irq;
+ }
+
+ /* Create the sysfs files */
+ this_device = priv->miscdev.this_device;
+ dev_set_drvdata(this_device, priv);
+ ret = sysfs_create_group(&this_device->kobj, &fpga_attr_group);
+ if (ret) {
+ dev_err(&op->dev, "Unable to create sysfs files\n");
+ goto out_misc_deregister;
+ }
+
+ dev_info(priv->dev, "CARMA FPGA Programmer: %s rev%s with %s FPGAs\n",
+ (ver & (1 << 17)) ? "Correlator" : "Digitizer",
+ (ver & (1 << 16)) ? "B" : "A",
+ (ver & (1 << 18)) ? "EP2S130" : "EP2S90");
+
+ return 0;
+
+out_misc_deregister:
+ misc_deregister(&priv->miscdev);
+out_free_irq:
+ free_irq(priv->irq, priv);
+out_irq_dispose_mapping:
+ irq_dispose_mapping(priv->irq);
+out_unmap_immr:
+ iounmap(priv->immr);
+out_unmap_regs:
+ iounmap(priv->regs);
+out_dma_release_channel:
+ dma_release_channel(priv->chan);
+out_free_priv:
+ kref_put(&priv->ref, fpga_dev_remove);
+out_return:
+ return ret;
+}
+
+static struct of_device_id fpga_of_match[] = {
+ { .compatible = "carma,fpga-programmer", },
+ {},
+};
+
+static struct of_platform_driver fpga_of_driver = {
+ .probe = fpga_of_probe,
+ .remove = fpga_of_remove,
+ .driver = {
+ .name = drv_name,
+ .of_match_table = fpga_of_match,
+ .owner = THIS_MODULE,
+ },
+};
+
+/*
+ * Module Init / Exit
+ */
+
+static int __init fpga_init(void)
+{
+ led_trigger_register_simple("fpga", &ledtrig_fpga);
+ return of_register_platform_driver(&fpga_of_driver);
+}
+
+static void __exit fpga_exit(void)
+{
+ of_unregister_platform_driver(&fpga_of_driver);
+ led_trigger_unregister_simple(ledtrig_fpga);
+}
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
+MODULE_DESCRIPTION("CARMA Board DATA-FPGA Programmer");
+MODULE_LICENSE("GPL");
+
+module_init(fpga_init);
+module_exit(fpga_exit);
diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c
new file mode 100644
index 00000000000..3965821fef1
--- /dev/null
+++ b/drivers/misc/carma/carma-fpga.c
@@ -0,0 +1,1433 @@
+/*
+ * CARMA DATA-FPGA Access Driver
+ *
+ * Copyright (c) 2009-2011 Ira W. Snyder <iws@ovro.caltech.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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+/*
+ * FPGA Memory Dump Format
+ *
+ * FPGA #0 control registers (32 x 32-bit words)
+ * FPGA #1 control registers (32 x 32-bit words)
+ * FPGA #2 control registers (32 x 32-bit words)
+ * FPGA #3 control registers (32 x 32-bit words)
+ * SYSFPGA control registers (32 x 32-bit words)
+ * FPGA #0 correlation array (NUM_CORL0 correlation blocks)
+ * FPGA #1 correlation array (NUM_CORL1 correlation blocks)
+ * FPGA #2 correlation array (NUM_CORL2 correlation blocks)
+ * FPGA #3 correlation array (NUM_CORL3 correlation blocks)
+ *
+ * Each correlation array consists of:
+ *
+ * Correlation Data (2 x NUM_LAGSn x 32-bit words)
+ * Pipeline Metadata (2 x NUM_METAn x 32-bit words)
+ * Quantization Counters (2 x NUM_QCNTn x 32-bit words)
+ *
+ * The NUM_CORLn, NUM_LAGSn, NUM_METAn, and NUM_QCNTn values come from
+ * the FPGA configuration registers. They do not change once the FPGA's
+ * have been programmed, they only change on re-programming.
+ */
+
+/*
+ * Basic Description:
+ *
+ * This driver is used to capture correlation spectra off of the four data
+ * processing FPGAs. The FPGAs are often reprogrammed at runtime, therefore
+ * this driver supports dynamic enable/disable of capture while the device
+ * remains open.
+ *
+ * The nominal capture rate is 64Hz (every 15.625ms). To facilitate this fast
+ * capture rate, all buffers are pre-allocated to avoid any potentially long
+ * running memory allocations while capturing.
+ *
+ * There are two lists and one pointer which are used to keep track of the
+ * different states of data buffers.
+ *
+ * 1) free list
+ * This list holds all empty data buffers which are ready to receive data.
+ *
+ * 2) inflight pointer
+ * This pointer holds the currently inflight data buffer. This buffer is having
+ * data copied into it by the DMA engine.
+ *
+ * 3) used list
+ * This list holds data buffers which have been filled, and are waiting to be
+ * read by userspace.
+ *
+ * All buffers start life on the free list, then move successively to the
+ * inflight pointer, and then to the used list. After they have been read by
+ * userspace, they are moved back to the free list. The cycle repeats as long
+ * as necessary.
+ *
+ * It should be noted that all buffers are mapped and ready for DMA when they
+ * are on any of the three lists. They are only unmapped when they are in the
+ * process of being read by userspace.
+ */
+
+/*
+ * Notes on the IRQ masking scheme:
+ *
+ * The IRQ masking scheme here is different than most other hardware. The only
+ * way for the DATA-FPGAs to detect if the kernel has taken too long to copy
+ * the data is if the status registers are not cleared before the next
+ * correlation data dump is ready.
+ *
+ * The interrupt line is connected to the status registers, such that when they
+ * are cleared, the interrupt is de-asserted. Therein lies our problem. We need
+ * to schedule a long-running DMA operation and return from the interrupt
+ * handler quickly, but we cannot clear the status registers.
+ *
+ * To handle this, the system controller FPGA has the capability to connect the
+ * interrupt line to a user-controlled GPIO pin. This pin is driven high
+ * (unasserted) and left that way. To mask the interrupt, we change the
+ * interrupt source to the GPIO pin. Tada, we hid the interrupt. :)
+ */
+
+#include <linux/of_platform.h>
+#include <linux/dma-mapping.h>
+#include <linux/miscdevice.h>
+#include <linux/interrupt.h>
+#include <linux/dmaengine.h>
+#include <linux/seq_file.h>
+#include <linux/highmem.h>
+#include <linux/debugfs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kref.h>
+#include <linux/io.h>
+
+#include <media/videobuf-dma-sg.h>
+
+/* system controller registers */
+#define SYS_IRQ_SOURCE_CTL 0x24
+#define SYS_IRQ_OUTPUT_EN 0x28
+#define SYS_IRQ_OUTPUT_DATA 0x2C
+#define SYS_IRQ_INPUT_DATA 0x30
+#define SYS_FPGA_CONFIG_STATUS 0x44
+
+/* GPIO IRQ line assignment */
+#define IRQ_CORL_DONE 0x10
+
+/* FPGA registers */
+#define MMAP_REG_VERSION 0x00
+#define MMAP_REG_CORL_CONF1 0x08
+#define MMAP_REG_CORL_CONF2 0x0C
+#define MMAP_REG_STATUS 0x48
+
+#define SYS_FPGA_BLOCK 0xF0000000
+
+#define DATA_FPGA_START 0x400000
+#define DATA_FPGA_SIZE 0x80000
+
+static const char drv_name[] = "carma-fpga";
+
+#define NUM_FPGA 4
+
+#define MIN_DATA_BUFS 8
+#define MAX_DATA_BUFS 64
+
+struct fpga_info {
+ unsigned int num_lag_ram;
+ unsigned int blk_size;
+};
+
+struct data_buf {
+ struct list_head entry;
+ struct videobuf_dmabuf vb;
+ size_t size;
+};
+
+struct fpga_device {
+ /* character device */
+ struct miscdevice miscdev;
+ struct device *dev;
+ struct mutex mutex;
+
+ /* reference count */
+ struct kref ref;
+
+ /* FPGA registers and information */
+ struct fpga_info info[NUM_FPGA];
+ void __iomem *regs;
+ int irq;
+
+ /* FPGA Physical Address/Size Information */
+ resource_size_t phys_addr;
+ size_t phys_size;
+
+ /* DMA structures */
+ struct sg_table corl_table;
+ unsigned int corl_nents;
+ struct dma_chan *chan;
+
+ /* Protection for all members below */
+ spinlock_t lock;
+
+ /* Device enable/disable flag */
+ bool enabled;
+
+ /* Correlation data buffers */
+ wait_queue_head_t wait;
+ struct list_head free;
+ struct list_head used;
+ struct data_buf *inflight;
+
+ /* Information about data buffers */
+ unsigned int num_dropped;
+ unsigned int num_buffers;
+ size_t bufsize;
+ struct dentry *dbg_entry;
+};
+
+struct fpga_reader {
+ struct fpga_device *priv;
+ struct data_buf *buf;
+ off_t buf_start;
+};
+
+static void fpga_device_release(struct kref *ref)
+{
+ struct fpga_device *priv = container_of(ref, struct fpga_device, ref);
+
+ /* the last reader has exited, cleanup the last bits */
+ mutex_destroy(&priv->mutex);
+ kfree(priv);
+}
+
+/*
+ * Data Buffer Allocation Helpers
+ */
+
+/**
+ * data_free_buffer() - free a single data buffer and all allocated memory
+ * @buf: the buffer to free
+ *
+ * This will free all of the pages allocated to the given data buffer, and
+ * then free the structure itself
+ */
+static void data_free_buffer(struct data_buf *buf)
+{
+ /* It is ok to free a NULL buffer */
+ if (!buf)
+ return;
+
+ /* free all memory */
+ videobuf_dma_free(&buf->vb);
+ kfree(buf);
+}
+
+/**
+ * data_alloc_buffer() - allocate and fill a data buffer with pages
+ * @bytes: the number of bytes required
+ *
+ * This allocates all space needed for a data buffer. It must be mapped before
+ * use in a DMA transaction using videobuf_dma_map().
+ *
+ * Returns NULL on failure
+ */
+static struct data_buf *data_alloc_buffer(const size_t bytes)
+{
+ unsigned int nr_pages;
+ struct data_buf *buf;
+ int ret;
+
+ /* calculate the number of pages necessary */
+ nr_pages = DIV_ROUND_UP(bytes, PAGE_SIZE);
+
+ /* allocate the buffer structure */
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ goto out_return;
+
+ /* initialize internal fields */
+ INIT_LIST_HEAD(&buf->entry);
+ buf->size = bytes;
+
+ /* allocate the videobuf */
+ videobuf_dma_init(&buf->vb);
+ ret = videobuf_dma_init_kernel(&buf->vb, DMA_FROM_DEVICE, nr_pages);
+ if (ret)
+ goto out_free_buf;
+
+ return buf;
+
+out_free_buf:
+ kfree(buf);
+out_return:
+ return NULL;
+}
+
+/**
+ * data_free_buffers() - free all allocated buffers
+ * @priv: the driver's private data structure
+ *
+ * Free all buffers allocated by the driver (except those currently in the
+ * process of being read by userspace).
+ *
+ * LOCKING: must hold dev->mutex
+ * CONTEXT: user
+ */
+static void data_free_buffers(struct fpga_device *priv)
+{
+ struct data_buf *buf, *tmp;
+
+ /* the device should be stopped, no DMA in progress */
+ BUG_ON(priv->inflight != NULL);
+
+ list_for_each_entry_safe(buf, tmp, &priv->free, entry) {
+ list_del_init(&buf->entry);
+ videobuf_dma_unmap(priv->dev, &buf->vb);
+ data_free_buffer(buf);
+ }
+
+ list_for_each_entry_safe(buf, tmp, &priv->used, entry) {
+ list_del_init(&buf->entry);
+ videobuf_dma_unmap(priv->dev, &buf->vb);
+ data_free_buffer(buf);
+ }
+
+ priv->num_buffers = 0;
+ priv->bufsize = 0;
+}
+
+/**
+ * data_alloc_buffers() - allocate 1 seconds worth of data buffers
+ * @priv: the driver's private data structure
+ *
+ * Allocate enough buffers for a whole second worth of data
+ *
+ * This routine will attempt to degrade nicely by succeeding even if a full
+ * second worth of data buffers could not be allocated, as long as a minimum
+ * number were allocated. In this case, it will print a message to the kernel
+ * log.
+ *
+ * The device must not be modifying any lists when this is called.
+ *
+ * CONTEXT: user
+ * LOCKING: must hold dev->mutex
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static int data_alloc_buffers(struct fpga_device *priv)
+{
+ struct data_buf *buf;
+ int i, ret;
+
+ for (i = 0; i < MAX_DATA_BUFS; i++) {
+
+ /* allocate a buffer */
+ buf = data_alloc_buffer(priv->bufsize);
+ if (!buf)
+ break;
+
+ /* map it for DMA */
+ ret = videobuf_dma_map(priv->dev, &buf->vb);
+ if (ret) {
+ data_free_buffer(buf);
+ break;
+ }
+
+ /* add it to the list of free buffers */
+ list_add_tail(&buf->entry, &priv->free);
+ priv->num_buffers++;
+ }
+
+ /* Make sure we allocated the minimum required number of buffers */
+ if (priv->num_buffers < MIN_DATA_BUFS) {
+ dev_err(priv->dev, "Unable to allocate enough data buffers\n");
+ data_free_buffers(priv);
+ return -ENOMEM;
+ }
+
+ /* Warn if we are running in a degraded state, but do not fail */
+ if (priv->num_buffers < MAX_DATA_BUFS) {
+ dev_warn(priv->dev,
+ "Unable to allocate %d buffers, using %d buffers instead\n",
+ MAX_DATA_BUFS, i);
+ }
+
+ return 0;
+}
+
+/*
+ * DMA Operations Helpers
+ */
+
+/**
+ * fpga_start_addr() - get the physical address a DATA-FPGA
+ * @priv: the driver's private data structure
+ * @fpga: the DATA-FPGA number (zero based)
+ */
+static dma_addr_t fpga_start_addr(struct fpga_device *priv, unsigned int fpga)
+{
+ return priv->phys_addr + 0x400000 + (0x80000 * fpga);
+}
+
+/**
+ * fpga_block_addr() - get the physical address of a correlation data block
+ * @priv: the driver's private data structure
+ * @fpga: the DATA-FPGA number (zero based)
+ * @blknum: the correlation block number (zero based)
+ */
+static dma_addr_t fpga_block_addr(struct fpga_device *priv, unsigned int fpga,
+ unsigned int blknum)
+{
+ return fpga_start_addr(priv, fpga) + (0x10000 * (1 + blknum));
+}
+
+#define REG_BLOCK_SIZE (32 * 4)
+
+/**
+ * data_setup_corl_table() - create the scatterlist for correlation dumps
+ * @priv: the driver's private data structure
+ *
+ * Create the scatterlist for transferring a correlation dump from the
+ * DATA FPGAs. This structure will be reused for each buffer than needs
+ * to be filled with correlation data.
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static int data_setup_corl_table(struct fpga_device *priv)
+{
+ struct sg_table *table = &priv->corl_table;
+ struct scatterlist *sg;
+ struct fpga_info *info;
+ int i, j, ret;
+
+ /* Calculate the number of entries needed */
+ priv->corl_nents = (1 + NUM_FPGA) * REG_BLOCK_SIZE;
+ for (i = 0; i < NUM_FPGA; i++)
+ priv->corl_nents += priv->info[i].num_lag_ram;
+
+ /* Allocate the scatterlist table */
+ ret = sg_alloc_table(table, priv->corl_nents, GFP_KERNEL);
+ if (ret) {
+ dev_err(priv->dev, "unable to allocate DMA table\n");
+ return ret;
+ }
+
+ /* Add the DATA FPGA registers to the scatterlist */
+ sg = table->sgl;
+ for (i = 0; i < NUM_FPGA; i++) {
+ sg_dma_address(sg) = fpga_start_addr(priv, i);
+ sg_dma_len(sg) = REG_BLOCK_SIZE;
+ sg = sg_next(sg);
+ }
+
+ /* Add the SYS-FPGA registers to the scatterlist */
+ sg_dma_address(sg) = SYS_FPGA_BLOCK;
+ sg_dma_len(sg) = REG_BLOCK_SIZE;
+ sg = sg_next(sg);
+
+ /* Add the FPGA correlation data blocks to the scatterlist */
+ for (i = 0; i < NUM_FPGA; i++) {
+ info = &priv->info[i];
+ for (j = 0; j < info->num_lag_ram; j++) {
+ sg_dma_address(sg) = fpga_block_addr(priv, i, j);
+ sg_dma_len(sg) = info->blk_size;
+ sg = sg_next(sg);
+ }
+ }
+
+ /*
+ * All physical addresses and lengths are present in the structure
+ * now. It can be reused for every FPGA DATA interrupt
+ */
+ return 0;
+}
+
+/*
+ * FPGA Register Access Helpers
+ */
+
+static void fpga_write_reg(struct fpga_device *priv, unsigned int fpga,
+ unsigned int reg, u32 val)
+{
+ const int fpga_start = DATA_FPGA_START + (fpga * DATA_FPGA_SIZE);
+ iowrite32be(val, priv->regs + fpga_start + reg);
+}
+
+static u32 fpga_read_reg(struct fpga_device *priv, unsigned int fpga,
+ unsigned int reg)
+{
+ const int fpga_start = DATA_FPGA_START + (fpga * DATA_FPGA_SIZE);
+ return ioread32be(priv->regs + fpga_start + reg);
+}
+
+/**
+ * data_calculate_bufsize() - calculate the data buffer size required
+ * @priv: the driver's private data structure
+ *
+ * Calculate the total buffer size needed to hold a single block
+ * of correlation data
+ *
+ * CONTEXT: user
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static int data_calculate_bufsize(struct fpga_device *priv)
+{
+ u32 num_corl, num_lags, num_meta, num_qcnt, num_pack;
+ u32 conf1, conf2, version;
+ u32 num_lag_ram, blk_size;
+ int i;
+
+ /* Each buffer starts with the 5 FPGA register areas */
+ priv->bufsize = (1 + NUM_FPGA) * REG_BLOCK_SIZE;
+
+ /* Read and store the configuration data for each FPGA */
+ for (i = 0; i < NUM_FPGA; i++) {
+ version = fpga_read_reg(priv, i, MMAP_REG_VERSION);
+ conf1 = fpga_read_reg(priv, i, MMAP_REG_CORL_CONF1);
+ conf2 = fpga_read_reg(priv, i, MMAP_REG_CORL_CONF2);
+
+ /* minor version 2 and later */
+ if ((version & 0x000000FF) >= 2) {
+ num_corl = (conf1 & 0x000000F0) >> 4;
+ num_pack = (conf1 & 0x00000F00) >> 8;
+ num_lags = (conf1 & 0x00FFF000) >> 12;
+ num_meta = (conf1 & 0x7F000000) >> 24;
+ num_qcnt = (conf2 & 0x00000FFF) >> 0;
+ } else {
+ num_corl = (conf1 & 0x000000F0) >> 4;
+ num_pack = 1; /* implied */
+ num_lags = (conf1 & 0x000FFF00) >> 8;
+ num_meta = (conf1 & 0x7FF00000) >> 20;
+ num_qcnt = (conf2 & 0x00000FFF) >> 0;
+ }
+
+ num_lag_ram = (num_corl + num_pack - 1) / num_pack;
+ blk_size = ((num_pack * num_lags) + num_meta + num_qcnt) * 8;
+
+ priv->info[i].num_lag_ram = num_lag_ram;
+ priv->info[i].blk_size = blk_size;
+ priv->bufsize += num_lag_ram * blk_size;
+
+ dev_dbg(priv->dev, "FPGA %d NUM_CORL: %d\n", i, num_corl);
+ dev_dbg(priv->dev, "FPGA %d NUM_PACK: %d\n", i, num_pack);
+ dev_dbg(priv->dev, "FPGA %d NUM_LAGS: %d\n", i, num_lags);
+ dev_dbg(priv->dev, "FPGA %d NUM_META: %d\n", i, num_meta);
+ dev_dbg(priv->dev, "FPGA %d NUM_QCNT: %d\n", i, num_qcnt);
+ dev_dbg(priv->dev, "FPGA %d BLK_SIZE: %d\n", i, blk_size);
+ }
+
+ dev_dbg(priv->dev, "TOTAL BUFFER SIZE: %zu bytes\n", priv->bufsize);
+ return 0;
+}
+
+/*
+ * Interrupt Handling
+ */
+
+/**
+ * data_disable_interrupts() - stop the device from generating interrupts
+ * @priv: the driver's private data structure
+ *
+ * Hide interrupts by switching to GPIO interrupt source
+ *
+ * LOCKING: must hold dev->lock
+ */
+static void data_disable_interrupts(struct fpga_device *priv)
+{
+ /* hide the interrupt by switching the IRQ driver to GPIO */
+ iowrite32be(0x2F, priv->regs + SYS_IRQ_SOURCE_CTL);
+}
+
+/**
+ * data_enable_interrupts() - allow the device to generate interrupts
+ * @priv: the driver's private data structure
+ *
+ * Unhide interrupts by switching to the FPGA interrupt source. At the
+ * same time, clear the DATA-FPGA status registers.
+ *
+ * LOCKING: must hold dev->lock
+ */
+static void data_enable_interrupts(struct fpga_device *priv)
+{
+ /* clear the actual FPGA corl_done interrupt */
+ fpga_write_reg(priv, 0, MMAP_REG_STATUS, 0x0);
+ fpga_write_reg(priv, 1, MMAP_REG_STATUS, 0x0);
+ fpga_write_reg(priv, 2, MMAP_REG_STATUS, 0x0);
+ fpga_write_reg(priv, 3, MMAP_REG_STATUS, 0x0);
+
+ /* flush the writes */
+ fpga_read_reg(priv, 0, MMAP_REG_STATUS);
+
+ /* switch back to the external interrupt source */
+ iowrite32be(0x3F, priv->regs + SYS_IRQ_SOURCE_CTL);
+}
+
+/**
+ * data_dma_cb() - DMAEngine callback for DMA completion
+ * @data: the driver's private data structure
+ *
+ * Complete a DMA transfer from the DATA-FPGA's
+ *
+ * This is called via the DMA callback mechanism, and will handle moving the
+ * completed DMA transaction to the used list, and then wake any processes
+ * waiting for new data
+ *
+ * CONTEXT: any, softirq expected
+ */
+static void data_dma_cb(void *data)
+{
+ struct fpga_device *priv = data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ /* If there is no inflight buffer, we've got a bug */
+ BUG_ON(priv->inflight == NULL);
+
+ /* Move the inflight buffer onto the used list */
+ list_move_tail(&priv->inflight->entry, &priv->used);
+ priv->inflight = NULL;
+
+ /* clear the FPGA status and re-enable interrupts */
+ data_enable_interrupts(priv);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ /*
+ * We've changed both the inflight and used lists, so we need
+ * to wake up any processes that are blocking for those events
+ */
+ wake_up(&priv->wait);
+}
+
+/**
+ * data_submit_dma() - prepare and submit the required DMA to fill a buffer
+ * @priv: the driver's private data structure
+ * @buf: the data buffer
+ *
+ * Prepare and submit the necessary DMA transactions to fill a correlation
+ * data buffer.
+ *
+ * LOCKING: must hold dev->lock
+ * CONTEXT: hardirq only
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static int data_submit_dma(struct fpga_device *priv, struct data_buf *buf)
+{
+ struct scatterlist *dst_sg, *src_sg;
+ unsigned int dst_nents, src_nents;
+ struct dma_chan *chan = priv->chan;
+ struct dma_async_tx_descriptor *tx;
+ dma_cookie_t cookie;
+ dma_addr_t dst, src;
+
+ dst_sg = buf->vb.sglist;
+ dst_nents = buf->vb.sglen;
+
+ src_sg = priv->corl_table.sgl;
+ src_nents = priv->corl_nents;
+
+ /*
+ * All buffers passed to this function should be ready and mapped
+ * for DMA already. Therefore, we don't need to do anything except
+ * submit it to the Freescale DMA Engine for processing
+ */
+
+ /* setup the scatterlist to scatterlist transfer */
+ tx = chan->device->device_prep_dma_sg(chan,
+ dst_sg, dst_nents,
+ src_sg, src_nents,
+ 0);
+ if (!tx) {
+ dev_err(priv->dev, "unable to prep scatterlist DMA\n");
+ return -ENOMEM;
+ }
+
+ /* submit the transaction to the DMA controller */
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(priv->dev, "unable to submit scatterlist DMA\n");
+ return -ENOMEM;
+ }
+
+ /* Prepare the re-read of the SYS-FPGA block */
+ dst = sg_dma_address(dst_sg) + (NUM_FPGA * REG_BLOCK_SIZE);
+ src = SYS_FPGA_BLOCK;
+ tx = chan->device->device_prep_dma_memcpy(chan, dst, src,
+ REG_BLOCK_SIZE,
+ DMA_PREP_INTERRUPT);
+ if (!tx) {
+ dev_err(priv->dev, "unable to prep SYS-FPGA DMA\n");
+ return -ENOMEM;
+ }
+
+ /* Setup the callback */
+ tx->callback = data_dma_cb;
+ tx->callback_param = priv;
+
+ /* submit the transaction to the DMA controller */
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(priv->dev, "unable to submit SYS-FPGA DMA\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+#define CORL_DONE 0x1
+#define CORL_ERR 0x2
+
+static irqreturn_t data_irq(int irq, void *dev_id)
+{
+ struct fpga_device *priv = dev_id;
+ bool submitted = false;
+ struct data_buf *buf;
+ u32 status;
+ int i;
+
+ /* detect spurious interrupts via FPGA status */
+ for (i = 0; i < 4; i++) {
+ status = fpga_read_reg(priv, i, MMAP_REG_STATUS);
+ if (!(status & (CORL_DONE | CORL_ERR))) {
+ dev_err(priv->dev, "spurious irq detected (FPGA)\n");
+ return IRQ_NONE;
+ }
+ }
+
+ /* detect spurious interrupts via raw IRQ pin readback */
+ status = ioread32be(priv->regs + SYS_IRQ_INPUT_DATA);
+ if (status & IRQ_CORL_DONE) {
+ dev_err(priv->dev, "spurious irq detected (IRQ)\n");
+ return IRQ_NONE;
+ }
+
+ spin_lock(&priv->lock);
+
+ /* hide the interrupt by switching the IRQ driver to GPIO */
+ data_disable_interrupts(priv);
+
+ /* If there are no free buffers, drop this data */
+ if (list_empty(&priv->free)) {
+ priv->num_dropped++;
+ goto out;
+ }
+
+ buf = list_first_entry(&priv->free, struct data_buf, entry);
+ list_del_init(&buf->entry);
+ BUG_ON(buf->size != priv->bufsize);
+
+ /* Submit a DMA transfer to get the correlation data */
+ if (data_submit_dma(priv, buf)) {
+ dev_err(priv->dev, "Unable to setup DMA transfer\n");
+ list_move_tail(&buf->entry, &priv->free);
+ goto out;
+ }
+
+ /* Save the buffer for the DMA callback */
+ priv->inflight = buf;
+ submitted = true;
+
+ /* Start the DMA Engine */
+ dma_async_memcpy_issue_pending(priv->chan);
+
+out:
+ /* If no DMA was submitted, re-enable interrupts */
+ if (!submitted)
+ data_enable_interrupts(priv);
+
+ spin_unlock(&priv->lock);
+ return IRQ_HANDLED;
+}
+
+/*
+ * Realtime Device Enable Helpers
+ */
+
+/**
+ * data_device_enable() - enable the device for buffered dumping
+ * @priv: the driver's private data structure
+ *
+ * Enable the device for buffered dumping. Allocates buffers and hooks up
+ * the interrupt handler. When this finishes, data will come pouring in.
+ *
+ * LOCKING: must hold dev->mutex
+ * CONTEXT: user context only
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static int data_device_enable(struct fpga_device *priv)
+{
+ u32 val;
+ int ret;
+
+ /* multiple enables are safe: they do nothing */
+ if (priv->enabled)
+ return 0;
+
+ /* check that the FPGAs are programmed */
+ val = ioread32be(priv->regs + SYS_FPGA_CONFIG_STATUS);
+ if (!(val & (1 << 18))) {
+ dev_err(priv->dev, "DATA-FPGAs are not enabled\n");
+ return -ENODATA;
+ }
+
+ /* read the FPGAs to calculate the buffer size */
+ ret = data_calculate_bufsize(priv);
+ if (ret) {
+ dev_err(priv->dev, "unable to calculate buffer size\n");
+ goto out_error;
+ }
+
+ /* allocate the correlation data buffers */
+ ret = data_alloc_buffers(priv);
+ if (ret) {
+ dev_err(priv->dev, "unable to allocate buffers\n");
+ goto out_error;
+ }
+
+ /* setup the source scatterlist for dumping correlation data */
+ ret = data_setup_corl_table(priv);
+ if (ret) {
+ dev_err(priv->dev, "unable to setup correlation DMA table\n");
+ goto out_error;
+ }
+
+ /* hookup the irq handler */
+ ret = request_irq(priv->irq, data_irq, IRQF_SHARED, drv_name, priv);
+ if (ret) {
+ dev_err(priv->dev, "unable to request IRQ handler\n");
+ goto out_error;
+ }
+
+ /* switch to the external FPGA IRQ line */
+ data_enable_interrupts(priv);
+
+ /* success, we're enabled */
+ priv->enabled = true;
+ return 0;
+
+out_error:
+ sg_free_table(&priv->corl_table);
+ priv->corl_nents = 0;
+
+ data_free_buffers(priv);
+ return ret;
+}
+
+/**
+ * data_device_disable() - disable the device for buffered dumping
+ * @priv: the driver's private data structure
+ *
+ * Disable the device for buffered dumping. Stops new DMA transactions from
+ * being generated, waits for all outstanding DMA to complete, and then frees
+ * all buffers.
+ *
+ * LOCKING: must hold dev->mutex
+ * CONTEXT: user only
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static int data_device_disable(struct fpga_device *priv)
+{
+ int ret;
+
+ /* allow multiple disable */
+ if (!priv->enabled)
+ return 0;
+
+ /* switch to the internal GPIO IRQ line */
+ data_disable_interrupts(priv);
+
+ /* unhook the irq handler */
+ free_irq(priv->irq, priv);
+
+ /*
+ * wait for all outstanding DMA to complete
+ *
+ * Device interrupts are disabled, therefore another buffer cannot
+ * be marked inflight.
+ */
+ ret = wait_event_interruptible(priv->wait, priv->inflight == NULL);
+ if (ret)
+ return ret;
+
+ /* free the correlation table */
+ sg_free_table(&priv->corl_table);
+ priv->corl_nents = 0;
+
+ /*
+ * We are taking the spinlock not to protect priv->enabled, but instead
+ * to make sure that there are no readers in the process of altering
+ * the free or used lists while we are setting this flag.
+ */
+ spin_lock_irq(&priv->lock);
+ priv->enabled = false;
+ spin_unlock_irq(&priv->lock);
+
+ /* free all buffers: the free and used lists are not being changed */
+ data_free_buffers(priv);
+ return 0;
+}
+
+/*
+ * DEBUGFS Interface
+ */
+#ifdef CONFIG_DEBUG_FS
+
+/*
+ * Count the number of entries in the given list
+ */
+static unsigned int list_num_entries(struct list_head *list)
+{
+ struct list_head *entry;
+ unsigned int ret = 0;
+
+ list_for_each(entry, list)
+ ret++;
+
+ return ret;
+}
+
+static int data_debug_show(struct seq_file *f, void *offset)
+{
+ struct fpga_device *priv = f->private;
+ int ret;
+
+ /*
+ * Lock the mutex first, so that we get an accurate value for enable
+ * Lock the spinlock next, to get accurate list counts
+ */
+ ret = mutex_lock_interruptible(&priv->mutex);
+ if (ret)
+ return ret;
+
+ spin_lock_irq(&priv->lock);
+
+ seq_printf(f, "enabled: %d\n", priv->enabled);
+ seq_printf(f, "bufsize: %d\n", priv->bufsize);
+ seq_printf(f, "num_buffers: %d\n", priv->num_buffers);
+ seq_printf(f, "num_free: %d\n", list_num_entries(&priv->free));
+ seq_printf(f, "inflight: %d\n", priv->inflight != NULL);
+ seq_printf(f, "num_used: %d\n", list_num_entries(&priv->used));
+ seq_printf(f, "num_dropped: %d\n", priv->num_dropped);
+
+ spin_unlock_irq(&priv->lock);
+ mutex_unlock(&priv->mutex);
+ return 0;
+}
+
+static int data_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, data_debug_show, inode->i_private);
+}
+
+static const struct file_operations data_debug_fops = {
+ .owner = THIS_MODULE,
+ .open = data_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int data_debugfs_init(struct fpga_device *priv)
+{
+ priv->dbg_entry = debugfs_create_file(drv_name, S_IRUGO, NULL, priv,
+ &data_debug_fops);
+ if (IS_ERR(priv->dbg_entry))
+ return PTR_ERR(priv->dbg_entry);
+
+ return 0;
+}
+
+static void data_debugfs_exit(struct fpga_device *priv)
+{
+ debugfs_remove(priv->dbg_entry);
+}
+
+#else
+
+static inline int data_debugfs_init(struct fpga_device *priv)
+{
+ return 0;
+}
+
+static inline void data_debugfs_exit(struct fpga_device *priv)
+{
+}
+
+#endif /* CONFIG_DEBUG_FS */
+
+/*
+ * SYSFS Attributes
+ */
+
+static ssize_t data_en_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fpga_device *priv = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%u\n", priv->enabled);
+}
+
+static ssize_t data_en_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fpga_device *priv = dev_get_drvdata(dev);
+ unsigned long enable;
+ int ret;
+
+ ret = strict_strtoul(buf, 0, &enable);
+ if (ret) {
+ dev_err(priv->dev, "unable to parse enable input\n");
+ return -EINVAL;
+ }
+
+ ret = mutex_lock_interruptible(&priv->mutex);
+ if (ret)
+ return ret;
+
+ if (enable)
+ ret = data_device_enable(priv);
+ else
+ ret = data_device_disable(priv);
+
+ if (ret) {
+ dev_err(priv->dev, "device %s failed\n",
+ enable ? "enable" : "disable");
+ count = ret;
+ goto out_unlock;
+ }
+
+out_unlock:
+ mutex_unlock(&priv->mutex);
+ return count;
+}
+
+static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, data_en_show, data_en_set);
+
+static struct attribute *data_sysfs_attrs[] = {
+ &dev_attr_enable.attr,
+ NULL,
+};
+
+static const struct attribute_group rt_sysfs_attr_group = {
+ .attrs = data_sysfs_attrs,
+};
+
+/*
+ * FPGA Realtime Data Character Device
+ */
+
+static int data_open(struct inode *inode, struct file *filp)
+{
+ /*
+ * The miscdevice layer puts our struct miscdevice into the
+ * filp->private_data field. We use this to find our private
+ * data and then overwrite it with our own private structure.
+ */
+ struct fpga_device *priv = container_of(filp->private_data,
+ struct fpga_device, miscdev);
+ struct fpga_reader *reader;
+ int ret;
+
+ /* allocate private data */
+ reader = kzalloc(sizeof(*reader), GFP_KERNEL);
+ if (!reader)
+ return -ENOMEM;
+
+ reader->priv = priv;
+ reader->buf = NULL;
+
+ filp->private_data = reader;
+ ret = nonseekable_open(inode, filp);
+ if (ret) {
+ dev_err(priv->dev, "nonseekable-open failed\n");
+ kfree(reader);
+ return ret;
+ }
+
+ /*
+ * success, increase the reference count of the private data structure
+ * so that it doesn't disappear if the device is unbound
+ */
+ kref_get(&priv->ref);
+ return 0;
+}
+
+static int data_release(struct inode *inode, struct file *filp)
+{
+ struct fpga_reader *reader = filp->private_data;
+ struct fpga_device *priv = reader->priv;
+
+ /* free the per-reader structure */
+ data_free_buffer(reader->buf);
+ kfree(reader);
+ filp->private_data = NULL;
+
+ /* decrement our reference count to the private data */
+ kref_put(&priv->ref, fpga_device_release);
+ return 0;
+}
+
+static ssize_t data_read(struct file *filp, char __user *ubuf, size_t count,
+ loff_t *f_pos)
+{
+ struct fpga_reader *reader = filp->private_data;
+ struct fpga_device *priv = reader->priv;
+ struct list_head *used = &priv->used;
+ struct data_buf *dbuf;
+ size_t avail;
+ void *data;
+ int ret;
+
+ /* check if we already have a partial buffer */
+ if (reader->buf) {
+ dbuf = reader->buf;
+ goto have_buffer;
+ }
+
+ spin_lock_irq(&priv->lock);
+
+ /* Block until there is at least one buffer on the used list */
+ while (list_empty(used)) {
+ spin_unlock_irq(&priv->lock);
+
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ ret = wait_event_interruptible(priv->wait, !list_empty(used));
+ if (ret)
+ return ret;
+
+ spin_lock_irq(&priv->lock);
+ }
+
+ /* Grab the first buffer off of the used list */
+ dbuf = list_first_entry(used, struct data_buf, entry);
+ list_del_init(&dbuf->entry);
+
+ spin_unlock_irq(&priv->lock);
+
+ /* Buffers are always mapped: unmap it */
+ videobuf_dma_unmap(priv->dev, &dbuf->vb);
+
+ /* save the buffer for later */
+ reader->buf = dbuf;
+ reader->buf_start = 0;
+
+have_buffer:
+ /* Get the number of bytes available */
+ avail = dbuf->size - reader->buf_start;
+ data = dbuf->vb.vaddr + reader->buf_start;
+
+ /* Get the number of bytes we can transfer */
+ count = min(count, avail);
+
+ /* Copy the data to the userspace buffer */
+ if (copy_to_user(ubuf, data, count))
+ return -EFAULT;
+
+ /* Update the amount of available space */
+ avail -= count;
+
+ /*
+ * If there is still some data available, save the buffer for the
+ * next userspace call to read() and return
+ */
+ if (avail > 0) {
+ reader->buf_start += count;
+ reader->buf = dbuf;
+ return count;
+ }
+
+ /*
+ * Get the buffer ready to be reused for DMA
+ *
+ * If it fails, we pretend that the read never happed and return
+ * -EFAULT to userspace. The read will be retried.
+ */
+ ret = videobuf_dma_map(priv->dev, &dbuf->vb);
+ if (ret) {
+ dev_err(priv->dev, "unable to remap buffer for DMA\n");
+ return -EFAULT;
+ }
+
+ /* Lock against concurrent enable/disable */
+ spin_lock_irq(&priv->lock);
+
+ /* the reader is finished with this buffer */
+ reader->buf = NULL;
+
+ /*
+ * One of two things has happened, the device is disabled, or the
+ * device has been reconfigured underneath us. In either case, we
+ * should just throw away the buffer.
+ */
+ if (!priv->enabled || dbuf->size != priv->bufsize) {
+ videobuf_dma_unmap(priv->dev, &dbuf->vb);
+ data_free_buffer(dbuf);
+ goto out_unlock;
+ }
+
+ /* The buffer is safe to reuse, so add it back to the free list */
+ list_add_tail(&dbuf->entry, &priv->free);
+
+out_unlock:
+ spin_unlock_irq(&priv->lock);
+ return count;
+}
+
+static unsigned int data_poll(struct file *filp, struct poll_table_struct *tbl)
+{
+ struct fpga_reader *reader = filp->private_data;
+ struct fpga_device *priv = reader->priv;
+ unsigned int mask = 0;
+
+ poll_wait(filp, &priv->wait, tbl);
+
+ if (!list_empty(&priv->used))
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+static int data_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct fpga_reader *reader = filp->private_data;
+ struct fpga_device *priv = reader->priv;
+ unsigned long offset, vsize, psize, addr;
+
+ /* VMA properties */
+ offset = vma->vm_pgoff << PAGE_SHIFT;
+ vsize = vma->vm_end - vma->vm_start;
+ psize = priv->phys_size - offset;
+ addr = (priv->phys_addr + offset) >> PAGE_SHIFT;
+
+ /* Check against the FPGA region's physical memory size */
+ if (vsize > psize) {
+ dev_err(priv->dev, "requested mmap mapping too large\n");
+ return -EINVAL;
+ }
+
+ /* IO memory (stop cacheing) */
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ return io_remap_pfn_range(vma, vma->vm_start, addr, vsize,
+ vma->vm_page_prot);
+}
+
+static const struct file_operations data_fops = {
+ .owner = THIS_MODULE,
+ .open = data_open,
+ .release = data_release,
+ .read = data_read,
+ .poll = data_poll,
+ .mmap = data_mmap,
+ .llseek = no_llseek,
+};
+
+/*
+ * OpenFirmware Device Subsystem
+ */
+
+static bool dma_filter(struct dma_chan *chan, void *data)
+{
+ /*
+ * DMA Channel #0 is used for the FPGA Programmer, so ignore it
+ *
+ * This probably won't survive an unload/load cycle of the Freescale
+ * DMAEngine driver, but that won't be a problem
+ */
+ if (chan->chan_id == 0 && chan->device->dev_id == 0)
+ return false;
+
+ return true;
+}
+
+static int data_of_probe(struct platform_device *op,
+ const struct of_device_id *match)
+{
+ struct device_node *of_node = op->dev.of_node;
+ struct device *this_device;
+ struct fpga_device *priv;
+ struct resource res;
+ dma_cap_mask_t mask;
+ int ret;
+
+ /* Allocate private data */
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&op->dev, "Unable to allocate device private data\n");
+ ret = -ENOMEM;
+ goto out_return;
+ }
+
+ dev_set_drvdata(&op->dev, priv);
+ priv->dev = &op->dev;
+ kref_init(&priv->ref);
+ mutex_init(&priv->mutex);
+
+ dev_set_drvdata(priv->dev, priv);
+ spin_lock_init(&priv->lock);
+ INIT_LIST_HEAD(&priv->free);
+ INIT_LIST_HEAD(&priv->used);
+ init_waitqueue_head(&priv->wait);
+
+ /* Setup the misc device */
+ priv->miscdev.minor = MISC_DYNAMIC_MINOR;
+ priv->miscdev.name = drv_name;
+ priv->miscdev.fops = &data_fops;
+
+ /* Get the physical address of the FPGA registers */
+ ret = of_address_to_resource(of_node, 0, &res);
+ if (ret) {
+ dev_err(&op->dev, "Unable to find FPGA physical address\n");
+ ret = -ENODEV;
+ goto out_free_priv;
+ }
+
+ priv->phys_addr = res.start;
+ priv->phys_size = resource_size(&res);
+
+ /* ioremap the registers for use */
+ priv->regs = of_iomap(of_node, 0);
+ if (!priv->regs) {
+ dev_err(&op->dev, "Unable to ioremap registers\n");
+ ret = -ENOMEM;
+ goto out_free_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);
+
+ /* Request a DMA channel */
+ priv->chan = dma_request_channel(mask, dma_filter, NULL);
+ if (!priv->chan) {
+ dev_err(&op->dev, "Unable to request DMA channel\n");
+ ret = -ENODEV;
+ goto out_unmap_regs;
+ }
+
+ /* Find the correct IRQ number */
+ priv->irq = irq_of_parse_and_map(of_node, 0);
+ if (priv->irq == NO_IRQ) {
+ dev_err(&op->dev, "Unable to find IRQ line\n");
+ ret = -ENODEV;
+ goto out_release_dma;
+ }
+
+ /* Drive the GPIO for FPGA IRQ high (no interrupt) */
+ iowrite32be(IRQ_CORL_DONE, priv->regs + SYS_IRQ_OUTPUT_DATA);
+
+ /* Register the miscdevice */
+ ret = misc_register(&priv->miscdev);
+ if (ret) {
+ dev_err(&op->dev, "Unable to register miscdevice\n");
+ goto out_irq_dispose_mapping;
+ }
+
+ /* Create the debugfs files */
+ ret = data_debugfs_init(priv);
+ if (ret) {
+ dev_err(&op->dev, "Unable to create debugfs files\n");
+ goto out_misc_deregister;
+ }
+
+ /* Create the sysfs files */
+ this_device = priv->miscdev.this_device;
+ dev_set_drvdata(this_device, priv);
+ ret = sysfs_create_group(&this_device->kobj, &rt_sysfs_attr_group);
+ if (ret) {
+ dev_err(&op->dev, "Unable to create sysfs files\n");
+ goto out_data_debugfs_exit;
+ }
+
+ dev_info(&op->dev, "CARMA FPGA Realtime Data Driver Loaded\n");
+ return 0;
+
+out_data_debugfs_exit:
+ data_debugfs_exit(priv);
+out_misc_deregister:
+ misc_deregister(&priv->miscdev);
+out_irq_dispose_mapping:
+ irq_dispose_mapping(priv->irq);
+out_release_dma:
+ dma_release_channel(priv->chan);
+out_unmap_regs:
+ iounmap(priv->regs);
+out_free_priv:
+ kref_put(&priv->ref, fpga_device_release);
+out_return:
+ return ret;
+}
+
+static int data_of_remove(struct platform_device *op)
+{
+ struct fpga_device *priv = dev_get_drvdata(&op->dev);
+ struct device *this_device = priv->miscdev.this_device;
+
+ /* remove all sysfs files, now the device cannot be re-enabled */
+ sysfs_remove_group(&this_device->kobj, &rt_sysfs_attr_group);
+
+ /* remove all debugfs files */
+ data_debugfs_exit(priv);
+
+ /* disable the device from generating data */
+ data_device_disable(priv);
+
+ /* remove the character device to stop new readers from appearing */
+ misc_deregister(&priv->miscdev);
+
+ /* cleanup everything not needed by readers */
+ irq_dispose_mapping(priv->irq);
+ dma_release_channel(priv->chan);
+ iounmap(priv->regs);
+
+ /* release our reference */
+ kref_put(&priv->ref, fpga_device_release);
+ return 0;
+}
+
+static struct of_device_id data_of_match[] = {
+ { .compatible = "carma,carma-fpga", },
+ {},
+};
+
+static struct of_platform_driver data_of_driver = {
+ .probe = data_of_probe,
+ .remove = data_of_remove,
+ .driver = {
+ .name = drv_name,
+ .of_match_table = data_of_match,
+ .owner = THIS_MODULE,
+ },
+};
+
+/*
+ * Module Init / Exit
+ */
+
+static int __init data_init(void)
+{
+ return of_register_platform_driver(&data_of_driver);
+}
+
+static void __exit data_exit(void)
+{
+ of_unregister_platform_driver(&data_of_driver);
+}
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
+MODULE_DESCRIPTION("CARMA DATA-FPGA Access Driver");
+MODULE_LICENSE("GPL");
+
+module_init(data_init);
+module_exit(data_exit);
diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c
index 38657cdaf54..c4acac74725 100644
--- a/drivers/misc/sgi-gru/grufault.c
+++ b/drivers/misc/sgi-gru/grufault.c
@@ -33,6 +33,7 @@
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/security.h>
+#include <linux/prefetch.h>
#include <asm/pgtable.h>
#include "gru.h"
#include "grutables.h"
diff --git a/drivers/misc/sgi-gru/grufile.c b/drivers/misc/sgi-gru/grufile.c
index 20e4e9395b6..ecafa4ba238 100644
--- a/drivers/misc/sgi-gru/grufile.c
+++ b/drivers/misc/sgi-gru/grufile.c
@@ -348,15 +348,15 @@ static unsigned long gru_chiplet_cpu_to_mmr(int chiplet, int cpu, int *corep)
static int gru_irq_count[GRU_CHIPLETS_PER_BLADE];
-static void gru_noop(unsigned int irq)
+static void gru_noop(struct irq_data *d)
{
}
static struct irq_chip gru_chip[GRU_CHIPLETS_PER_BLADE] = {
[0 ... GRU_CHIPLETS_PER_BLADE - 1] {
- .mask = gru_noop,
- .unmask = gru_noop,
- .ack = gru_noop
+ .irq_mask = gru_noop,
+ .irq_unmask = gru_noop,
+ .irq_ack = gru_noop
}
};
diff --git a/drivers/misc/sgi-gru/grumain.c b/drivers/misc/sgi-gru/grumain.c
index f8538bbd0bf..ae16c8cb4f3 100644
--- a/drivers/misc/sgi-gru/grumain.c
+++ b/drivers/misc/sgi-gru/grumain.c
@@ -28,6 +28,7 @@
#include <linux/device.h>
#include <linux/list.h>
#include <linux/err.h>
+#include <linux/prefetch.h>
#include <asm/uv/uv_hub.h>
#include "gru.h"
#include "grutables.h"
diff --git a/drivers/misc/ti-st/Kconfig b/drivers/misc/ti-st/Kconfig
index 2c8c3f39710..abb5de1afce 100644
--- a/drivers/misc/ti-st/Kconfig
+++ b/drivers/misc/ti-st/Kconfig
@@ -5,7 +5,7 @@
menu "Texas Instruments shared transport line discipline"
config TI_ST
tristate "Shared transport core driver"
- depends on RFKILL
+ depends on NET && GPIOLIB
select FW_LOADER
help
This enables the shared transport core driver for TI
diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c
index 486117f72c9..f91f82eabda 100644
--- a/drivers/misc/ti-st/st_core.c
+++ b/drivers/misc/ti-st/st_core.c
@@ -43,13 +43,15 @@ static void add_channel_to_table(struct st_data_s *st_gdata,
pr_info("%s: id %d\n", __func__, new_proto->chnl_id);
/* list now has the channel id as index itself */
st_gdata->list[new_proto->chnl_id] = new_proto;
+ st_gdata->is_registered[new_proto->chnl_id] = true;
}
static void remove_channel_from_table(struct st_data_s *st_gdata,
struct st_proto_s *proto)
{
pr_info("%s: id %d\n", __func__, proto->chnl_id);
- st_gdata->list[proto->chnl_id] = NULL;
+/* st_gdata->list[proto->chnl_id] = NULL; */
+ st_gdata->is_registered[proto->chnl_id] = false;
}
/*
@@ -104,7 +106,7 @@ void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata)
if (unlikely
(st_gdata == NULL || st_gdata->rx_skb == NULL
- || st_gdata->list[chnl_id] == NULL)) {
+ || st_gdata->is_registered[chnl_id] == false)) {
pr_err("chnl_id %d not registered, no data to send?",
chnl_id);
kfree_skb(st_gdata->rx_skb);
@@ -141,14 +143,15 @@ void st_reg_complete(struct st_data_s *st_gdata, char err)
unsigned char i = 0;
pr_info(" %s ", __func__);
for (i = 0; i < ST_MAX_CHANNELS; i++) {
- if (likely(st_gdata != NULL && st_gdata->list[i] != NULL &&
- st_gdata->list[i]->reg_complete_cb != NULL)) {
+ if (likely(st_gdata != NULL &&
+ st_gdata->is_registered[i] == true &&
+ st_gdata->list[i]->reg_complete_cb != NULL)) {
st_gdata->list[i]->reg_complete_cb
(st_gdata->list[i]->priv_data, err);
pr_info("protocol %d's cb sent %d\n", i, err);
if (err) { /* cleanup registered protocol */
st_gdata->protos_registered--;
- st_gdata->list[i] = NULL;
+ st_gdata->is_registered[i] = false;
}
}
}
@@ -475,9 +478,9 @@ void kim_st_list_protocols(struct st_data_s *st_gdata, void *buf)
{
seq_printf(buf, "[%d]\nBT=%c\nFM=%c\nGPS=%c\n",
st_gdata->protos_registered,
- st_gdata->list[0x04] != NULL ? 'R' : 'U',
- st_gdata->list[0x08] != NULL ? 'R' : 'U',
- st_gdata->list[0x09] != NULL ? 'R' : 'U');
+ st_gdata->is_registered[0x04] == true ? 'R' : 'U',
+ st_gdata->is_registered[0x08] == true ? 'R' : 'U',
+ st_gdata->is_registered[0x09] == true ? 'R' : 'U');
}
/********************************************************************/
@@ -504,7 +507,7 @@ long st_register(struct st_proto_s *new_proto)
return -EPROTONOSUPPORT;
}
- if (st_gdata->list[new_proto->chnl_id] != NULL) {
+ if (st_gdata->is_registered[new_proto->chnl_id] == true) {
pr_err("chnl_id %d already registered", new_proto->chnl_id);
return -EALREADY;
}
@@ -563,7 +566,7 @@ long st_register(struct st_proto_s *new_proto)
/* check for already registered once more,
* since the above check is old
*/
- if (st_gdata->list[new_proto->chnl_id] != NULL) {
+ if (st_gdata->is_registered[new_proto->chnl_id] == true) {
pr_err(" proto %d already registered ",
new_proto->chnl_id);
return -EALREADY;
diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c
index b4488c8f6b2..5da93ee6f6b 100644
--- a/drivers/misc/ti-st/st_kim.c
+++ b/drivers/misc/ti-st/st_kim.c
@@ -30,6 +30,7 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/sched.h>
+#include <linux/sysfs.h>
#include <linux/tty.h>
#include <linux/skbuff.h>
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 63667a8f140..d6d62fd07ee 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -284,6 +284,7 @@ int mmc_add_card(struct mmc_card *card)
type = "SD-combo";
if (mmc_card_blockaddr(card))
type = "SDHC-combo";
+ break;
default:
type = "?";
break;
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 2e032f0e8cf..a6c32904014 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -832,7 +832,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
- if (end_command)
+ if (end_command && host->cmd)
mmc_omap_cmd_done(host, host->cmd);
if (host->data != NULL) {
if (transfer_error)
diff --git a/drivers/mmc/host/sdhci-of-core.c b/drivers/mmc/host/sdhci-of-core.c
index f9b611fc773..60e4186a434 100644
--- a/drivers/mmc/host/sdhci-of-core.c
+++ b/drivers/mmc/host/sdhci-of-core.c
@@ -124,8 +124,10 @@ static bool __devinit sdhci_of_wp_inverted(struct device_node *np)
#endif
}
+static const struct of_device_id sdhci_of_match[];
static int __devinit sdhci_of_probe(struct platform_device *ofdev)
{
+ const struct of_device_id *match;
struct device_node *np = ofdev->dev.of_node;
struct sdhci_of_data *sdhci_of_data;
struct sdhci_host *host;
@@ -134,9 +136,10 @@ static int __devinit sdhci_of_probe(struct platform_device *ofdev)
int size;
int ret;
- if (!ofdev->dev.of_match)
+ match = of_match_device(sdhci_of_match, &ofdev->dev);
+ if (!match)
return -EINVAL;
- sdhci_of_data = ofdev->dev.of_match->data;
+ sdhci_of_data = match->data;
if (!of_device_is_available(np))
return -ENODEV;
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index a136be70634..f8b5f37007b 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -957,6 +957,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
host->ioaddr = pci_ioremap_bar(pdev, bar);
if (!host->ioaddr) {
dev_err(&pdev->dev, "failed to remap registers\n");
+ ret = -ENOMEM;
goto release;
}
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 9e15f41f87b..5d20661bc35 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1334,6 +1334,13 @@ static void sdhci_tasklet_finish(unsigned long param)
host = (struct sdhci_host*)param;
+ /*
+ * If this tasklet gets rescheduled while running, it will
+ * be run again afterwards but without any active request.
+ */
+ if (!host->mrq)
+ return;
+
spin_lock_irqsave(&host->lock, flags);
del_timer(&host->timer);
@@ -1345,7 +1352,7 @@ static void sdhci_tasklet_finish(unsigned long param)
* upon error conditions.
*/
if (!(host->flags & SDHCI_DEVICE_DEAD) &&
- (mrq->cmd->error ||
+ ((mrq->cmd && mrq->cmd->error) ||
(mrq->data && (mrq->data->error ||
(mrq->data->stop && mrq->data->stop->error))) ||
(host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) {
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index 62d37de6de7..710339a85c8 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -728,15 +728,15 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
tmio_mmc_set_clock(host, ios->clock);
/* Power sequence - OFF -> UP -> ON */
- if (ios->power_mode == MMC_POWER_OFF || !ios->clock) {
+ if (ios->power_mode == MMC_POWER_UP) {
+ /* power up SD bus */
+ if (host->set_pwr)
+ host->set_pwr(host->pdev, 1);
+ } else if (ios->power_mode == MMC_POWER_OFF || !ios->clock) {
/* power down SD bus */
if (ios->power_mode == MMC_POWER_OFF && host->set_pwr)
host->set_pwr(host->pdev, 0);
tmio_mmc_clk_stop(host);
- } else if (ios->power_mode == MMC_POWER_UP) {
- /* power up SD bus */
- if (host->set_pwr)
- host->set_pwr(host->pdev, 1);
} else {
/* start bus clock */
tmio_mmc_clk_start(host);
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 44b1f46458c..5069111c81c 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -260,6 +260,13 @@ config MTD_BCM963XX
Support for parsing CFE image tag and creating MTD partitions on
Broadcom BCM63xx boards.
+config MTD_LANTIQ
+ tristate "Lantiq SoC NOR support"
+ depends on LANTIQ
+ select MTD_PARTITIONS
+ help
+ Support for NOR flash attached to the Lantiq SoC's External Bus Unit.
+
config MTD_DILNETPC
tristate "CFI Flash device mapped on DIL/Net PC"
depends on X86 && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index 08533bd5cba..6adf4c9b905 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -60,3 +60,4 @@ obj-$(CONFIG_MTD_VMU) += vmu-flash.o
obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o
obj-$(CONFIG_MTD_BCM963XX) += bcm963xx-flash.o
obj-$(CONFIG_MTD_LATCH_ADDR) += latch-addr-flash.o
+obj-$(CONFIG_MTD_LANTIQ) += lantiq-flash.o
diff --git a/drivers/mtd/maps/lantiq-flash.c b/drivers/mtd/maps/lantiq-flash.c
new file mode 100644
index 00000000000..a90cabd7b84
--- /dev/null
+++ b/drivers/mtd/maps/lantiq-flash.c
@@ -0,0 +1,251 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2004 Liu Peng Infineon IFAP DC COM CPE
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/cfi.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/physmap.h>
+
+#include <lantiq_soc.h>
+#include <lantiq_platform.h>
+
+/*
+ * The NOR flash is connected to the same external bus unit (EBU) as PCI.
+ * To make PCI work we need to enable the endianness swapping for the address
+ * written to the EBU. This endianness swapping works for PCI correctly but
+ * fails for attached NOR devices. To workaround this we need to use a complex
+ * map. The workaround involves swapping all addresses whilst probing the chip.
+ * Once probing is complete we stop swapping the addresses but swizzle the
+ * unlock addresses to ensure that access to the NOR device works correctly.
+ */
+
+enum {
+ LTQ_NOR_PROBING,
+ LTQ_NOR_NORMAL
+};
+
+struct ltq_mtd {
+ struct resource *res;
+ struct mtd_info *mtd;
+ struct map_info *map;
+};
+
+static char ltq_map_name[] = "ltq_nor";
+
+static map_word
+ltq_read16(struct map_info *map, unsigned long adr)
+{
+ unsigned long flags;
+ map_word temp;
+
+ if (map->map_priv_1 == LTQ_NOR_PROBING)
+ adr ^= 2;
+ spin_lock_irqsave(&ebu_lock, flags);
+ temp.x[0] = *(u16 *)(map->virt + adr);
+ spin_unlock_irqrestore(&ebu_lock, flags);
+ return temp;
+}
+
+static void
+ltq_write16(struct map_info *map, map_word d, unsigned long adr)
+{
+ unsigned long flags;
+
+ if (map->map_priv_1 == LTQ_NOR_PROBING)
+ adr ^= 2;
+ spin_lock_irqsave(&ebu_lock, flags);
+ *(u16 *)(map->virt + adr) = d.x[0];
+ spin_unlock_irqrestore(&ebu_lock, flags);
+}
+
+/*
+ * The following 2 functions copy data between iomem and a cached memory
+ * section. As memcpy() makes use of pre-fetching we cannot use it here.
+ * The normal alternative of using memcpy_{to,from}io also makes use of
+ * memcpy() on MIPS so it is not applicable either. We are therefore stuck
+ * with having to use our own loop.
+ */
+static void
+ltq_copy_from(struct map_info *map, void *to,
+ unsigned long from, ssize_t len)
+{
+ unsigned char *f = (unsigned char *)map->virt + from;
+ unsigned char *t = (unsigned char *)to;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ebu_lock, flags);
+ while (len--)
+ *t++ = *f++;
+ spin_unlock_irqrestore(&ebu_lock, flags);
+}
+
+static void
+ltq_copy_to(struct map_info *map, unsigned long to,
+ const void *from, ssize_t len)
+{
+ unsigned char *f = (unsigned char *)from;
+ unsigned char *t = (unsigned char *)map->virt + to;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ebu_lock, flags);
+ while (len--)
+ *t++ = *f++;
+ spin_unlock_irqrestore(&ebu_lock, flags);
+}
+
+static const char const *part_probe_types[] = { "cmdlinepart", NULL };
+
+static int __init
+ltq_mtd_probe(struct platform_device *pdev)
+{
+ struct physmap_flash_data *ltq_mtd_data = dev_get_platdata(&pdev->dev);
+ struct ltq_mtd *ltq_mtd;
+ struct mtd_partition *parts;
+ struct resource *res;
+ int nr_parts = 0;
+ struct cfi_private *cfi;
+ int err;
+
+ ltq_mtd = kzalloc(sizeof(struct ltq_mtd), GFP_KERNEL);
+ platform_set_drvdata(pdev, ltq_mtd);
+
+ ltq_mtd->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!ltq_mtd->res) {
+ dev_err(&pdev->dev, "failed to get memory resource");
+ err = -ENOENT;
+ goto err_out;
+ }
+
+ res = devm_request_mem_region(&pdev->dev, ltq_mtd->res->start,
+ resource_size(ltq_mtd->res), dev_name(&pdev->dev));
+ if (!ltq_mtd->res) {
+ dev_err(&pdev->dev, "failed to request mem resource");
+ err = -EBUSY;
+ goto err_out;
+ }
+
+ ltq_mtd->map = kzalloc(sizeof(struct map_info), GFP_KERNEL);
+ ltq_mtd->map->phys = res->start;
+ ltq_mtd->map->size = resource_size(res);
+ ltq_mtd->map->virt = devm_ioremap_nocache(&pdev->dev,
+ ltq_mtd->map->phys, ltq_mtd->map->size);
+ if (!ltq_mtd->map->virt) {
+ dev_err(&pdev->dev, "failed to ioremap!\n");
+ err = -ENOMEM;
+ goto err_free;
+ }
+
+ ltq_mtd->map->name = ltq_map_name;
+ ltq_mtd->map->bankwidth = 2;
+ ltq_mtd->map->read = ltq_read16;
+ ltq_mtd->map->write = ltq_write16;
+ ltq_mtd->map->copy_from = ltq_copy_from;
+ ltq_mtd->map->copy_to = ltq_copy_to;
+
+ ltq_mtd->map->map_priv_1 = LTQ_NOR_PROBING;
+ ltq_mtd->mtd = do_map_probe("cfi_probe", ltq_mtd->map);
+ ltq_mtd->map->map_priv_1 = LTQ_NOR_NORMAL;
+
+ if (!ltq_mtd->mtd) {
+ dev_err(&pdev->dev, "probing failed\n");
+ err = -ENXIO;
+ goto err_unmap;
+ }
+
+ ltq_mtd->mtd->owner = THIS_MODULE;
+
+ cfi = ltq_mtd->map->fldrv_priv;
+ cfi->addr_unlock1 ^= 1;
+ cfi->addr_unlock2 ^= 1;
+
+ nr_parts = parse_mtd_partitions(ltq_mtd->mtd,
+ part_probe_types, &parts, 0);
+ if (nr_parts > 0) {
+ dev_info(&pdev->dev,
+ "using %d partitions from cmdline", nr_parts);
+ } else {
+ nr_parts = ltq_mtd_data->nr_parts;
+ parts = ltq_mtd_data->parts;
+ }
+
+ err = add_mtd_partitions(ltq_mtd->mtd, parts, nr_parts);
+ if (err) {
+ dev_err(&pdev->dev, "failed to add partitions\n");
+ goto err_destroy;
+ }
+
+ return 0;
+
+err_destroy:
+ map_destroy(ltq_mtd->mtd);
+err_unmap:
+ iounmap(ltq_mtd->map->virt);
+err_free:
+ kfree(ltq_mtd->map);
+err_out:
+ kfree(ltq_mtd);
+ return err;
+}
+
+static int __devexit
+ltq_mtd_remove(struct platform_device *pdev)
+{
+ struct ltq_mtd *ltq_mtd = platform_get_drvdata(pdev);
+
+ if (ltq_mtd) {
+ if (ltq_mtd->mtd) {
+ del_mtd_partitions(ltq_mtd->mtd);
+ map_destroy(ltq_mtd->mtd);
+ }
+ if (ltq_mtd->map->virt)
+ iounmap(ltq_mtd->map->virt);
+ kfree(ltq_mtd->map);
+ kfree(ltq_mtd);
+ }
+ return 0;
+}
+
+static struct platform_driver ltq_mtd_driver = {
+ .remove = __devexit_p(ltq_mtd_remove),
+ .driver = {
+ .name = "ltq_nor",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init
+init_ltq_mtd(void)
+{
+ int ret = platform_driver_probe(&ltq_mtd_driver, ltq_mtd_probe);
+
+ if (ret)
+ pr_err("ltq_nor: error registering platform driver");
+ return ret;
+}
+
+static void __exit
+exit_ltq_mtd(void)
+{
+ platform_driver_unregister(&ltq_mtd_driver);
+}
+
+module_init(init_ltq_mtd);
+module_exit(exit_ltq_mtd);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
+MODULE_DESCRIPTION("Lantiq SoC NOR");
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
index bd483f0c57e..c1d33464aee 100644
--- a/drivers/mtd/maps/physmap_of.c
+++ b/drivers/mtd/maps/physmap_of.c
@@ -214,11 +214,13 @@ static void __devinit of_free_probes(const char **probes)
}
#endif
+static struct of_device_id of_flash_match[];
static int __devinit of_flash_probe(struct platform_device *dev)
{
#ifdef CONFIG_MTD_PARTITIONS
const char **part_probe_types;
#endif
+ const struct of_device_id *match;
struct device_node *dp = dev->dev.of_node;
struct resource res;
struct of_flash *info;
@@ -232,9 +234,10 @@ static int __devinit of_flash_probe(struct platform_device *dev)
struct mtd_info **mtd_list = NULL;
resource_size_t res_size;
- if (!dev->dev.of_match)
+ match = of_match_device(of_flash_match, &dev->dev);
+ if (!match)
return -EINVAL;
- probe_type = dev->dev.of_match->data;
+ probe_type = match->data;
reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32);
diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c
index 3ffe05db492..5d513b54a7d 100644
--- a/drivers/mtd/nand/au1550nd.c
+++ b/drivers/mtd/nand/au1550nd.c
@@ -10,6 +10,7 @@
*/
#include <linux/slab.h>
+#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
@@ -470,7 +471,7 @@ static int __init au1xxx_nand_init(void)
#ifdef CONFIG_MIPS_PB1550
/* set gpio206 high */
- au_writel(au_readl(GPIO2_DIR) & ~(1 << 6), GPIO2_DIR);
+ gpio_direction_input(206);
boot_swapboot = (au_readl(MEM_STSTAT) & (0x7 << 1)) | ((bcsr_read(BCSR_STATUS) >> 6) & 0x1);
diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c
index 96c0b34ba8d..657b9f4b6f9 100644
--- a/drivers/mtd/nand/diskonchip.c
+++ b/drivers/mtd/nand/diskonchip.c
@@ -400,7 +400,7 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE);
doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
- /* We can't' use dev_ready here, but at least we wait for the
+ /* We can't use dev_ready here, but at least we wait for the
* command to complete
*/
udelay(50);
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 6c884ef1b06..19f04a34783 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2017,6 +2017,13 @@ config FTMAC100
from Faraday. It is used on Faraday A320, Andes AG101 and some
other ARM/NDS32 SoC's.
+config LANTIQ_ETOP
+ tristate "Lantiq SoC ETOP driver"
+ depends on SOC_TYPE_XWAY
+ help
+ Support for the MII0 inside the Lantiq SoC
+
+
source "drivers/net/fs_enet/Kconfig"
source "drivers/net/octeon/Kconfig"
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index e5a7375685a..209fbb70619 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -259,6 +259,7 @@ obj-$(CONFIG_MLX4_CORE) += mlx4/
obj-$(CONFIG_ENC28J60) += enc28j60.o
obj-$(CONFIG_ETHOC) += ethoc.o
obj-$(CONFIG_GRETH) += greth.o
+obj-$(CONFIG_LANTIQ_ETOP) += lantiq_etop.o
obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o
diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c
index 82260ca7032..d7c1bfe4b6e 100644
--- a/drivers/net/acenic.c
+++ b/drivers/net/acenic.c
@@ -68,6 +68,7 @@
#include <linux/sockios.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/prefetch.h>
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
#include <linux/if_vlan.h>
diff --git a/drivers/net/arm/etherh.c b/drivers/net/arm/etherh.c
index e252cd59501..03e217a868d 100644
--- a/drivers/net/arm/etherh.c
+++ b/drivers/net/arm/etherh.c
@@ -527,7 +527,7 @@ static void __init etherh_banner(void)
* Read the ethernet address string from the on board rom.
* This is an ascii string...
*/
-static int __init etherh_addr(char *addr, struct expansion_card *ec)
+static int __devinit etherh_addr(char *addr, struct expansion_card *ec)
{
struct in_chunk_dir cd;
char *s;
@@ -656,7 +656,7 @@ static const struct net_device_ops etherh_netdev_ops = {
static u32 etherh_regoffsets[16];
static u32 etherm_regoffsets[16];
-static int __init
+static int __devinit
etherh_probe(struct expansion_card *ec, const struct ecard_id *id)
{
const struct etherh_data *data = id->data;
diff --git a/drivers/net/atarilance.c b/drivers/net/atarilance.c
index ce0091eb06f..1264d781b55 100644
--- a/drivers/net/atarilance.c
+++ b/drivers/net/atarilance.c
@@ -554,7 +554,7 @@ static unsigned long __init lance_probe1( struct net_device *dev,
memaddr == (unsigned short *)0xffe00000) {
/* PAMs card and Riebl on ST use level 5 autovector */
if (request_irq(IRQ_AUTO_5, lance_interrupt, IRQ_TYPE_PRIO,
- "PAM/Riebl-ST Ethernet", dev)) {
+ "PAM,Riebl-ST Ethernet", dev)) {
printk( "Lance: request for irq %d failed\n", IRQ_AUTO_5 );
return 0;
}
diff --git a/drivers/net/can/mscan/mpc5xxx_can.c b/drivers/net/can/mscan/mpc5xxx_can.c
index bd1d811c204..5fedc337556 100644
--- a/drivers/net/can/mscan/mpc5xxx_can.c
+++ b/drivers/net/can/mscan/mpc5xxx_can.c
@@ -247,8 +247,10 @@ static u32 __devinit mpc512x_can_get_clock(struct platform_device *ofdev,
}
#endif /* CONFIG_PPC_MPC512x */
+static struct of_device_id mpc5xxx_can_table[];
static int __devinit mpc5xxx_can_probe(struct platform_device *ofdev)
{
+ const struct of_device_id *match;
struct mpc5xxx_can_data *data;
struct device_node *np = ofdev->dev.of_node;
struct net_device *dev;
@@ -258,9 +260,10 @@ static int __devinit mpc5xxx_can_probe(struct platform_device *ofdev)
int irq, mscan_clksrc = 0;
int err = -ENOMEM;
- if (!ofdev->dev.of_match)
+ match = of_match_device(mpc5xxx_can_table, &ofdev->dev);
+ if (!match)
return -EINVAL;
- data = (struct mpc5xxx_can_data *)ofdev->dev.of_match->data;
+ data = match->data;
base = of_iomap(np, 0);
if (!base) {
diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c
index ba763e0481e..6a0a8fca62b 100644
--- a/drivers/net/ehea/ehea_main.c
+++ b/drivers/net/ehea/ehea_main.c
@@ -41,6 +41,7 @@
#include <linux/memory.h>
#include <asm/kexec.h>
#include <linux/mutex.h>
+#include <linux/prefetch.h>
#include <net/ip.h>
diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c
index a9388944f1d..21abb5c01a5 100644
--- a/drivers/net/fs_enet/fs_enet-main.c
+++ b/drivers/net/fs_enet/fs_enet-main.c
@@ -996,8 +996,10 @@ static const struct net_device_ops fs_enet_netdev_ops = {
#endif
};
+static struct of_device_id fs_enet_match[];
static int __devinit fs_enet_probe(struct platform_device *ofdev)
{
+ const struct of_device_id *match;
struct net_device *ndev;
struct fs_enet_private *fep;
struct fs_platform_info *fpi;
@@ -1005,14 +1007,15 @@ static int __devinit fs_enet_probe(struct platform_device *ofdev)
const u8 *mac_addr;
int privsize, len, ret = -ENODEV;
- if (!ofdev->dev.of_match)
+ match = of_match_device(fs_enet_match, &ofdev->dev);
+ if (!match)
return -EINVAL;
fpi = kzalloc(sizeof(*fpi), GFP_KERNEL);
if (!fpi)
return -ENOMEM;
- if (!IS_FEC(ofdev->dev.of_match)) {
+ if (!IS_FEC(match)) {
data = of_get_property(ofdev->dev.of_node, "fsl,cpm-command", &len);
if (!data || len != 4)
goto out_free_fpi;
@@ -1047,7 +1050,7 @@ static int __devinit fs_enet_probe(struct platform_device *ofdev)
fep->dev = &ofdev->dev;
fep->ndev = ndev;
fep->fpi = fpi;
- fep->ops = ofdev->dev.of_match->data;
+ fep->ops = match->data;
ret = fep->ops->setup_data(ndev);
if (ret)
diff --git a/drivers/net/fs_enet/mii-fec.c b/drivers/net/fs_enet/mii-fec.c
index 7e840d373ab..6a2e150e75b 100644
--- a/drivers/net/fs_enet/mii-fec.c
+++ b/drivers/net/fs_enet/mii-fec.c
@@ -101,17 +101,20 @@ static int fs_enet_fec_mii_reset(struct mii_bus *bus)
return 0;
}
+static struct of_device_id fs_enet_mdio_fec_match[];
static int __devinit fs_enet_mdio_probe(struct platform_device *ofdev)
{
+ const struct of_device_id *match;
struct resource res;
struct mii_bus *new_bus;
struct fec_info *fec;
int (*get_bus_freq)(struct device_node *);
int ret = -ENOMEM, clock, speed;
- if (!ofdev->dev.of_match)
+ match = of_match_device(fs_enet_mdio_fec_match, &ofdev->dev);
+ if (!match)
return -EINVAL;
- get_bus_freq = ofdev->dev.of_match->data;
+ get_bus_freq = match->data;
new_bus = mdiobus_alloc();
if (!new_bus)
diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c
index 2dce3d03818..fa01b0b03b7 100644
--- a/drivers/net/ixgbe/ixgbe_main.c
+++ b/drivers/net/ixgbe/ixgbe_main.c
@@ -5136,11 +5136,6 @@ err_set_interrupt:
return err;
}
-static void ring_free_rcu(struct rcu_head *head)
-{
- kfree(container_of(head, struct ixgbe_ring, rcu));
-}
-
/**
* ixgbe_clear_interrupt_scheme - Clear the current interrupt scheme settings
* @adapter: board private structure to clear interrupt scheme on
@@ -5162,7 +5157,7 @@ void ixgbe_clear_interrupt_scheme(struct ixgbe_adapter *adapter)
/* ixgbe_get_stats64() might access this ring, we must wait
* a grace period before freeing it.
*/
- call_rcu(&ring->rcu, ring_free_rcu);
+ kfree_rcu(ring, rcu);
adapter->rx_ring[i] = NULL;
}
diff --git a/drivers/net/lantiq_etop.c b/drivers/net/lantiq_etop.c
new file mode 100644
index 00000000000..45f252b7da3
--- /dev/null
+++ b/drivers/net/lantiq_etop.c
@@ -0,0 +1,805 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2011 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/uaccess.h>
+#include <linux/in.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/phy.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/mm.h>
+#include <linux/platform_device.h>
+#include <linux/ethtool.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include <asm/checksum.h>
+
+#include <lantiq_soc.h>
+#include <xway_dma.h>
+#include <lantiq_platform.h>
+
+#define LTQ_ETOP_MDIO 0x11804
+#define MDIO_REQUEST 0x80000000
+#define MDIO_READ 0x40000000
+#define MDIO_ADDR_MASK 0x1f
+#define MDIO_ADDR_OFFSET 0x15
+#define MDIO_REG_MASK 0x1f
+#define MDIO_REG_OFFSET 0x10
+#define MDIO_VAL_MASK 0xffff
+
+#define PPE32_CGEN 0x800
+#define LQ_PPE32_ENET_MAC_CFG 0x1840
+
+#define LTQ_ETOP_ENETS0 0x11850
+#define LTQ_ETOP_MAC_DA0 0x1186C
+#define LTQ_ETOP_MAC_DA1 0x11870
+#define LTQ_ETOP_CFG 0x16020
+#define LTQ_ETOP_IGPLEN 0x16080
+
+#define MAX_DMA_CHAN 0x8
+#define MAX_DMA_CRC_LEN 0x4
+#define MAX_DMA_DATA_LEN 0x600
+
+#define ETOP_FTCU BIT(28)
+#define ETOP_MII_MASK 0xf
+#define ETOP_MII_NORMAL 0xd
+#define ETOP_MII_REVERSE 0xe
+#define ETOP_PLEN_UNDER 0x40
+#define ETOP_CGEN 0x800
+
+/* use 2 static channels for TX/RX */
+#define LTQ_ETOP_TX_CHANNEL 1
+#define LTQ_ETOP_RX_CHANNEL 6
+#define IS_TX(x) (x == LTQ_ETOP_TX_CHANNEL)
+#define IS_RX(x) (x == LTQ_ETOP_RX_CHANNEL)
+
+#define ltq_etop_r32(x) ltq_r32(ltq_etop_membase + (x))
+#define ltq_etop_w32(x, y) ltq_w32(x, ltq_etop_membase + (y))
+#define ltq_etop_w32_mask(x, y, z) \
+ ltq_w32_mask(x, y, ltq_etop_membase + (z))
+
+#define DRV_VERSION "1.0"
+
+static void __iomem *ltq_etop_membase;
+
+struct ltq_etop_chan {
+ int idx;
+ int tx_free;
+ struct net_device *netdev;
+ struct napi_struct napi;
+ struct ltq_dma_channel dma;
+ struct sk_buff *skb[LTQ_DESC_NUM];
+};
+
+struct ltq_etop_priv {
+ struct net_device *netdev;
+ struct ltq_eth_data *pldata;
+ struct resource *res;
+
+ struct mii_bus *mii_bus;
+ struct phy_device *phydev;
+
+ struct ltq_etop_chan ch[MAX_DMA_CHAN];
+ int tx_free[MAX_DMA_CHAN >> 1];
+
+ spinlock_t lock;
+};
+
+static int
+ltq_etop_alloc_skb(struct ltq_etop_chan *ch)
+{
+ ch->skb[ch->dma.desc] = dev_alloc_skb(MAX_DMA_DATA_LEN);
+ if (!ch->skb[ch->dma.desc])
+ return -ENOMEM;
+ ch->dma.desc_base[ch->dma.desc].addr = dma_map_single(NULL,
+ ch->skb[ch->dma.desc]->data, MAX_DMA_DATA_LEN,
+ DMA_FROM_DEVICE);
+ ch->dma.desc_base[ch->dma.desc].addr =
+ CPHYSADDR(ch->skb[ch->dma.desc]->data);
+ ch->dma.desc_base[ch->dma.desc].ctl =
+ LTQ_DMA_OWN | LTQ_DMA_RX_OFFSET(NET_IP_ALIGN) |
+ MAX_DMA_DATA_LEN;
+ skb_reserve(ch->skb[ch->dma.desc], NET_IP_ALIGN);
+ return 0;
+}
+
+static void
+ltq_etop_hw_receive(struct ltq_etop_chan *ch)
+{
+ struct ltq_etop_priv *priv = netdev_priv(ch->netdev);
+ struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc];
+ struct sk_buff *skb = ch->skb[ch->dma.desc];
+ int len = (desc->ctl & LTQ_DMA_SIZE_MASK) - MAX_DMA_CRC_LEN;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (ltq_etop_alloc_skb(ch)) {
+ netdev_err(ch->netdev,
+ "failed to allocate new rx buffer, stopping DMA\n");
+ ltq_dma_close(&ch->dma);
+ }
+ ch->dma.desc++;
+ ch->dma.desc %= LTQ_DESC_NUM;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ skb_put(skb, len);
+ skb->dev = ch->netdev;
+ skb->protocol = eth_type_trans(skb, ch->netdev);
+ netif_receive_skb(skb);
+}
+
+static int
+ltq_etop_poll_rx(struct napi_struct *napi, int budget)
+{
+ struct ltq_etop_chan *ch = container_of(napi,
+ struct ltq_etop_chan, napi);
+ int rx = 0;
+ int complete = 0;
+
+ while ((rx < budget) && !complete) {
+ struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc];
+
+ if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) == LTQ_DMA_C) {
+ ltq_etop_hw_receive(ch);
+ rx++;
+ } else {
+ complete = 1;
+ }
+ }
+ if (complete || !rx) {
+ napi_complete(&ch->napi);
+ ltq_dma_ack_irq(&ch->dma);
+ }
+ return rx;
+}
+
+static int
+ltq_etop_poll_tx(struct napi_struct *napi, int budget)
+{
+ struct ltq_etop_chan *ch =
+ container_of(napi, struct ltq_etop_chan, napi);
+ struct ltq_etop_priv *priv = netdev_priv(ch->netdev);
+ struct netdev_queue *txq =
+ netdev_get_tx_queue(ch->netdev, ch->idx >> 1);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ while ((ch->dma.desc_base[ch->tx_free].ctl &
+ (LTQ_DMA_OWN | LTQ_DMA_C)) == LTQ_DMA_C) {
+ dev_kfree_skb_any(ch->skb[ch->tx_free]);
+ ch->skb[ch->tx_free] = NULL;
+ memset(&ch->dma.desc_base[ch->tx_free], 0,
+ sizeof(struct ltq_dma_desc));
+ ch->tx_free++;
+ ch->tx_free %= LTQ_DESC_NUM;
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ if (netif_tx_queue_stopped(txq))
+ netif_tx_start_queue(txq);
+ napi_complete(&ch->napi);
+ ltq_dma_ack_irq(&ch->dma);
+ return 1;
+}
+
+static irqreturn_t
+ltq_etop_dma_irq(int irq, void *_priv)
+{
+ struct ltq_etop_priv *priv = _priv;
+ int ch = irq - LTQ_DMA_CH0_INT;
+
+ napi_schedule(&priv->ch[ch].napi);
+ return IRQ_HANDLED;
+}
+
+static void
+ltq_etop_free_channel(struct net_device *dev, struct ltq_etop_chan *ch)
+{
+ struct ltq_etop_priv *priv = netdev_priv(dev);
+
+ ltq_dma_free(&ch->dma);
+ if (ch->dma.irq)
+ free_irq(ch->dma.irq, priv);
+ if (IS_RX(ch->idx)) {
+ int desc;
+ for (desc = 0; desc < LTQ_DESC_NUM; desc++)
+ dev_kfree_skb_any(ch->skb[ch->dma.desc]);
+ }
+}
+
+static void
+ltq_etop_hw_exit(struct net_device *dev)
+{
+ struct ltq_etop_priv *priv = netdev_priv(dev);
+ int i;
+
+ ltq_pmu_disable(PMU_PPE);
+ for (i = 0; i < MAX_DMA_CHAN; i++)
+ if (IS_TX(i) || IS_RX(i))
+ ltq_etop_free_channel(dev, &priv->ch[i]);
+}
+
+static int
+ltq_etop_hw_init(struct net_device *dev)
+{
+ struct ltq_etop_priv *priv = netdev_priv(dev);
+ int i;
+
+ ltq_pmu_enable(PMU_PPE);
+
+ switch (priv->pldata->mii_mode) {
+ case PHY_INTERFACE_MODE_RMII:
+ ltq_etop_w32_mask(ETOP_MII_MASK,
+ ETOP_MII_REVERSE, LTQ_ETOP_CFG);
+ break;
+
+ case PHY_INTERFACE_MODE_MII:
+ ltq_etop_w32_mask(ETOP_MII_MASK,
+ ETOP_MII_NORMAL, LTQ_ETOP_CFG);
+ break;
+
+ default:
+ netdev_err(dev, "unknown mii mode %d\n",
+ priv->pldata->mii_mode);
+ return -ENOTSUPP;
+ }
+
+ /* enable crc generation */
+ ltq_etop_w32(PPE32_CGEN, LQ_PPE32_ENET_MAC_CFG);
+
+ ltq_dma_init_port(DMA_PORT_ETOP);
+
+ for (i = 0; i < MAX_DMA_CHAN; i++) {
+ int irq = LTQ_DMA_CH0_INT + i;
+ struct ltq_etop_chan *ch = &priv->ch[i];
+
+ ch->idx = ch->dma.nr = i;
+
+ if (IS_TX(i)) {
+ ltq_dma_alloc_tx(&ch->dma);
+ request_irq(irq, ltq_etop_dma_irq, IRQF_DISABLED,
+ "etop_tx", priv);
+ } else if (IS_RX(i)) {
+ ltq_dma_alloc_rx(&ch->dma);
+ for (ch->dma.desc = 0; ch->dma.desc < LTQ_DESC_NUM;
+ ch->dma.desc++)
+ if (ltq_etop_alloc_skb(ch))
+ return -ENOMEM;
+ ch->dma.desc = 0;
+ request_irq(irq, ltq_etop_dma_irq, IRQF_DISABLED,
+ "etop_rx", priv);
+ }
+ ch->dma.irq = irq;
+ }
+ return 0;
+}
+
+static void
+ltq_etop_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, "Lantiq ETOP");
+ strcpy(info->bus_info, "internal");
+ strcpy(info->version, DRV_VERSION);
+}
+
+static int
+ltq_etop_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct ltq_etop_priv *priv = netdev_priv(dev);
+
+ return phy_ethtool_gset(priv->phydev, cmd);
+}
+
+static int
+ltq_etop_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct ltq_etop_priv *priv = netdev_priv(dev);
+
+ return phy_ethtool_sset(priv->phydev, cmd);
+}
+
+static int
+ltq_etop_nway_reset(struct net_device *dev)
+{
+ struct ltq_etop_priv *priv = netdev_priv(dev);
+
+ return phy_start_aneg(priv->phydev);
+}
+
+static const struct ethtool_ops ltq_etop_ethtool_ops = {
+ .get_drvinfo = ltq_etop_get_drvinfo,
+ .get_settings = ltq_etop_get_settings,
+ .set_settings = ltq_etop_set_settings,
+ .nway_reset = ltq_etop_nway_reset,
+};
+
+static int
+ltq_etop_mdio_wr(struct mii_bus *bus, int phy_addr, int phy_reg, u16 phy_data)
+{
+ u32 val = MDIO_REQUEST |
+ ((phy_addr & MDIO_ADDR_MASK) << MDIO_ADDR_OFFSET) |
+ ((phy_reg & MDIO_REG_MASK) << MDIO_REG_OFFSET) |
+ phy_data;
+
+ while (ltq_etop_r32(LTQ_ETOP_MDIO) & MDIO_REQUEST)
+ ;
+ ltq_etop_w32(val, LTQ_ETOP_MDIO);
+ return 0;
+}
+
+static int
+ltq_etop_mdio_rd(struct mii_bus *bus, int phy_addr, int phy_reg)
+{
+ u32 val = MDIO_REQUEST | MDIO_READ |
+ ((phy_addr & MDIO_ADDR_MASK) << MDIO_ADDR_OFFSET) |
+ ((phy_reg & MDIO_REG_MASK) << MDIO_REG_OFFSET);
+
+ while (ltq_etop_r32(LTQ_ETOP_MDIO) & MDIO_REQUEST)
+ ;
+ ltq_etop_w32(val, LTQ_ETOP_MDIO);
+ while (ltq_etop_r32(LTQ_ETOP_MDIO) & MDIO_REQUEST)
+ ;
+ val = ltq_etop_r32(LTQ_ETOP_MDIO) & MDIO_VAL_MASK;
+ return val;
+}
+
+static void
+ltq_etop_mdio_link(struct net_device *dev)
+{
+ /* nothing to do */
+}
+
+static int
+ltq_etop_mdio_probe(struct net_device *dev)
+{
+ struct ltq_etop_priv *priv = netdev_priv(dev);
+ struct phy_device *phydev = NULL;
+ int phy_addr;
+
+ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
+ if (priv->mii_bus->phy_map[phy_addr]) {
+ phydev = priv->mii_bus->phy_map[phy_addr];
+ break;
+ }
+ }
+
+ if (!phydev) {
+ netdev_err(dev, "no PHY found\n");
+ return -ENODEV;
+ }
+
+ phydev = phy_connect(dev, dev_name(&phydev->dev), &ltq_etop_mdio_link,
+ 0, priv->pldata->mii_mode);
+
+ if (IS_ERR(phydev)) {
+ netdev_err(dev, "Could not attach to PHY\n");
+ return PTR_ERR(phydev);
+ }
+
+ phydev->supported &= (SUPPORTED_10baseT_Half
+ | SUPPORTED_10baseT_Full
+ | SUPPORTED_100baseT_Half
+ | SUPPORTED_100baseT_Full
+ | SUPPORTED_Autoneg
+ | SUPPORTED_MII
+ | SUPPORTED_TP);
+
+ phydev->advertising = phydev->supported;
+ priv->phydev = phydev;
+ pr_info("%s: attached PHY [%s] (phy_addr=%s, irq=%d)\n",
+ dev->name, phydev->drv->name,
+ dev_name(&phydev->dev), phydev->irq);
+
+ return 0;
+}
+
+static int
+ltq_etop_mdio_init(struct net_device *dev)
+{
+ struct ltq_etop_priv *priv = netdev_priv(dev);
+ int i;
+ int err;
+
+ priv->mii_bus = mdiobus_alloc();
+ if (!priv->mii_bus) {
+ netdev_err(dev, "failed to allocate mii bus\n");
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ priv->mii_bus->priv = dev;
+ priv->mii_bus->read = ltq_etop_mdio_rd;
+ priv->mii_bus->write = ltq_etop_mdio_wr;
+ priv->mii_bus->name = "ltq_mii";
+ snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%x", 0);
+ priv->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+ if (!priv->mii_bus->irq) {
+ err = -ENOMEM;
+ goto err_out_free_mdiobus;
+ }
+
+ for (i = 0; i < PHY_MAX_ADDR; ++i)
+ priv->mii_bus->irq[i] = PHY_POLL;
+
+ if (mdiobus_register(priv->mii_bus)) {
+ err = -ENXIO;
+ goto err_out_free_mdio_irq;
+ }
+
+ if (ltq_etop_mdio_probe(dev)) {
+ err = -ENXIO;
+ goto err_out_unregister_bus;
+ }
+ return 0;
+
+err_out_unregister_bus:
+ mdiobus_unregister(priv->mii_bus);
+err_out_free_mdio_irq:
+ kfree(priv->mii_bus->irq);
+err_out_free_mdiobus:
+ mdiobus_free(priv->mii_bus);
+err_out:
+ return err;
+}
+
+static void
+ltq_etop_mdio_cleanup(struct net_device *dev)
+{
+ struct ltq_etop_priv *priv = netdev_priv(dev);
+
+ phy_disconnect(priv->phydev);
+ mdiobus_unregister(priv->mii_bus);
+ kfree(priv->mii_bus->irq);
+ mdiobus_free(priv->mii_bus);
+}
+
+static int
+ltq_etop_open(struct net_device *dev)
+{
+ struct ltq_etop_priv *priv = netdev_priv(dev);
+ int i;
+
+ for (i = 0; i < MAX_DMA_CHAN; i++) {
+ struct ltq_etop_chan *ch = &priv->ch[i];
+
+ if (!IS_TX(i) && (!IS_RX(i)))
+ continue;
+ ltq_dma_open(&ch->dma);
+ napi_enable(&ch->napi);
+ }
+ phy_start(priv->phydev);
+ netif_tx_start_all_queues(dev);
+ return 0;
+}
+
+static int
+ltq_etop_stop(struct net_device *dev)
+{
+ struct ltq_etop_priv *priv = netdev_priv(dev);
+ int i;
+
+ netif_tx_stop_all_queues(dev);
+ phy_stop(priv->phydev);
+ for (i = 0; i < MAX_DMA_CHAN; i++) {
+ struct ltq_etop_chan *ch = &priv->ch[i];
+
+ if (!IS_RX(i) && !IS_TX(i))
+ continue;
+ napi_disable(&ch->napi);
+ ltq_dma_close(&ch->dma);
+ }
+ return 0;
+}
+
+static int
+ltq_etop_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ int queue = skb_get_queue_mapping(skb);
+ struct netdev_queue *txq = netdev_get_tx_queue(dev, queue);
+ struct ltq_etop_priv *priv = netdev_priv(dev);
+ struct ltq_etop_chan *ch = &priv->ch[(queue << 1) | 1];
+ struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc];
+ int len;
+ unsigned long flags;
+ u32 byte_offset;
+
+ len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
+
+ if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) || ch->skb[ch->dma.desc]) {
+ dev_kfree_skb_any(skb);
+ netdev_err(dev, "tx ring full\n");
+ netif_tx_stop_queue(txq);
+ return NETDEV_TX_BUSY;
+ }
+
+ /* dma needs to start on a 16 byte aligned address */
+ byte_offset = CPHYSADDR(skb->data) % 16;
+ ch->skb[ch->dma.desc] = skb;
+
+ dev->trans_start = jiffies;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ desc->addr = ((unsigned int) dma_map_single(NULL, skb->data, len,
+ DMA_TO_DEVICE)) - byte_offset;
+ wmb();
+ desc->ctl = LTQ_DMA_OWN | LTQ_DMA_SOP | LTQ_DMA_EOP |
+ LTQ_DMA_TX_OFFSET(byte_offset) | (len & LTQ_DMA_SIZE_MASK);
+ ch->dma.desc++;
+ ch->dma.desc %= LTQ_DESC_NUM;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ if (ch->dma.desc_base[ch->dma.desc].ctl & LTQ_DMA_OWN)
+ netif_tx_stop_queue(txq);
+
+ return NETDEV_TX_OK;
+}
+
+static int
+ltq_etop_change_mtu(struct net_device *dev, int new_mtu)
+{
+ int ret = eth_change_mtu(dev, new_mtu);
+
+ if (!ret) {
+ struct ltq_etop_priv *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ ltq_etop_w32((ETOP_PLEN_UNDER << 16) | new_mtu,
+ LTQ_ETOP_IGPLEN);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
+ return ret;
+}
+
+static int
+ltq_etop_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct ltq_etop_priv *priv = netdev_priv(dev);
+
+ /* TODO: mii-toll reports "No MII transceiver present!." ?!*/
+ return phy_mii_ioctl(priv->phydev, rq, cmd);
+}
+
+static int
+ltq_etop_set_mac_address(struct net_device *dev, void *p)
+{
+ int ret = eth_mac_addr(dev, p);
+
+ if (!ret) {
+ struct ltq_etop_priv *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ /* store the mac for the unicast filter */
+ spin_lock_irqsave(&priv->lock, flags);
+ ltq_etop_w32(*((u32 *)dev->dev_addr), LTQ_ETOP_MAC_DA0);
+ ltq_etop_w32(*((u16 *)&dev->dev_addr[4]) << 16,
+ LTQ_ETOP_MAC_DA1);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
+ return ret;
+}
+
+static void
+ltq_etop_set_multicast_list(struct net_device *dev)
+{
+ struct ltq_etop_priv *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ /* ensure that the unicast filter is not enabled in promiscious mode */
+ spin_lock_irqsave(&priv->lock, flags);
+ if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI))
+ ltq_etop_w32_mask(ETOP_FTCU, 0, LTQ_ETOP_ENETS0);
+ else
+ ltq_etop_w32_mask(0, ETOP_FTCU, LTQ_ETOP_ENETS0);
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static u16
+ltq_etop_select_queue(struct net_device *dev, struct sk_buff *skb)
+{
+ /* we are currently only using the first queue */
+ return 0;
+}
+
+static int
+ltq_etop_init(struct net_device *dev)
+{
+ struct ltq_etop_priv *priv = netdev_priv(dev);
+ struct sockaddr mac;
+ int err;
+
+ ether_setup(dev);
+ dev->watchdog_timeo = 10 * HZ;
+ err = ltq_etop_hw_init(dev);
+ if (err)
+ goto err_hw;
+ ltq_etop_change_mtu(dev, 1500);
+
+ memcpy(&mac, &priv->pldata->mac, sizeof(struct sockaddr));
+ if (!is_valid_ether_addr(mac.sa_data)) {
+ pr_warn("etop: invalid MAC, using random\n");
+ random_ether_addr(mac.sa_data);
+ }
+
+ err = ltq_etop_set_mac_address(dev, &mac);
+ if (err)
+ goto err_netdev;
+ ltq_etop_set_multicast_list(dev);
+ err = ltq_etop_mdio_init(dev);
+ if (err)
+ goto err_netdev;
+ return 0;
+
+err_netdev:
+ unregister_netdev(dev);
+ free_netdev(dev);
+err_hw:
+ ltq_etop_hw_exit(dev);
+ return err;
+}
+
+static void
+ltq_etop_tx_timeout(struct net_device *dev)
+{
+ int err;
+
+ ltq_etop_hw_exit(dev);
+ err = ltq_etop_hw_init(dev);
+ if (err)
+ goto err_hw;
+ dev->trans_start = jiffies;
+ netif_wake_queue(dev);
+ return;
+
+err_hw:
+ ltq_etop_hw_exit(dev);
+ netdev_err(dev, "failed to restart etop after TX timeout\n");
+}
+
+static const struct net_device_ops ltq_eth_netdev_ops = {
+ .ndo_open = ltq_etop_open,
+ .ndo_stop = ltq_etop_stop,
+ .ndo_start_xmit = ltq_etop_tx,
+ .ndo_change_mtu = ltq_etop_change_mtu,
+ .ndo_do_ioctl = ltq_etop_ioctl,
+ .ndo_set_mac_address = ltq_etop_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_multicast_list = ltq_etop_set_multicast_list,
+ .ndo_select_queue = ltq_etop_select_queue,
+ .ndo_init = ltq_etop_init,
+ .ndo_tx_timeout = ltq_etop_tx_timeout,
+};
+
+static int __init
+ltq_etop_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct ltq_etop_priv *priv;
+ struct resource *res;
+ int err;
+ int i;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to get etop resource\n");
+ err = -ENOENT;
+ goto err_out;
+ }
+
+ res = devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), dev_name(&pdev->dev));
+ if (!res) {
+ dev_err(&pdev->dev, "failed to request etop resource\n");
+ err = -EBUSY;
+ goto err_out;
+ }
+
+ ltq_etop_membase = devm_ioremap_nocache(&pdev->dev,
+ res->start, resource_size(res));
+ if (!ltq_etop_membase) {
+ dev_err(&pdev->dev, "failed to remap etop engine %d\n",
+ pdev->id);
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ dev = alloc_etherdev_mq(sizeof(struct ltq_etop_priv), 4);
+ strcpy(dev->name, "eth%d");
+ dev->netdev_ops = &ltq_eth_netdev_ops;
+ dev->ethtool_ops = &ltq_etop_ethtool_ops;
+ priv = netdev_priv(dev);
+ priv->res = res;
+ priv->pldata = dev_get_platdata(&pdev->dev);
+ priv->netdev = dev;
+ spin_lock_init(&priv->lock);
+
+ for (i = 0; i < MAX_DMA_CHAN; i++) {
+ if (IS_TX(i))
+ netif_napi_add(dev, &priv->ch[i].napi,
+ ltq_etop_poll_tx, 8);
+ else if (IS_RX(i))
+ netif_napi_add(dev, &priv->ch[i].napi,
+ ltq_etop_poll_rx, 32);
+ priv->ch[i].netdev = dev;
+ }
+
+ err = register_netdev(dev);
+ if (err)
+ goto err_free;
+
+ platform_set_drvdata(pdev, dev);
+ return 0;
+
+err_free:
+ kfree(dev);
+err_out:
+ return err;
+}
+
+static int __devexit
+ltq_etop_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+
+ if (dev) {
+ netif_tx_stop_all_queues(dev);
+ ltq_etop_hw_exit(dev);
+ ltq_etop_mdio_cleanup(dev);
+ unregister_netdev(dev);
+ }
+ return 0;
+}
+
+static struct platform_driver ltq_mii_driver = {
+ .remove = __devexit_p(ltq_etop_remove),
+ .driver = {
+ .name = "ltq_etop",
+ .owner = THIS_MODULE,
+ },
+};
+
+int __init
+init_ltq_etop(void)
+{
+ int ret = platform_driver_probe(&ltq_mii_driver, ltq_etop_probe);
+
+ if (ret)
+ pr_err("ltq_etop: Error registering platfom driver!");
+ return ret;
+}
+
+static void __exit
+exit_ltq_etop(void)
+{
+ platform_driver_unregister(&ltq_mii_driver);
+}
+
+module_init(init_ltq_etop);
+module_exit(exit_ltq_etop);
+
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
+MODULE_DESCRIPTION("Lantiq SoC ETOP");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index bbcf80afaf1..d72a70615c0 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -590,21 +590,13 @@ static int macvlan_port_create(struct net_device *dev)
return err;
}
-static void macvlan_port_rcu_free(struct rcu_head *head)
-{
- struct macvlan_port *port;
-
- port = container_of(head, struct macvlan_port, rcu);
- kfree(port);
-}
-
static void macvlan_port_destroy(struct net_device *dev)
{
struct macvlan_port *port = macvlan_port_get(dev);
dev->priv_flags &= ~IFF_MACVLAN_PORT;
netdev_rx_handler_unregister(dev);
- call_rcu(&port->rcu, macvlan_port_rcu_free);
+ kfree_rcu(port, rcu);
}
static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[])
diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c
index d381a0f9ee1..30aad54b1b3 100644
--- a/drivers/net/sunhme.c
+++ b/drivers/net/sunhme.c
@@ -3238,15 +3238,18 @@ static void happy_meal_pci_exit(void)
#endif
#ifdef CONFIG_SBUS
+static const struct of_device_id hme_sbus_match[];
static int __devinit hme_sbus_probe(struct platform_device *op)
{
+ const struct of_device_id *match;
struct device_node *dp = op->dev.of_node;
const char *model = of_get_property(dp, "model", NULL);
int is_qfe;
- if (!op->dev.of_match)
+ match = of_match_device(hme_sbus_match, &op->dev);
+ if (!match)
return -EINVAL;
- is_qfe = (op->dev.of_match->data != NULL);
+ is_qfe = (match->data != NULL);
if (!is_qfe && model && !strcmp(model, "SUNW,sbus-qfe"))
is_qfe = 1;
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 75b0d3cb767..9f689f1da0f 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -56,7 +56,7 @@ EXPORT_SYMBOL_GPL(irq_of_parse_and_map);
* Returns a pointer to the interrupt parent node, or NULL if the interrupt
* parent could not be determined.
*/
-static struct device_node *of_irq_find_parent(struct device_node *child)
+struct device_node *of_irq_find_parent(struct device_node *child)
{
struct device_node *p;
const __be32 *parp;
diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c
index a3755ffc03d..bc8ce48f077 100644
--- a/drivers/parport/parport_pc.c
+++ b/drivers/parport/parport_pc.c
@@ -2550,7 +2550,6 @@ static int __devinit sio_ite_8872_probe(struct pci_dev *pdev, int autoirq,
const struct parport_pc_via_data *via)
{
short inta_addr[6] = { 0x2A0, 0x2C0, 0x220, 0x240, 0x1E0 };
- struct resource *base_res;
u32 ite8872set;
u32 ite8872_lpt, ite8872_lpthi;
u8 ite8872_irq, type;
@@ -2561,8 +2560,7 @@ static int __devinit sio_ite_8872_probe(struct pci_dev *pdev, int autoirq,
/* make sure which one chip */
for (i = 0; i < 5; i++) {
- base_res = request_region(inta_addr[i], 32, "it887x");
- if (base_res) {
+ if (request_region(inta_addr[i], 32, "it887x")) {
int test;
pci_write_config_dword(pdev, 0x60,
0xe5000000 | inta_addr[i]);
@@ -2571,7 +2569,7 @@ static int __devinit sio_ite_8872_probe(struct pci_dev *pdev, int autoirq,
test = inb(inta_addr[i]);
if (test != 0xff)
break;
- release_region(inta_addr[i], 0x8);
+ release_region(inta_addr[i], 32);
}
}
if (i >= 5) {
@@ -2635,7 +2633,7 @@ static int __devinit sio_ite_8872_probe(struct pci_dev *pdev, int autoirq,
/*
* Release the resource so that parport_pc_probe_port can get it.
*/
- release_resource(base_res);
+ release_region(inta_addr[i], 32);
if (parport_pc_probe_port(ite8872_lpt, ite8872_lpthi,
irq, PARPORT_DMA_NONE, &pdev->dev, 0)) {
printk(KERN_INFO
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index c8ff646c0b0..0fa466a91bf 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -88,4 +88,6 @@ config PCI_IOAPIC
depends on HOTPLUG
default y
-select NLS if (DMI || ACPI)
+config PCI_LABEL
+ def_bool y if (DMI || ACPI)
+ select NLS
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 98d61c8e984..c85f744270a 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -56,10 +56,10 @@ obj-$(CONFIG_TILE) += setup-bus.o setup-irq.o
# ACPI Related PCI FW Functions
# ACPI _DSM provided firmware instance and string name
#
-obj-$(CONFIG_ACPI) += pci-acpi.o pci-label.o
+obj-$(CONFIG_ACPI) += pci-acpi.o
# SMBIOS provided firmware instance and labels
-obj-$(CONFIG_DMI) += pci-label.o
+obj-$(CONFIG_PCI_LABEL) += pci-label.o
# Cardbus & CompactPCI use setup-bus
obj-$(CONFIG_HOTPLUG) += setup-bus.o
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c
index 505c1c7075f..6af6b628175 100644
--- a/drivers/pci/intel-iommu.c
+++ b/drivers/pci/intel-iommu.c
@@ -39,6 +39,7 @@
#include <linux/syscore_ops.h>
#include <linux/tboot.h>
#include <linux/dmi.h>
+#include <linux/pci-ats.h>
#include <asm/cacheflush.h>
#include <asm/iommu.h>
#include "pci.h"
@@ -1299,7 +1300,7 @@ static void iommu_detach_domain(struct dmar_domain *domain,
static struct iova_domain reserved_iova_list;
static struct lock_class_key reserved_rbtree_key;
-static void dmar_init_reserved_ranges(void)
+static int dmar_init_reserved_ranges(void)
{
struct pci_dev *pdev = NULL;
struct iova *iova;
@@ -1313,8 +1314,10 @@ static void dmar_init_reserved_ranges(void)
/* IOAPIC ranges shouldn't be accessed by DMA */
iova = reserve_iova(&reserved_iova_list, IOVA_PFN(IOAPIC_RANGE_START),
IOVA_PFN(IOAPIC_RANGE_END));
- if (!iova)
+ if (!iova) {
printk(KERN_ERR "Reserve IOAPIC range failed\n");
+ return -ENODEV;
+ }
/* Reserve all PCI MMIO to avoid peer-to-peer access */
for_each_pci_dev(pdev) {
@@ -1327,11 +1330,13 @@ static void dmar_init_reserved_ranges(void)
iova = reserve_iova(&reserved_iova_list,
IOVA_PFN(r->start),
IOVA_PFN(r->end));
- if (!iova)
+ if (!iova) {
printk(KERN_ERR "Reserve iova failed\n");
+ return -ENODEV;
+ }
}
}
-
+ return 0;
}
static void domain_reserve_special_ranges(struct dmar_domain *domain)
@@ -1835,7 +1840,7 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw)
ret = iommu_attach_domain(domain, iommu);
if (ret) {
- domain_exit(domain);
+ free_domain_mem(domain);
goto error;
}
@@ -2213,7 +2218,7 @@ static int __init iommu_prepare_static_identity_mapping(int hw)
return 0;
}
-int __init init_dmars(void)
+static int __init init_dmars(int force_on)
{
struct dmar_drhd_unit *drhd;
struct dmar_rmrr_unit *rmrr;
@@ -2393,8 +2398,15 @@ int __init init_dmars(void)
* enable translation
*/
for_each_drhd_unit(drhd) {
- if (drhd->ignored)
+ if (drhd->ignored) {
+ /*
+ * we always have to disable PMRs or DMA may fail on
+ * this device
+ */
+ if (force_on)
+ iommu_disable_protect_mem_regions(drhd->iommu);
continue;
+ }
iommu = drhd->iommu;
iommu_flush_write_buffer(iommu);
@@ -3240,9 +3252,15 @@ static int device_notifier(struct notifier_block *nb,
if (!domain)
return 0;
- if (action == BUS_NOTIFY_UNBOUND_DRIVER && !iommu_pass_through)
+ if (action == BUS_NOTIFY_UNBOUND_DRIVER && !iommu_pass_through) {
domain_remove_one_dev_info(domain, pdev);
+ if (!(domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) &&
+ !(domain->flags & DOMAIN_FLAG_STATIC_IDENTITY) &&
+ list_empty(&domain->devices))
+ domain_exit(domain);
+ }
+
return 0;
}
@@ -3277,12 +3295,21 @@ int __init intel_iommu_init(void)
if (no_iommu || dmar_disabled)
return -ENODEV;
- iommu_init_mempool();
- dmar_init_reserved_ranges();
+ if (iommu_init_mempool()) {
+ if (force_on)
+ panic("tboot: Failed to initialize iommu memory\n");
+ return -ENODEV;
+ }
+
+ if (dmar_init_reserved_ranges()) {
+ if (force_on)
+ panic("tboot: Failed to reserve iommu ranges\n");
+ return -ENODEV;
+ }
init_no_remapping_devices();
- ret = init_dmars();
+ ret = init_dmars(force_on);
if (ret) {
if (force_on)
panic("tboot: Failed to initialize DMARs\n");
@@ -3391,6 +3418,11 @@ static void domain_remove_one_dev_info(struct dmar_domain *domain,
domain->iommu_count--;
domain_update_iommu_cap(domain);
spin_unlock_irqrestore(&domain->iommu_lock, tmp_flags);
+
+ spin_lock_irqsave(&iommu->lock, tmp_flags);
+ clear_bit(domain->id, iommu->domain_ids);
+ iommu->domains[domain->id] = NULL;
+ spin_unlock_irqrestore(&iommu->lock, tmp_flags);
}
spin_unlock_irqrestore(&device_domain_lock, flags);
@@ -3607,9 +3639,9 @@ static int intel_iommu_attach_device(struct iommu_domain *domain,
pte = dmar_domain->pgd;
if (dma_pte_present(pte)) {
- free_pgtable_page(dmar_domain->pgd);
dmar_domain->pgd = (struct dma_pte *)
phys_to_virt(dma_pte_addr(pte));
+ free_pgtable_page(pte);
}
dmar_domain->agaw--;
}
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index 553d8ee55c1..42fae477651 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -13,6 +13,7 @@
#include <linux/mutex.h>
#include <linux/string.h>
#include <linux/delay.h>
+#include <linux/pci-ats.h>
#include "pci.h"
#define VIRTFN_ID_LEN 16
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index d86ea8b0113..135df164a4c 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -781,7 +781,7 @@ static int pci_pm_resume(struct device *dev)
#endif /* !CONFIG_SUSPEND */
-#ifdef CONFIG_HIBERNATION
+#ifdef CONFIG_HIBERNATE_CALLBACKS
static int pci_pm_freeze(struct device *dev)
{
@@ -970,7 +970,7 @@ static int pci_pm_restore(struct device *dev)
return error;
}
-#else /* !CONFIG_HIBERNATION */
+#else /* !CONFIG_HIBERNATE_CALLBACKS */
#define pci_pm_freeze NULL
#define pci_pm_freeze_noirq NULL
@@ -981,7 +981,7 @@ static int pci_pm_restore(struct device *dev)
#define pci_pm_restore NULL
#define pci_pm_restore_noirq NULL
-#endif /* !CONFIG_HIBERNATION */
+#endif /* !CONFIG_HIBERNATE_CALLBACKS */
#ifdef CONFIG_PM_RUNTIME
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index a6ec200fe5e..4020025f854 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -250,15 +250,6 @@ struct pci_sriov {
u8 __iomem *mstate; /* VF Migration State Array */
};
-/* Address Translation Service */
-struct pci_ats {
- int pos; /* capability position */
- int stu; /* Smallest Translation Unit */
- int qdep; /* Invalidate Queue Depth */
- int ref_cnt; /* Physical Function reference count */
- unsigned int is_enabled:1; /* Enable bit is set */
-};
-
#ifdef CONFIG_PCI_IOV
extern int pci_iov_init(struct pci_dev *dev);
extern void pci_iov_release(struct pci_dev *dev);
@@ -269,19 +260,6 @@ extern resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev,
extern void pci_restore_iov_state(struct pci_dev *dev);
extern int pci_iov_bus_range(struct pci_bus *bus);
-extern int pci_enable_ats(struct pci_dev *dev, int ps);
-extern void pci_disable_ats(struct pci_dev *dev);
-extern int pci_ats_queue_depth(struct pci_dev *dev);
-/**
- * pci_ats_enabled - query the ATS status
- * @dev: the PCI device
- *
- * Returns 1 if ATS capability is enabled, or 0 if not.
- */
-static inline int pci_ats_enabled(struct pci_dev *dev)
-{
- return dev->ats && dev->ats->is_enabled;
-}
#else
static inline int pci_iov_init(struct pci_dev *dev)
{
@@ -304,21 +282,6 @@ static inline int pci_iov_bus_range(struct pci_bus *bus)
return 0;
}
-static inline int pci_enable_ats(struct pci_dev *dev, int ps)
-{
- return -ENODEV;
-}
-static inline void pci_disable_ats(struct pci_dev *dev)
-{
-}
-static inline int pci_ats_queue_depth(struct pci_dev *dev)
-{
- return -ENODEV;
-}
-static inline int pci_ats_enabled(struct pci_dev *dev)
-{
- return 0;
-}
#endif /* CONFIG_PCI_IOV */
static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index ebf51ad1b71..a806cb321d2 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -579,7 +579,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
}
size0 = calculate_iosize(size, min_size, size1,
resource_size(b_res), 4096);
- size1 = !add_size? size0:
+ size1 = (!add_head || (add_head && !add_size)) ? size0 :
calculate_iosize(size, min_size+add_size, size1,
resource_size(b_res), 4096);
if (!size0 && !size1) {
@@ -677,7 +677,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
align += aligns[order];
}
size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), min_align);
- size1 = !add_size ? size :
+ size1 = (!add_head || (add_head && !add_size)) ? size0 :
calculate_memsize(size, min_size+add_size, 0,
resource_size(b_res), min_align);
if (!size0 && !size1) {
diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c
index fe77e822384..e8c19def1b0 100644
--- a/drivers/pcmcia/pcmcia_resource.c
+++ b/drivers/pcmcia/pcmcia_resource.c
@@ -173,7 +173,7 @@ static int pcmcia_access_config(struct pcmcia_device *p_dev,
c = p_dev->function_config;
if (!(c->state & CONFIG_LOCKED)) {
- dev_dbg(&p_dev->dev, "Configuration isn't't locked\n");
+ dev_dbg(&p_dev->dev, "Configuration isn't locked\n");
mutex_unlock(&s->ops_mutex);
return -EACCES;
}
diff --git a/drivers/pcmcia/pxa2xx_balloon3.c b/drivers/pcmcia/pxa2xx_balloon3.c
index 453c54c9761..4c3e94c0ae8 100644
--- a/drivers/pcmcia/pxa2xx_balloon3.c
+++ b/drivers/pcmcia/pxa2xx_balloon3.c
@@ -25,6 +25,8 @@
#include <mach/balloon3.h>
+#include <asm/mach-types.h>
+
#include "soc_common.h"
/*
@@ -127,6 +129,9 @@ static int __init balloon3_pcmcia_init(void)
{
int ret;
+ if (!machine_is_balloon3())
+ return -ENODEV;
+
balloon3_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!balloon3_pcmcia_device)
return -ENOMEM;
diff --git a/drivers/pcmcia/pxa2xx_trizeps4.c b/drivers/pcmcia/pxa2xx_trizeps4.c
index b7e596620db..b829e655457 100644
--- a/drivers/pcmcia/pxa2xx_trizeps4.c
+++ b/drivers/pcmcia/pxa2xx_trizeps4.c
@@ -69,15 +69,15 @@ static int trizeps_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
for (i = 0; i < ARRAY_SIZE(irqs); i++) {
if (irqs[i].sock != skt->nr)
continue;
- if (gpio_request(IRQ_TO_GPIO(irqs[i].irq), irqs[i].str) < 0) {
+ if (gpio_request(irq_to_gpio(irqs[i].irq), irqs[i].str) < 0) {
pr_err("%s: sock %d unable to request gpio %d\n",
- __func__, skt->nr, IRQ_TO_GPIO(irqs[i].irq));
+ __func__, skt->nr, irq_to_gpio(irqs[i].irq));
ret = -EBUSY;
goto error;
}
- if (gpio_direction_input(IRQ_TO_GPIO(irqs[i].irq)) < 0) {
+ if (gpio_direction_input(irq_to_gpio(irqs[i].irq)) < 0) {
pr_err("%s: sock %d unable to set input gpio %d\n",
- __func__, skt->nr, IRQ_TO_GPIO(irqs[i].irq));
+ __func__, skt->nr, irq_to_gpio(irqs[i].irq));
ret = -EINVAL;
goto error;
}
@@ -86,7 +86,7 @@ static int trizeps_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
error:
for (; i >= 0; i--) {
- gpio_free(IRQ_TO_GPIO(irqs[i].irq));
+ gpio_free(irq_to_gpio(irqs[i].irq));
}
return (ret);
}
@@ -97,7 +97,7 @@ static void trizeps_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
/* free allocated gpio's */
gpio_free(GPIO_PRDY);
for (i = 0; i < ARRAY_SIZE(irqs); i++)
- gpio_free(IRQ_TO_GPIO(irqs[i].irq));
+ gpio_free(irq_to_gpio(irqs[i].irq));
}
static unsigned long trizeps_pcmcia_status[2];
@@ -226,6 +226,9 @@ static int __init trizeps_pcmcia_init(void)
{
int ret;
+ if (!machine_is_trizeps4() && !machine_is_trizeps4wl())
+ return -ENODEV;
+
trizeps_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!trizeps_pcmcia_device)
return -ENOMEM;
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 2ee442c2a5d..0485e394712 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -187,7 +187,8 @@ config MSI_LAPTOP
depends on ACPI
depends on BACKLIGHT_CLASS_DEVICE
depends on RFKILL
- depends on SERIO_I8042
+ depends on INPUT && SERIO_I8042
+ select INPUT_SPARSEKMAP
---help---
This is a driver for laptops built by MSI (MICRO-STAR
INTERNATIONAL):
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index 5ea6c3477d1..ac4e7f83ce6 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -89,7 +89,7 @@ MODULE_LICENSE("GPL");
#define ACERWMID_EVENT_GUID "676AA15E-6A47-4D9F-A2CC-1E6D18D14026"
MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
-MODULE_ALIAS("wmi:6AF4F258-B401-42Fd-BE91-3D4AC2D7C0D3");
+MODULE_ALIAS("wmi:6AF4F258-B401-42FD-BE91-3D4AC2D7C0D3");
MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026");
enum acer_wmi_event_ids {
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index efc776cb0c6..832a3fd7c1c 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -201,8 +201,8 @@ static int asus_wmi_input_init(struct asus_wmi *asus)
if (!asus->inputdev)
return -ENOMEM;
- asus->inputdev->name = asus->driver->input_phys;
- asus->inputdev->phys = asus->driver->input_name;
+ asus->inputdev->name = asus->driver->input_name;
+ asus->inputdev->phys = asus->driver->input_phys;
asus->inputdev->id.bustype = BUS_HOST;
asus->inputdev->dev.parent = &asus->platform_device->dev;
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index 5f2dd386152..2c1abf63957 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -585,8 +585,9 @@ static bool eeepc_wlan_rfkill_blocked(struct eeepc_laptop *eeepc)
return true;
}
-static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc)
+static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
{
+ struct pci_dev *port;
struct pci_dev *dev;
struct pci_bus *bus;
bool blocked = eeepc_wlan_rfkill_blocked(eeepc);
@@ -599,9 +600,16 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc)
mutex_lock(&eeepc->hotplug_lock);
if (eeepc->hotplug_slot) {
- bus = pci_find_bus(0, 1);
+ port = acpi_get_pci_dev(handle);
+ if (!port) {
+ pr_warning("Unable to find port\n");
+ goto out_unlock;
+ }
+
+ bus = port->subordinate;
+
if (!bus) {
- pr_warning("Unable to find PCI bus 1?\n");
+ pr_warning("Unable to find PCI bus?\n");
goto out_unlock;
}
@@ -609,6 +617,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc)
pr_err("Unable to read PCI config space?\n");
goto out_unlock;
}
+
absent = (l == 0xffffffff);
if (blocked != absent) {
@@ -647,6 +656,17 @@ out_unlock:
mutex_unlock(&eeepc->hotplug_lock);
}
+static void eeepc_rfkill_hotplug_update(struct eeepc_laptop *eeepc, char *node)
+{
+ acpi_status status = AE_OK;
+ acpi_handle handle;
+
+ status = acpi_get_handle(NULL, node, &handle);
+
+ if (ACPI_SUCCESS(status))
+ eeepc_rfkill_hotplug(eeepc, handle);
+}
+
static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
{
struct eeepc_laptop *eeepc = data;
@@ -654,7 +674,7 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
if (event != ACPI_NOTIFY_BUS_CHECK)
return;
- eeepc_rfkill_hotplug(eeepc);
+ eeepc_rfkill_hotplug(eeepc, handle);
}
static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc,
@@ -672,6 +692,11 @@ static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc,
eeepc);
if (ACPI_FAILURE(status))
pr_warning("Failed to register notify on %s\n", node);
+ /*
+ * Refresh pci hotplug in case the rfkill state was
+ * changed during setup.
+ */
+ eeepc_rfkill_hotplug(eeepc, handle);
} else
return -ENODEV;
@@ -693,6 +718,12 @@ static void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc,
if (ACPI_FAILURE(status))
pr_err("Error removing rfkill notify handler %s\n",
node);
+ /*
+ * Refresh pci hotplug in case the rfkill
+ * state was changed after
+ * eeepc_unregister_rfkill_notifier()
+ */
+ eeepc_rfkill_hotplug(eeepc, handle);
}
}
@@ -816,11 +847,7 @@ static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc)
rfkill_destroy(eeepc->wlan_rfkill);
eeepc->wlan_rfkill = NULL;
}
- /*
- * Refresh pci hotplug in case the rfkill state was changed after
- * eeepc_unregister_rfkill_notifier()
- */
- eeepc_rfkill_hotplug(eeepc);
+
if (eeepc->hotplug_slot)
pci_hp_deregister(eeepc->hotplug_slot);
@@ -889,11 +916,6 @@ static int eeepc_rfkill_init(struct eeepc_laptop *eeepc)
eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
- /*
- * Refresh pci hotplug in case the rfkill state was changed during
- * setup.
- */
- eeepc_rfkill_hotplug(eeepc);
exit:
if (result && result != -ENODEV)
@@ -928,8 +950,11 @@ static int eeepc_hotk_restore(struct device *device)
struct eeepc_laptop *eeepc = dev_get_drvdata(device);
/* Refresh both wlan rfkill state and pci hotplug */
- if (eeepc->wlan_rfkill)
- eeepc_rfkill_hotplug(eeepc);
+ if (eeepc->wlan_rfkill) {
+ eeepc_rfkill_hotplug_update(eeepc, "\\_SB.PCI0.P0P5");
+ eeepc_rfkill_hotplug_update(eeepc, "\\_SB.PCI0.P0P6");
+ eeepc_rfkill_hotplug_update(eeepc, "\\_SB.PCI0.P0P7");
+ }
if (eeepc->bluetooth_rfkill)
rfkill_set_sw_state(eeepc->bluetooth_rfkill,
diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c
index 0ddc434fb93..649dcadd8ea 100644
--- a/drivers/platform/x86/eeepc-wmi.c
+++ b/drivers/platform/x86/eeepc-wmi.c
@@ -67,9 +67,11 @@ static const struct key_entry eeepc_wmi_keymap[] = {
{ KE_KEY, 0x82, { KEY_CAMERA } },
{ KE_KEY, 0x83, { KEY_CAMERA_ZOOMIN } },
{ KE_KEY, 0x88, { KEY_WLAN } },
+ { KE_KEY, 0xbd, { KEY_CAMERA } },
{ KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */
{ KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */
+ { KE_KEY, 0xe8, { KEY_SCREENLOCK } },
{ KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } },
{ KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } },
{ KE_KEY, 0xec, { KEY_CAMERA_UP } },
diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c
index d653104b59c..464bb3fc4d8 100644
--- a/drivers/platform/x86/intel_pmic_gpio.c
+++ b/drivers/platform/x86/intel_pmic_gpio.c
@@ -74,6 +74,19 @@ struct pmic_gpio {
u32 trigger_type;
};
+static void pmic_program_irqtype(int gpio, int type)
+{
+ if (type & IRQ_TYPE_EDGE_RISING)
+ intel_scu_ipc_update_register(GPIO0 + gpio, 0x20, 0x20);
+ else
+ intel_scu_ipc_update_register(GPIO0 + gpio, 0x00, 0x20);
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ intel_scu_ipc_update_register(GPIO0 + gpio, 0x10, 0x10);
+ else
+ intel_scu_ipc_update_register(GPIO0 + gpio, 0x00, 0x10);
+};
+
static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
if (offset > 8) {
@@ -166,16 +179,38 @@ static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
return pg->irq_base + offset;
}
+static void pmic_bus_lock(struct irq_data *data)
+{
+ struct pmic_gpio *pg = irq_data_get_irq_chip_data(data);
+
+ mutex_lock(&pg->buslock);
+}
+
+static void pmic_bus_sync_unlock(struct irq_data *data)
+{
+ struct pmic_gpio *pg = irq_data_get_irq_chip_data(data);
+
+ if (pg->update_type) {
+ unsigned int gpio = pg->update_type & ~GPIO_UPDATE_TYPE;
+
+ pmic_program_irqtype(gpio, pg->trigger_type);
+ pg->update_type = 0;
+ }
+ mutex_unlock(&pg->buslock);
+}
+
/* the gpiointr register is read-clear, so just do nothing. */
static void pmic_irq_unmask(struct irq_data *data) { }
static void pmic_irq_mask(struct irq_data *data) { }
static struct irq_chip pmic_irqchip = {
- .name = "PMIC-GPIO",
- .irq_mask = pmic_irq_mask,
- .irq_unmask = pmic_irq_unmask,
- .irq_set_type = pmic_irq_type,
+ .name = "PMIC-GPIO",
+ .irq_mask = pmic_irq_mask,
+ .irq_unmask = pmic_irq_unmask,
+ .irq_set_type = pmic_irq_type,
+ .irq_bus_lock = pmic_bus_lock,
+ .irq_bus_sync_unlock = pmic_bus_sync_unlock,
};
static irqreturn_t pmic_irq_handler(int irq, void *data)
diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c
index de434c6dc2d..d347116d150 100644
--- a/drivers/platform/x86/samsung-laptop.c
+++ b/drivers/platform/x86/samsung-laptop.c
@@ -571,6 +571,16 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {
.callback = dmi_check_cb,
},
{
+ .ident = "R410 Plus",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "R410P"),
+ DMI_MATCH(DMI_BOARD_NAME, "R460"),
+ },
+ .callback = dmi_check_cb,
+ },
+ {
.ident = "R518",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR,
@@ -591,12 +601,12 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {
.callback = dmi_check_cb,
},
{
- .ident = "N150/N210/N220",
+ .ident = "N150/N210/N220/N230",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR,
"SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
- DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220/N230"),
+ DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220/N230"),
},
.callback = dmi_check_cb,
},
@@ -771,6 +781,7 @@ static int __init samsung_init(void)
/* create a backlight device to talk to this one */
memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_PLATFORM;
props.max_brightness = sabi_config->max_brightness;
backlight_device = backlight_device_register("samsung", &sdev->dev,
NULL, &backlight_ops,
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index e642f5f2950..6fe8cd6e23b 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -138,6 +138,8 @@ MODULE_PARM_DESC(kbd_backlight_timeout,
"1 for 30 seconds, 2 for 60 seconds and 3 to disable timeout "
"(default: 0)");
+static void sony_nc_kbd_backlight_resume(void);
+
enum sony_nc_rfkill {
SONY_WIFI,
SONY_BLUETOOTH,
@@ -771,11 +773,6 @@ static int sony_nc_handles_setup(struct platform_device *pd)
if (!handles)
return -ENOMEM;
- sysfs_attr_init(&handles->devattr.attr);
- handles->devattr.attr.name = "handles";
- handles->devattr.attr.mode = S_IRUGO;
- handles->devattr.show = sony_nc_handles_show;
-
for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {
if (!acpi_callsetfunc(sony_nc_acpi_handle,
"SN00", i + 0x20, &result)) {
@@ -785,11 +782,18 @@ static int sony_nc_handles_setup(struct platform_device *pd)
}
}
- /* allow reading capabilities via sysfs */
- if (device_create_file(&pd->dev, &handles->devattr)) {
- kfree(handles);
- handles = NULL;
- return -1;
+ if (debug) {
+ sysfs_attr_init(&handles->devattr.attr);
+ handles->devattr.attr.name = "handles";
+ handles->devattr.attr.mode = S_IRUGO;
+ handles->devattr.show = sony_nc_handles_show;
+
+ /* allow reading capabilities via sysfs */
+ if (device_create_file(&pd->dev, &handles->devattr)) {
+ kfree(handles);
+ handles = NULL;
+ return -1;
+ }
}
return 0;
@@ -798,7 +802,8 @@ static int sony_nc_handles_setup(struct platform_device *pd)
static int sony_nc_handles_cleanup(struct platform_device *pd)
{
if (handles) {
- device_remove_file(&pd->dev, &handles->devattr);
+ if (debug)
+ device_remove_file(&pd->dev, &handles->devattr);
kfree(handles);
handles = NULL;
}
@@ -808,6 +813,11 @@ static int sony_nc_handles_cleanup(struct platform_device *pd)
static int sony_find_snc_handle(int handle)
{
int i;
+
+ /* not initialized yet, return early */
+ if (!handles)
+ return -1;
+
for (i = 0; i < 0x10; i++) {
if (handles->cap[i] == handle) {
dprintk("found handle 0x%.4x (offset: 0x%.2x)\n",
@@ -924,6 +934,14 @@ static ssize_t sony_nc_sysfs_store(struct device *dev,
/*
* Backlight device
*/
+struct sony_backlight_props {
+ struct backlight_device *dev;
+ int handle;
+ u8 offset;
+ u8 maxlvl;
+};
+struct sony_backlight_props sony_bl_props;
+
static int sony_backlight_update_status(struct backlight_device *bd)
{
return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
@@ -944,21 +962,26 @@ static int sony_nc_get_brightness_ng(struct backlight_device *bd)
{
int result;
int *handle = (int *)bl_get_data(bd);
+ struct sony_backlight_props *sdev =
+ (struct sony_backlight_props *)bl_get_data(bd);
- sony_call_snc_handle(*handle, 0x0200, &result);
+ sony_call_snc_handle(sdev->handle, 0x0200, &result);
- return result & 0xff;
+ return (result & 0xff) - sdev->offset;
}
static int sony_nc_update_status_ng(struct backlight_device *bd)
{
int value, result;
int *handle = (int *)bl_get_data(bd);
+ struct sony_backlight_props *sdev =
+ (struct sony_backlight_props *)bl_get_data(bd);
- value = bd->props.brightness;
- sony_call_snc_handle(*handle, 0x0100 | (value << 16), &result);
+ value = bd->props.brightness + sdev->offset;
+ if (sony_call_snc_handle(sdev->handle, 0x0100 | (value << 16), &result))
+ return -EIO;
- return sony_nc_get_brightness_ng(bd);
+ return value;
}
static const struct backlight_ops sony_backlight_ops = {
@@ -971,8 +994,6 @@ static const struct backlight_ops sony_backlight_ng_ops = {
.update_status = sony_nc_update_status_ng,
.get_brightness = sony_nc_get_brightness_ng,
};
-static int backlight_ng_handle;
-static struct backlight_device *sony_backlight_device;
/*
* New SNC-only Vaios event mapping to driver known keys
@@ -1168,6 +1189,9 @@ static int sony_nc_resume(struct acpi_device *device)
/* re-read rfkill state */
sony_nc_rfkill_update();
+ /* restore kbd backlight states */
+ sony_nc_kbd_backlight_resume();
+
return 0;
}
@@ -1355,6 +1379,7 @@ out_no_enum:
#define KBDBL_HANDLER 0x137
#define KBDBL_PRESENT 0xB00
#define SET_MODE 0xC00
+#define SET_STATE 0xD00
#define SET_TIMEOUT 0xE00
struct kbd_backlight {
@@ -1377,6 +1402,10 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value)
(value << 0x10) | SET_MODE, &result))
return -EIO;
+ /* Try to turn the light on/off immediately */
+ sony_call_snc_handle(KBDBL_HANDLER, (value << 0x10) | SET_STATE,
+ &result);
+
kbdbl_handle->mode = value;
return 0;
@@ -1458,7 +1487,7 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd)
{
int result;
- if (sony_call_snc_handle(0x137, KBDBL_PRESENT, &result))
+ if (sony_call_snc_handle(KBDBL_HANDLER, KBDBL_PRESENT, &result))
return 0;
if (!(result & 0x02))
return 0;
@@ -1501,13 +1530,105 @@ outkzalloc:
static int sony_nc_kbd_backlight_cleanup(struct platform_device *pd)
{
if (kbdbl_handle) {
+ int result;
+
device_remove_file(&pd->dev, &kbdbl_handle->mode_attr);
device_remove_file(&pd->dev, &kbdbl_handle->timeout_attr);
+
+ /* restore the default hw behaviour */
+ sony_call_snc_handle(KBDBL_HANDLER, 0x1000 | SET_MODE, &result);
+ sony_call_snc_handle(KBDBL_HANDLER, SET_TIMEOUT, &result);
+
kfree(kbdbl_handle);
}
return 0;
}
+static void sony_nc_kbd_backlight_resume(void)
+{
+ int ignore = 0;
+
+ if (!kbdbl_handle)
+ return;
+
+ if (kbdbl_handle->mode == 0)
+ sony_call_snc_handle(KBDBL_HANDLER, SET_MODE, &ignore);
+
+ if (kbdbl_handle->timeout != 0)
+ sony_call_snc_handle(KBDBL_HANDLER,
+ (kbdbl_handle->timeout << 0x10) | SET_TIMEOUT,
+ &ignore);
+}
+
+static void sony_nc_backlight_ng_read_limits(int handle,
+ struct sony_backlight_props *props)
+{
+ int offset;
+ acpi_status status;
+ u8 brlvl, i;
+ u8 min = 0xff, max = 0x00;
+ struct acpi_object_list params;
+ union acpi_object in_obj;
+ union acpi_object *lvl_enum;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+
+ props->handle = handle;
+ props->offset = 0;
+ props->maxlvl = 0xff;
+
+ offset = sony_find_snc_handle(handle);
+ if (offset < 0)
+ return;
+
+ /* try to read the boundaries from ACPI tables, if we fail the above
+ * defaults should be reasonable
+ */
+ params.count = 1;
+ params.pointer = &in_obj;
+ in_obj.type = ACPI_TYPE_INTEGER;
+ in_obj.integer.value = offset;
+ status = acpi_evaluate_object(sony_nc_acpi_handle, "SN06", &params,
+ &buffer);
+ if (ACPI_FAILURE(status))
+ return;
+
+ lvl_enum = (union acpi_object *) buffer.pointer;
+ if (!lvl_enum) {
+ pr_err("No SN06 return object.");
+ return;
+ }
+ if (lvl_enum->type != ACPI_TYPE_BUFFER) {
+ pr_err("Invalid SN06 return object 0x%.2x\n",
+ lvl_enum->type);
+ goto out_invalid;
+ }
+
+ /* the buffer lists brightness levels available, brightness levels are
+ * from 0 to 8 in the array, other values are used by ALS control.
+ */
+ for (i = 0; i < 9 && i < lvl_enum->buffer.length; i++) {
+
+ brlvl = *(lvl_enum->buffer.pointer + i);
+ dprintk("Brightness level: %d\n", brlvl);
+
+ if (!brlvl)
+ break;
+
+ if (brlvl > max)
+ max = brlvl;
+ if (brlvl < min)
+ min = brlvl;
+ }
+ props->offset = min;
+ props->maxlvl = max;
+ dprintk("Brightness levels: min=%d max=%d\n", props->offset,
+ props->maxlvl);
+
+out_invalid:
+ kfree(buffer.pointer);
+ return;
+}
+
static void sony_nc_backlight_setup(void)
{
acpi_handle unused;
@@ -1516,14 +1637,14 @@ static void sony_nc_backlight_setup(void)
struct backlight_properties props;
if (sony_find_snc_handle(0x12f) != -1) {
- backlight_ng_handle = 0x12f;
ops = &sony_backlight_ng_ops;
- max_brightness = 0xff;
+ sony_nc_backlight_ng_read_limits(0x12f, &sony_bl_props);
+ max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
} else if (sony_find_snc_handle(0x137) != -1) {
- backlight_ng_handle = 0x137;
ops = &sony_backlight_ng_ops;
- max_brightness = 0xff;
+ sony_nc_backlight_ng_read_limits(0x137, &sony_bl_props);
+ max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
} else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT",
&unused))) {
@@ -1536,22 +1657,22 @@ static void sony_nc_backlight_setup(void)
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = max_brightness;
- sony_backlight_device = backlight_device_register("sony", NULL,
- &backlight_ng_handle,
- ops, &props);
+ sony_bl_props.dev = backlight_device_register("sony", NULL,
+ &sony_bl_props,
+ ops, &props);
- if (IS_ERR(sony_backlight_device)) {
- pr_warning(DRV_PFX "unable to register backlight device\n");
- sony_backlight_device = NULL;
+ if (IS_ERR(sony_bl_props.dev)) {
+ pr_warn(DRV_PFX "unable to register backlight device\n");
+ sony_bl_props.dev = NULL;
} else
- sony_backlight_device->props.brightness =
- ops->get_brightness(sony_backlight_device);
+ sony_bl_props.dev->props.brightness =
+ ops->get_brightness(sony_bl_props.dev);
}
static void sony_nc_backlight_cleanup(void)
{
- if (sony_backlight_device)
- backlight_device_unregister(sony_backlight_device);
+ if (sony_bl_props.dev)
+ backlight_device_unregister(sony_bl_props.dev);
}
static int sony_nc_add(struct acpi_device *device)
@@ -2549,7 +2670,7 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd,
mutex_lock(&spic_dev.lock);
switch (cmd) {
case SONYPI_IOCGBRT:
- if (sony_backlight_device == NULL) {
+ if (sony_bl_props.dev == NULL) {
ret = -EIO;
break;
}
@@ -2562,7 +2683,7 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd,
ret = -EFAULT;
break;
case SONYPI_IOCSBRT:
- if (sony_backlight_device == NULL) {
+ if (sony_bl_props.dev == NULL) {
ret = -EIO;
break;
}
@@ -2576,8 +2697,8 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd,
break;
}
/* sync the backlight device status */
- sony_backlight_device->props.brightness =
- sony_backlight_get_brightness(sony_backlight_device);
+ sony_bl_props.dev->props.brightness =
+ sony_backlight_get_brightness(sony_bl_props.dev);
break;
case SONYPI_IOCGBAT1CAP:
if (ec_read16(SONYPI_BAT1_FULL, &val16)) {
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index a08561f5349..562fcf0dd2b 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -128,7 +128,8 @@ enum {
};
/* ACPI HIDs */
-#define TPACPI_ACPI_HKEY_HID "IBM0068"
+#define TPACPI_ACPI_IBM_HKEY_HID "IBM0068"
+#define TPACPI_ACPI_LENOVO_HKEY_HID "LEN0068"
#define TPACPI_ACPI_EC_HID "PNP0C09"
/* Input IDs */
@@ -3879,7 +3880,8 @@ errexit:
}
static const struct acpi_device_id ibm_htk_device_ids[] = {
- {TPACPI_ACPI_HKEY_HID, 0},
+ {TPACPI_ACPI_IBM_HKEY_HID, 0},
+ {TPACPI_ACPI_LENOVO_HKEY_HID, 0},
{"", 0},
};
@@ -8618,8 +8620,7 @@ static bool __pure __init tpacpi_is_valid_fw_id(const char* const s,
tpacpi_is_fw_digit(s[1]) &&
s[2] == t && s[3] == 'T' &&
tpacpi_is_fw_digit(s[4]) &&
- tpacpi_is_fw_digit(s[5]) &&
- s[6] == 'W' && s[7] == 'W';
+ tpacpi_is_fw_digit(s[5]);
}
/* returns 0 - probe ok, or < 0 - probe error.
diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c
index c29719cacbc..86c9a091a2f 100644
--- a/drivers/rapidio/rio.c
+++ b/drivers/rapidio/rio.c
@@ -1171,16 +1171,17 @@ static int rio_hdid_setup(char *str)
__setup("riohdid=", rio_hdid_setup);
-void rio_register_mport(struct rio_mport *port)
+int rio_register_mport(struct rio_mport *port)
{
if (next_portid >= RIO_MAX_MPORTS) {
pr_err("RIO: reached specified max number of mports\n");
- return;
+ return 1;
}
port->id = next_portid++;
port->host_deviceid = rio_get_hdid(port->id);
list_add_tail(&port->node, &rio_mports);
+ return 0;
}
EXPORT_SYMBOL_GPL(rio_local_get_device_id);
diff --git a/drivers/rapidio/switches/idt_gen2.c b/drivers/rapidio/switches/idt_gen2.c
index 095016a9dec..043ee3136e4 100644
--- a/drivers/rapidio/switches/idt_gen2.c
+++ b/drivers/rapidio/switches/idt_gen2.c
@@ -95,6 +95,9 @@ idtg2_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
else
table++;
+ if (route_port == RIO_INVALID_ROUTE)
+ route_port = IDT_DEFAULT_ROUTE;
+
rio_mport_write_config_32(mport, destid, hopcount,
LOCAL_RTE_CONF_DESTID_SEL, table);
@@ -411,6 +414,12 @@ static int idtg2_switch_init(struct rio_dev *rdev, int do_enum)
rdev->rswitch->em_handle = idtg2_em_handler;
rdev->rswitch->sw_sysfs = idtg2_sysfs;
+ if (do_enum) {
+ /* Ensure that default routing is disabled on startup */
+ rio_write_config_32(rdev,
+ RIO_STD_RTE_DEFAULT_PORT, IDT_NO_ROUTE);
+ }
+
return 0;
}
@@ -418,3 +427,4 @@ DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS1848, idtg2_switch_init);
DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS1616, idtg2_switch_init);
DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTVPS1616, idtg2_switch_init);
DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTSPS1616, idtg2_switch_init);
+DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS1432, idtg2_switch_init);
diff --git a/drivers/rapidio/switches/idtcps.c b/drivers/rapidio/switches/idtcps.c
index 3a971077e7b..d06ee2d44b4 100644
--- a/drivers/rapidio/switches/idtcps.c
+++ b/drivers/rapidio/switches/idtcps.c
@@ -26,6 +26,9 @@ idtcps_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
{
u32 result;
+ if (route_port == RIO_INVALID_ROUTE)
+ route_port = CPS_DEFAULT_ROUTE;
+
if (table == RIO_GLOBAL_TABLE) {
rio_mport_write_config_32(mport, destid, hopcount,
RIO_STD_RTE_CONF_DESTID_SEL_CSR, route_destid);
@@ -130,6 +133,9 @@ static int idtcps_switch_init(struct rio_dev *rdev, int do_enum)
/* set TVAL = ~50us */
rio_write_config_32(rdev,
rdev->phys_efptr + RIO_PORT_LINKTO_CTL_CSR, 0x8e << 8);
+ /* Ensure that default routing is disabled on startup */
+ rio_write_config_32(rdev,
+ RIO_STD_RTE_DEFAULT_PORT, CPS_NO_ROUTE);
}
return 0;
diff --git a/drivers/rapidio/switches/tsi57x.c b/drivers/rapidio/switches/tsi57x.c
index 1a62934bfeb..db8b8028988 100644
--- a/drivers/rapidio/switches/tsi57x.c
+++ b/drivers/rapidio/switches/tsi57x.c
@@ -303,6 +303,12 @@ static int tsi57x_switch_init(struct rio_dev *rdev, int do_enum)
rdev->rswitch->em_init = tsi57x_em_init;
rdev->rswitch->em_handle = tsi57x_em_handler;
+ if (do_enum) {
+ /* Ensure that default routing is disabled on startup */
+ rio_write_config_32(rdev, RIO_STD_RTE_DEFAULT_PORT,
+ RIO_INVALID_ROUTE);
+ }
+
return 0;
}
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index e1878877399..42891726ea7 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -3,10 +3,10 @@
#
config RTC_LIB
- tristate
+ bool
menuconfig RTC_CLASS
- tristate "Real Time Clock"
+ bool "Real Time Clock"
default n
depends on !S390
select RTC_LIB
@@ -15,9 +15,6 @@ menuconfig RTC_CLASS
be allowed to plug one or more RTCs to your system. You will
probably want to enable one or more of the interfaces below.
- This driver can also be built as a module. If so, the module
- will be called rtc-core.
-
if RTC_CLASS
config RTC_HCTOSYS
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 09b4437b3e6..4194e59e14c 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -41,26 +41,21 @@ static void rtc_device_release(struct device *dev)
* system's wall clock; restore it on resume().
*/
-static struct timespec delta;
static time_t oldtime;
+static struct timespec oldts;
static int rtc_suspend(struct device *dev, pm_message_t mesg)
{
struct rtc_device *rtc = to_rtc_device(dev);
struct rtc_time tm;
- struct timespec ts = current_kernel_time();
if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
return 0;
rtc_read_time(rtc, &tm);
+ ktime_get_ts(&oldts);
rtc_tm_to_time(&tm, &oldtime);
- /* RTC precision is 1 second; adjust delta for avg 1/2 sec err */
- set_normalized_timespec(&delta,
- ts.tv_sec - oldtime,
- ts.tv_nsec - (NSEC_PER_SEC >> 1));
-
return 0;
}
@@ -70,10 +65,12 @@ static int rtc_resume(struct device *dev)
struct rtc_time tm;
time_t newtime;
struct timespec time;
+ struct timespec newts;
if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
return 0;
+ ktime_get_ts(&newts);
rtc_read_time(rtc, &tm);
if (rtc_valid_tm(&tm) != 0) {
pr_debug("%s: bogus resume time\n", dev_name(&rtc->dev));
@@ -85,15 +82,13 @@ static int rtc_resume(struct device *dev)
pr_debug("%s: time travel!\n", dev_name(&rtc->dev));
return 0;
}
+ /* calculate the RTC time delta */
+ set_normalized_timespec(&time, newtime - oldtime, 0);
- /* restore wall clock using delta against this RTC;
- * adjust again for avg 1/2 second RTC sampling error
- */
- set_normalized_timespec(&time,
- newtime + delta.tv_sec,
- (NSEC_PER_SEC >> 1) + delta.tv_nsec);
- do_settimeofday(&time);
+ /* subtract kernel time between rtc_suspend to rtc_resume */
+ time = timespec_sub(time, timespec_sub(newts, oldts));
+ timekeeping_inject_sleeptime(&time);
return 0;
}
@@ -171,7 +166,7 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
err = __rtc_read_alarm(rtc, &alrm);
if (!err && !rtc_valid_tm(&alrm.time))
- rtc_set_alarm(rtc, &alrm);
+ rtc_initialize_alarm(rtc, &alrm);
strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
dev_set_name(&rtc->dev, "rtc%d", id);
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 23719f0acbf..ef6316acec4 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -375,6 +375,32 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
}
EXPORT_SYMBOL_GPL(rtc_set_alarm);
+/* Called once per device from rtc_device_register */
+int rtc_initialize_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
+{
+ int err;
+
+ err = rtc_valid_tm(&alarm->time);
+ if (err != 0)
+ return err;
+
+ err = mutex_lock_interruptible(&rtc->ops_lock);
+ if (err)
+ return err;
+
+ rtc->aie_timer.node.expires = rtc_tm_to_ktime(alarm->time);
+ rtc->aie_timer.period = ktime_set(0, 0);
+ if (alarm->enabled) {
+ rtc->aie_timer.enabled = 1;
+ timerqueue_add(&rtc->timerqueue, &rtc->aie_timer.node);
+ }
+ mutex_unlock(&rtc->ops_lock);
+ return err;
+}
+EXPORT_SYMBOL_GPL(rtc_initialize_alarm);
+
+
+
int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled)
{
int err = mutex_lock_interruptible(&rtc->ops_lock);
diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c
index a0fc4cf42ab..90d866272c8 100644
--- a/drivers/rtc/rtc-bfin.c
+++ b/drivers/rtc/rtc-bfin.c
@@ -250,6 +250,8 @@ static int bfin_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
bfin_rtc_int_set_alarm(rtc);
else
bfin_rtc_int_clear(~(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY));
+
+ return 0;
}
static int bfin_rtc_read_time(struct device *dev, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c
index 316f484999b..80f9c88214c 100644
--- a/drivers/rtc/rtc-coh901331.c
+++ b/drivers/rtc/rtc-coh901331.c
@@ -220,6 +220,7 @@ static int __init coh901331_probe(struct platform_device *pdev)
}
clk_disable(rtap->clk);
+ platform_set_drvdata(pdev, rtap);
rtap->rtc = rtc_device_register("coh901331", &pdev->dev, &coh901331_ops,
THIS_MODULE);
if (IS_ERR(rtap->rtc)) {
@@ -227,11 +228,10 @@ static int __init coh901331_probe(struct platform_device *pdev)
goto out_no_rtc;
}
- platform_set_drvdata(pdev, rtap);
-
return 0;
out_no_rtc:
+ platform_set_drvdata(pdev, NULL);
out_no_clk_enable:
clk_put(rtap->clk);
out_no_clk:
diff --git a/drivers/rtc/rtc-davinci.c b/drivers/rtc/rtc-davinci.c
index 8d46838dff8..755e1fe914a 100644
--- a/drivers/rtc/rtc-davinci.c
+++ b/drivers/rtc/rtc-davinci.c
@@ -524,6 +524,8 @@ static int __init davinci_rtc_probe(struct platform_device *pdev)
goto fail2;
}
+ platform_set_drvdata(pdev, davinci_rtc);
+
davinci_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
&davinci_rtc_ops, THIS_MODULE);
if (IS_ERR(davinci_rtc->rtc)) {
@@ -553,8 +555,6 @@ static int __init davinci_rtc_probe(struct platform_device *pdev)
rtcss_write(davinci_rtc, PRTCSS_RTC_CCTRL_CAEN, PRTCSS_RTC_CCTRL);
- platform_set_drvdata(pdev, davinci_rtc);
-
device_init_wakeup(&pdev->dev, 0);
return 0;
@@ -562,6 +562,7 @@ static int __init davinci_rtc_probe(struct platform_device *pdev)
fail4:
rtc_device_unregister(davinci_rtc->rtc);
fail3:
+ platform_set_drvdata(pdev, NULL);
iounmap(davinci_rtc->base);
fail2:
release_mem_region(davinci_rtc->pbase, davinci_rtc->base_size);
diff --git a/drivers/rtc/rtc-ds1286.c b/drivers/rtc/rtc-ds1286.c
index 60ce6960082..47e681df31e 100644
--- a/drivers/rtc/rtc-ds1286.c
+++ b/drivers/rtc/rtc-ds1286.c
@@ -355,6 +355,7 @@ static int __devinit ds1286_probe(struct platform_device *pdev)
goto out;
}
spin_lock_init(&priv->lock);
+ platform_set_drvdata(pdev, priv);
rtc = rtc_device_register("ds1286", &pdev->dev,
&ds1286_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
@@ -362,7 +363,6 @@ static int __devinit ds1286_probe(struct platform_device *pdev)
goto out;
}
priv->rtc = rtc;
- platform_set_drvdata(pdev, priv);
return 0;
out:
diff --git a/drivers/rtc/rtc-ep93xx.c b/drivers/rtc/rtc-ep93xx.c
index 11ae64dcbf3..335551d333b 100644
--- a/drivers/rtc/rtc-ep93xx.c
+++ b/drivers/rtc/rtc-ep93xx.c
@@ -151,6 +151,7 @@ static int __init ep93xx_rtc_probe(struct platform_device *pdev)
return -ENXIO;
pdev->dev.platform_data = ep93xx_rtc;
+ platform_set_drvdata(pdev, rtc);
rtc = rtc_device_register(pdev->name,
&pdev->dev, &ep93xx_rtc_ops, THIS_MODULE);
@@ -159,8 +160,6 @@ static int __init ep93xx_rtc_probe(struct platform_device *pdev)
goto exit;
}
- platform_set_drvdata(pdev, rtc);
-
err = sysfs_create_group(&pdev->dev.kobj, &ep93xx_rtc_sysfs_files);
if (err)
goto fail;
@@ -168,9 +167,9 @@ static int __init ep93xx_rtc_probe(struct platform_device *pdev)
return 0;
fail:
- platform_set_drvdata(pdev, NULL);
rtc_device_unregister(rtc);
exit:
+ platform_set_drvdata(pdev, NULL);
pdev->dev.platform_data = NULL;
return err;
}
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index 69fe664a222..eda128fc1d3 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -783,6 +783,9 @@ static int m41t80_probe(struct i2c_client *client,
goto exit;
}
+ clientdata->features = id->driver_data;
+ i2c_set_clientdata(client, clientdata);
+
rtc = rtc_device_register(client->name, &client->dev,
&m41t80_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
@@ -792,8 +795,6 @@ static int m41t80_probe(struct i2c_client *client,
}
clientdata->rtc = rtc;
- clientdata->features = id->driver_data;
- i2c_set_clientdata(client, clientdata);
/* Make sure HT (Halt Update) bit is cleared */
rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_HOUR);
diff --git a/drivers/rtc/rtc-max8925.c b/drivers/rtc/rtc-max8925.c
index 174036dda78..3bc046f427e 100644
--- a/drivers/rtc/rtc-max8925.c
+++ b/drivers/rtc/rtc-max8925.c
@@ -257,6 +257,10 @@ static int __devinit max8925_rtc_probe(struct platform_device *pdev)
goto out_irq;
}
+ dev_set_drvdata(&pdev->dev, info);
+ /* XXX - isn't this redundant? */
+ platform_set_drvdata(pdev, info);
+
info->rtc_dev = rtc_device_register("max8925-rtc", &pdev->dev,
&max8925_rtc_ops, THIS_MODULE);
ret = PTR_ERR(info->rtc_dev);
@@ -265,11 +269,9 @@ static int __devinit max8925_rtc_probe(struct platform_device *pdev)
goto out_rtc;
}
- dev_set_drvdata(&pdev->dev, info);
- platform_set_drvdata(pdev, info);
-
return 0;
out_rtc:
+ platform_set_drvdata(pdev, NULL);
free_irq(chip->irq_base + MAX8925_IRQ_RTC_ALARM0, info);
out_irq:
kfree(info);
diff --git a/drivers/rtc/rtc-max8998.c b/drivers/rtc/rtc-max8998.c
index 3f7bc6b9fef..2e48aa60427 100644
--- a/drivers/rtc/rtc-max8998.c
+++ b/drivers/rtc/rtc-max8998.c
@@ -265,6 +265,8 @@ static int __devinit max8998_rtc_probe(struct platform_device *pdev)
info->rtc = max8998->rtc;
info->irq = max8998->irq_base + MAX8998_IRQ_ALARM0;
+ platform_set_drvdata(pdev, info);
+
info->rtc_dev = rtc_device_register("max8998-rtc", &pdev->dev,
&max8998_rtc_ops, THIS_MODULE);
@@ -274,8 +276,6 @@ static int __devinit max8998_rtc_probe(struct platform_device *pdev)
goto out_rtc;
}
- platform_set_drvdata(pdev, info);
-
ret = request_threaded_irq(info->irq, NULL, max8998_rtc_alarm_irq, 0,
"rtc-alarm0", info);
@@ -293,6 +293,7 @@ static int __devinit max8998_rtc_probe(struct platform_device *pdev)
return 0;
out_rtc:
+ platform_set_drvdata(pdev, NULL);
kfree(info);
return ret;
}
diff --git a/drivers/rtc/rtc-mc13xxx.c b/drivers/rtc/rtc-mc13xxx.c
index c4200646955..a1a278bc340 100644
--- a/drivers/rtc/rtc-mc13xxx.c
+++ b/drivers/rtc/rtc-mc13xxx.c
@@ -349,11 +349,15 @@ static int __devinit mc13xxx_rtc_probe(struct platform_device *pdev)
if (ret)
goto err_alarm_irq_request;
+ mc13xxx_unlock(mc13xxx);
+
priv->rtc = rtc_device_register(pdev->name,
&pdev->dev, &mc13xxx_rtc_ops, THIS_MODULE);
if (IS_ERR(priv->rtc)) {
ret = PTR_ERR(priv->rtc);
+ mc13xxx_lock(mc13xxx);
+
mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_TODA, priv);
err_alarm_irq_request:
@@ -365,12 +369,12 @@ err_reset_irq_status:
mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_RTCRST, priv);
err_reset_irq_request:
+ mc13xxx_unlock(mc13xxx);
+
platform_set_drvdata(pdev, NULL);
kfree(priv);
}
- mc13xxx_unlock(mc13xxx);
-
return ret;
}
@@ -401,6 +405,7 @@ const struct platform_device_id mc13xxx_rtc_idtable[] = {
}, {
.name = "mc13892-rtc",
},
+ { }
};
static struct platform_driver mc13xxx_rtc_driver = {
diff --git a/drivers/rtc/rtc-msm6242.c b/drivers/rtc/rtc-msm6242.c
index 67820626e18..fcb113c1112 100644
--- a/drivers/rtc/rtc-msm6242.c
+++ b/drivers/rtc/rtc-msm6242.c
@@ -214,6 +214,7 @@ static int __init msm6242_rtc_probe(struct platform_device *dev)
error = -ENOMEM;
goto out_free_priv;
}
+ platform_set_drvdata(dev, priv);
rtc = rtc_device_register("rtc-msm6242", &dev->dev, &msm6242_rtc_ops,
THIS_MODULE);
@@ -223,10 +224,10 @@ static int __init msm6242_rtc_probe(struct platform_device *dev)
}
priv->rtc = rtc;
- platform_set_drvdata(dev, priv);
return 0;
out_unmap:
+ platform_set_drvdata(dev, NULL);
iounmap(priv->regs);
out_free_priv:
kfree(priv);
diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c
index 826ab64a8fa..d814417bee8 100644
--- a/drivers/rtc/rtc-mxc.c
+++ b/drivers/rtc/rtc-mxc.c
@@ -418,14 +418,6 @@ static int __init mxc_rtc_probe(struct platform_device *pdev)
goto exit_put_clk;
}
- rtc = rtc_device_register(pdev->name, &pdev->dev, &mxc_rtc_ops,
- THIS_MODULE);
- if (IS_ERR(rtc)) {
- ret = PTR_ERR(rtc);
- goto exit_put_clk;
- }
-
- pdata->rtc = rtc;
platform_set_drvdata(pdev, pdata);
/* Configure and enable the RTC */
@@ -438,8 +430,19 @@ static int __init mxc_rtc_probe(struct platform_device *pdev)
pdata->irq = -1;
}
+ rtc = rtc_device_register(pdev->name, &pdev->dev, &mxc_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ ret = PTR_ERR(rtc);
+ goto exit_clr_drvdata;
+ }
+
+ pdata->rtc = rtc;
+
return 0;
+exit_clr_drvdata:
+ platform_set_drvdata(pdev, NULL);
exit_put_clk:
clk_disable(pdata->clk);
clk_put(pdata->clk);
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
index de0dd7b1f14..bcae8dd4149 100644
--- a/drivers/rtc/rtc-omap.c
+++ b/drivers/rtc/rtc-omap.c
@@ -394,7 +394,7 @@ static int __init omap_rtc_probe(struct platform_device *pdev)
return 0;
fail2:
- free_irq(omap_rtc_timer, NULL);
+ free_irq(omap_rtc_timer, rtc);
fail1:
rtc_device_unregister(rtc);
fail0:
diff --git a/drivers/rtc/rtc-pcap.c b/drivers/rtc/rtc-pcap.c
index a633abc4289..cd4f198cc2e 100644
--- a/drivers/rtc/rtc-pcap.c
+++ b/drivers/rtc/rtc-pcap.c
@@ -151,6 +151,8 @@ static int __devinit pcap_rtc_probe(struct platform_device *pdev)
pcap_rtc->pcap = dev_get_drvdata(pdev->dev.parent);
+ platform_set_drvdata(pdev, pcap_rtc);
+
pcap_rtc->rtc = rtc_device_register("pcap", &pdev->dev,
&pcap_rtc_ops, THIS_MODULE);
if (IS_ERR(pcap_rtc->rtc)) {
@@ -158,7 +160,6 @@ static int __devinit pcap_rtc_probe(struct platform_device *pdev)
goto fail_rtc;
}
- platform_set_drvdata(pdev, pcap_rtc);
timer_irq = pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_1HZ);
alarm_irq = pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_TODA);
@@ -177,6 +178,7 @@ fail_alarm:
fail_timer:
rtc_device_unregister(pcap_rtc->rtc);
fail_rtc:
+ platform_set_drvdata(pdev, NULL);
kfree(pcap_rtc);
return err;
}
diff --git a/drivers/rtc/rtc-rp5c01.c b/drivers/rtc/rtc-rp5c01.c
index 694da39b6dd..359da6d020b 100644
--- a/drivers/rtc/rtc-rp5c01.c
+++ b/drivers/rtc/rtc-rp5c01.c
@@ -249,15 +249,15 @@ static int __init rp5c01_rtc_probe(struct platform_device *dev)
spin_lock_init(&priv->lock);
+ platform_set_drvdata(dev, priv);
+
rtc = rtc_device_register("rtc-rp5c01", &dev->dev, &rp5c01_rtc_ops,
THIS_MODULE);
if (IS_ERR(rtc)) {
error = PTR_ERR(rtc);
goto out_unmap;
}
-
priv->rtc = rtc;
- platform_set_drvdata(dev, priv);
error = sysfs_create_bin_file(&dev->dev.kobj, &priv->nvram_attr);
if (error)
@@ -268,6 +268,7 @@ static int __init rp5c01_rtc_probe(struct platform_device *dev)
out_unregister:
rtc_device_unregister(rtc);
out_unmap:
+ platform_set_drvdata(dev, NULL);
iounmap(priv->regs);
out_free_priv:
kfree(priv);
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index 714964913e5..16512ecae31 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -46,6 +46,7 @@ static struct clk *rtc_clk;
static void __iomem *s3c_rtc_base;
static int s3c_rtc_alarmno = NO_IRQ;
static int s3c_rtc_tickno = NO_IRQ;
+static bool wake_en;
static enum s3c_cpu_type s3c_rtc_cpu_type;
static DEFINE_SPINLOCK(s3c_rtc_pie_lock);
@@ -336,7 +337,6 @@ static void s3c_rtc_release(struct device *dev)
/* do not clear AIE here, it may be needed for wake */
- s3c_rtc_setpie(dev, 0);
free_irq(s3c_rtc_alarmno, rtc_dev);
free_irq(s3c_rtc_tickno, rtc_dev);
}
@@ -408,7 +408,6 @@ static int __devexit s3c_rtc_remove(struct platform_device *dev)
platform_set_drvdata(dev, NULL);
rtc_device_unregister(rtc);
- s3c_rtc_setpie(&dev->dev, 0);
s3c_rtc_setaie(&dev->dev, 0);
clk_disable(rtc_clk);
@@ -564,8 +563,12 @@ static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state)
}
s3c_rtc_enable(pdev, 0);
- if (device_may_wakeup(&pdev->dev))
- enable_irq_wake(s3c_rtc_alarmno);
+ if (device_may_wakeup(&pdev->dev) && !wake_en) {
+ if (enable_irq_wake(s3c_rtc_alarmno) == 0)
+ wake_en = true;
+ else
+ dev_err(&pdev->dev, "enable_irq_wake failed\n");
+ }
return 0;
}
@@ -581,8 +584,10 @@ static int s3c_rtc_resume(struct platform_device *pdev)
writew(tmp | ticnt_en_save, s3c_rtc_base + S3C2410_RTCCON);
}
- if (device_may_wakeup(&pdev->dev))
+ if (device_may_wakeup(&pdev->dev) && wake_en) {
disable_irq_wake(s3c_rtc_alarmno);
+ wake_en = false;
+ }
return 0;
}
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 4d2df2f76ea..86b6f1cc1b1 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -1742,11 +1742,20 @@ int dasd_sleep_on_interruptible(struct dasd_ccw_req *cqr)
static inline int _dasd_term_running_cqr(struct dasd_device *device)
{
struct dasd_ccw_req *cqr;
+ int rc;
if (list_empty(&device->ccw_queue))
return 0;
cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist);
- return device->discipline->term_IO(cqr);
+ rc = device->discipline->term_IO(cqr);
+ if (!rc)
+ /*
+ * CQR terminated because a more important request is pending.
+ * Undo decreasing of retry counter because this is
+ * not an error case.
+ */
+ cqr->retries++;
+ return rc;
}
int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr)
@@ -2314,15 +2323,14 @@ static void dasd_flush_request_queue(struct dasd_block *block)
static int dasd_open(struct block_device *bdev, fmode_t mode)
{
- struct dasd_block *block = bdev->bd_disk->private_data;
struct dasd_device *base;
int rc;
- if (!block)
+ base = dasd_device_from_gendisk(bdev->bd_disk);
+ if (!base)
return -ENODEV;
- base = block->base;
- atomic_inc(&block->open_count);
+ atomic_inc(&base->block->open_count);
if (test_bit(DASD_FLAG_OFFLINE, &base->flags)) {
rc = -ENODEV;
goto unlock;
@@ -2355,21 +2363,28 @@ static int dasd_open(struct block_device *bdev, fmode_t mode)
goto out;
}
+ dasd_put_device(base);
return 0;
out:
module_put(base->discipline->owner);
unlock:
- atomic_dec(&block->open_count);
+ atomic_dec(&base->block->open_count);
+ dasd_put_device(base);
return rc;
}
static int dasd_release(struct gendisk *disk, fmode_t mode)
{
- struct dasd_block *block = disk->private_data;
+ struct dasd_device *base;
- atomic_dec(&block->open_count);
- module_put(block->base->discipline->owner);
+ base = dasd_device_from_gendisk(disk);
+ if (!base)
+ return -ENODEV;
+
+ atomic_dec(&base->block->open_count);
+ module_put(base->discipline->owner);
+ dasd_put_device(base);
return 0;
}
@@ -2378,20 +2393,20 @@ static int dasd_release(struct gendisk *disk, fmode_t mode)
*/
static int dasd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
- struct dasd_block *block;
struct dasd_device *base;
- block = bdev->bd_disk->private_data;
- if (!block)
+ base = dasd_device_from_gendisk(bdev->bd_disk);
+ if (!base)
return -ENODEV;
- base = block->base;
if (!base->discipline ||
- !base->discipline->fill_geometry)
+ !base->discipline->fill_geometry) {
+ dasd_put_device(base);
return -EINVAL;
-
- base->discipline->fill_geometry(block, geo);
- geo->start = get_start_sect(bdev) >> block->s2b_shift;
+ }
+ base->discipline->fill_geometry(base->block, geo);
+ geo->start = get_start_sect(bdev) >> base->block->s2b_shift;
+ dasd_put_device(base);
return 0;
}
@@ -2528,7 +2543,6 @@ void dasd_generic_remove(struct ccw_device *cdev)
dasd_set_target_state(device, DASD_STATE_NEW);
/* dasd_delete_device destroys the device reference. */
block = device->block;
- device->block = NULL;
dasd_delete_device(device);
/*
* life cycle of block is bound to device, so delete it after
@@ -2650,7 +2664,6 @@ int dasd_generic_set_offline(struct ccw_device *cdev)
dasd_set_target_state(device, DASD_STATE_NEW);
/* dasd_delete_device destroys the device reference. */
block = device->block;
- device->block = NULL;
dasd_delete_device(device);
/*
* life cycle of block is bound to device, so delete it after
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index 42e1bf35f68..d71511c7850 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -674,6 +674,36 @@ dasd_device_from_cdev(struct ccw_device *cdev)
return device;
}
+void dasd_add_link_to_gendisk(struct gendisk *gdp, struct dasd_device *device)
+{
+ struct dasd_devmap *devmap;
+
+ devmap = dasd_find_busid(dev_name(&device->cdev->dev));
+ if (IS_ERR(devmap))
+ return;
+ spin_lock(&dasd_devmap_lock);
+ gdp->private_data = devmap;
+ spin_unlock(&dasd_devmap_lock);
+}
+
+struct dasd_device *dasd_device_from_gendisk(struct gendisk *gdp)
+{
+ struct dasd_device *device;
+ struct dasd_devmap *devmap;
+
+ if (!gdp->private_data)
+ return NULL;
+ device = NULL;
+ spin_lock(&dasd_devmap_lock);
+ devmap = gdp->private_data;
+ if (devmap && devmap->device) {
+ device = devmap->device;
+ dasd_get_device(device);
+ }
+ spin_unlock(&dasd_devmap_lock);
+ return device;
+}
+
/*
* SECTION: files in sysfs
*/
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index 29143eda9dd..85dddb1e412 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -239,7 +239,6 @@ static void dasd_ext_handler(unsigned int ext_int_code,
addr_t ip;
int rc;
- kstat_cpu(smp_processor_id()).irqs[EXTINT_DSD]++;
switch (ext_int_code >> 24) {
case DASD_DIAG_CODE_31BIT:
ip = (addr_t) param32;
@@ -250,6 +249,7 @@ static void dasd_ext_handler(unsigned int ext_int_code,
default:
return;
}
+ kstat_cpu(smp_processor_id()).irqs[EXTINT_DSD]++;
if (!ip) { /* no intparm: unsolicited interrupt */
DBF_EVENT(DBF_NOTICE, "%s", "caught unsolicited "
"interrupt");
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index db8005d9f2f..3ebdf5f92f8 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -2037,7 +2037,7 @@ static void dasd_eckd_check_for_device_change(struct dasd_device *device,
return;
/* summary unit check */
- if ((sense[7] == 0x0D) &&
+ if ((sense[27] & DASD_SENSE_BIT_0) && (sense[7] == 0x0D) &&
(scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK)) {
dasd_alias_handle_summary_unit_check(device, irb);
return;
@@ -2053,7 +2053,8 @@ static void dasd_eckd_check_for_device_change(struct dasd_device *device,
/* loss of device reservation is handled via base devices only
* as alias devices may be used with several bases
*/
- if (device->block && (sense[7] == 0x3F) &&
+ if (device->block && (sense[27] & DASD_SENSE_BIT_0) &&
+ (sense[7] == 0x3F) &&
(scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK) &&
test_bit(DASD_FLAG_IS_RESERVED, &device->flags)) {
if (device->features & DASD_FEATURE_FAILONSLCK)
diff --git a/drivers/s390/block/dasd_genhd.c b/drivers/s390/block/dasd_genhd.c
index 5505bc07e1e..19a1ff03d65 100644
--- a/drivers/s390/block/dasd_genhd.c
+++ b/drivers/s390/block/dasd_genhd.c
@@ -73,7 +73,7 @@ int dasd_gendisk_alloc(struct dasd_block *block)
if (base->features & DASD_FEATURE_READONLY ||
test_bit(DASD_FLAG_DEVICE_RO, &base->flags))
set_disk_ro(gdp, 1);
- gdp->private_data = block;
+ dasd_add_link_to_gendisk(gdp, base);
gdp->queue = block->request_queue;
block->gdp = gdp;
set_capacity(block->gdp, 0);
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index df9f6999411..d1e4f2c1264 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -686,6 +686,9 @@ struct dasd_device *dasd_device_from_cdev(struct ccw_device *);
struct dasd_device *dasd_device_from_cdev_locked(struct ccw_device *);
struct dasd_device *dasd_device_from_devindex(int);
+void dasd_add_link_to_gendisk(struct gendisk *, struct dasd_device *);
+struct dasd_device *dasd_device_from_gendisk(struct gendisk *);
+
int dasd_parse(void);
int dasd_busid_known(const char *);
diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c
index 26075e95b1b..72261e4c516 100644
--- a/drivers/s390/block/dasd_ioctl.c
+++ b/drivers/s390/block/dasd_ioctl.c
@@ -42,16 +42,22 @@ dasd_ioctl_api_version(void __user *argp)
static int
dasd_ioctl_enable(struct block_device *bdev)
{
- struct dasd_block *block = bdev->bd_disk->private_data;
+ struct dasd_device *base;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- dasd_enable_device(block->base);
+ base = dasd_device_from_gendisk(bdev->bd_disk);
+ if (!base)
+ return -ENODEV;
+
+ dasd_enable_device(base);
/* Formatting the dasd device can change the capacity. */
mutex_lock(&bdev->bd_mutex);
- i_size_write(bdev->bd_inode, (loff_t)get_capacity(block->gdp) << 9);
+ i_size_write(bdev->bd_inode,
+ (loff_t)get_capacity(base->block->gdp) << 9);
mutex_unlock(&bdev->bd_mutex);
+ dasd_put_device(base);
return 0;
}
@@ -62,11 +68,14 @@ dasd_ioctl_enable(struct block_device *bdev)
static int
dasd_ioctl_disable(struct block_device *bdev)
{
- struct dasd_block *block = bdev->bd_disk->private_data;
+ struct dasd_device *base;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
+ base = dasd_device_from_gendisk(bdev->bd_disk);
+ if (!base)
+ return -ENODEV;
/*
* Man this is sick. We don't do a real disable but only downgrade
* the device to DASD_STATE_BASIC. The reason is that dasdfmt uses
@@ -75,7 +84,7 @@ dasd_ioctl_disable(struct block_device *bdev)
* using the BIODASDFMT ioctl. Therefore the correct state for the
* device is DASD_STATE_BASIC that allows to do basic i/o.
*/
- dasd_set_target_state(block->base, DASD_STATE_BASIC);
+ dasd_set_target_state(base, DASD_STATE_BASIC);
/*
* Set i_size to zero, since read, write, etc. check against this
* value.
@@ -83,6 +92,7 @@ dasd_ioctl_disable(struct block_device *bdev)
mutex_lock(&bdev->bd_mutex);
i_size_write(bdev->bd_inode, 0);
mutex_unlock(&bdev->bd_mutex);
+ dasd_put_device(base);
return 0;
}
@@ -191,26 +201,36 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata)
static int
dasd_ioctl_format(struct block_device *bdev, void __user *argp)
{
- struct dasd_block *block = bdev->bd_disk->private_data;
+ struct dasd_device *base;
struct format_data_t fdata;
+ int rc;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (!argp)
return -EINVAL;
-
- if (block->base->features & DASD_FEATURE_READONLY ||
- test_bit(DASD_FLAG_DEVICE_RO, &block->base->flags))
+ base = dasd_device_from_gendisk(bdev->bd_disk);
+ if (!base)
+ return -ENODEV;
+ if (base->features & DASD_FEATURE_READONLY ||
+ test_bit(DASD_FLAG_DEVICE_RO, &base->flags)) {
+ dasd_put_device(base);
return -EROFS;
- if (copy_from_user(&fdata, argp, sizeof(struct format_data_t)))
+ }
+ if (copy_from_user(&fdata, argp, sizeof(struct format_data_t))) {
+ dasd_put_device(base);
return -EFAULT;
+ }
if (bdev != bdev->bd_contains) {
pr_warning("%s: The specified DASD is a partition and cannot "
"be formatted\n",
- dev_name(&block->base->cdev->dev));
+ dev_name(&base->cdev->dev));
+ dasd_put_device(base);
return -EINVAL;
}
- return dasd_format(block, &fdata);
+ rc = dasd_format(base->block, &fdata);
+ dasd_put_device(base);
+ return rc;
}
#ifdef CONFIG_DASD_PROFILE
@@ -340,8 +360,8 @@ static int dasd_ioctl_information(struct dasd_block *block,
static int
dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp)
{
- struct dasd_block *block = bdev->bd_disk->private_data;
- int intval;
+ struct dasd_device *base;
+ int intval, rc;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
@@ -350,10 +370,17 @@ dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp)
return -EINVAL;
if (get_user(intval, (int __user *)argp))
return -EFAULT;
- if (!intval && test_bit(DASD_FLAG_DEVICE_RO, &block->base->flags))
+ base = dasd_device_from_gendisk(bdev->bd_disk);
+ if (!base)
+ return -ENODEV;
+ if (!intval && test_bit(DASD_FLAG_DEVICE_RO, &base->flags)) {
+ dasd_put_device(base);
return -EROFS;
+ }
set_disk_ro(bdev->bd_disk, intval);
- return dasd_set_feature(block->base->cdev, DASD_FEATURE_READONLY, intval);
+ rc = dasd_set_feature(base->cdev, DASD_FEATURE_READONLY, intval);
+ dasd_put_device(base);
+ return rc;
}
static int dasd_ioctl_readall_cmb(struct dasd_block *block, unsigned int cmd,
@@ -372,59 +399,78 @@ static int dasd_ioctl_readall_cmb(struct dasd_block *block, unsigned int cmd,
int dasd_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg)
{
- struct dasd_block *block = bdev->bd_disk->private_data;
+ struct dasd_block *block;
+ struct dasd_device *base;
void __user *argp;
+ int rc;
if (is_compat_task())
argp = compat_ptr(arg);
else
argp = (void __user *)arg;
- if (!block)
- return -ENODEV;
-
if ((_IOC_DIR(cmd) != _IOC_NONE) && !arg) {
PRINT_DEBUG("empty data ptr");
return -EINVAL;
}
+ base = dasd_device_from_gendisk(bdev->bd_disk);
+ if (!base)
+ return -ENODEV;
+ block = base->block;
+ rc = 0;
switch (cmd) {
case BIODASDDISABLE:
- return dasd_ioctl_disable(bdev);
+ rc = dasd_ioctl_disable(bdev);
+ break;
case BIODASDENABLE:
- return dasd_ioctl_enable(bdev);
+ rc = dasd_ioctl_enable(bdev);
+ break;
case BIODASDQUIESCE:
- return dasd_ioctl_quiesce(block);
+ rc = dasd_ioctl_quiesce(block);
+ break;
case BIODASDRESUME:
- return dasd_ioctl_resume(block);
+ rc = dasd_ioctl_resume(block);
+ break;
case BIODASDFMT:
- return dasd_ioctl_format(bdev, argp);
+ rc = dasd_ioctl_format(bdev, argp);
+ break;
case BIODASDINFO:
- return dasd_ioctl_information(block, cmd, argp);
+ rc = dasd_ioctl_information(block, cmd, argp);
+ break;
case BIODASDINFO2:
- return dasd_ioctl_information(block, cmd, argp);
+ rc = dasd_ioctl_information(block, cmd, argp);
+ break;
case BIODASDPRRD:
- return dasd_ioctl_read_profile(block, argp);
+ rc = dasd_ioctl_read_profile(block, argp);
+ break;
case BIODASDPRRST:
- return dasd_ioctl_reset_profile(block);
+ rc = dasd_ioctl_reset_profile(block);
+ break;
case BLKROSET:
- return dasd_ioctl_set_ro(bdev, argp);
+ rc = dasd_ioctl_set_ro(bdev, argp);
+ break;
case DASDAPIVER:
- return dasd_ioctl_api_version(argp);
+ rc = dasd_ioctl_api_version(argp);
+ break;
case BIODASDCMFENABLE:
- return enable_cmf(block->base->cdev);
+ rc = enable_cmf(base->cdev);
+ break;
case BIODASDCMFDISABLE:
- return disable_cmf(block->base->cdev);
+ rc = disable_cmf(base->cdev);
+ break;
case BIODASDREADALLCMB:
- return dasd_ioctl_readall_cmb(block, cmd, argp);
+ rc = dasd_ioctl_readall_cmb(block, cmd, argp);
+ break;
default:
/* if the discipline has an ioctl method try it. */
- if (block->base->discipline->ioctl) {
- int rval = block->base->discipline->ioctl(block, cmd, argp);
- if (rval != -ENOIOCTLCMD)
- return rval;
- }
-
- return -EINVAL;
+ if (base->discipline->ioctl) {
+ rc = base->discipline->ioctl(block, cmd, argp);
+ if (rc == -ENOIOCTLCMD)
+ rc = -EINVAL;
+ } else
+ rc = -EINVAL;
}
+ dasd_put_device(base);
+ return rc;
}
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c
index 4b60ede07f0..be55fb2b1b1 100644
--- a/drivers/s390/char/sclp_cmd.c
+++ b/drivers/s390/char/sclp_cmd.c
@@ -518,6 +518,8 @@ static void __init insert_increment(u16 rn, int standby, int assigned)
return;
new_incr->rn = rn;
new_incr->standby = standby;
+ if (!standby)
+ new_incr->usecount = 1;
last_rn = 0;
prev = &sclp_mem_list;
list_for_each_entry(incr, &sclp_mem_list, list) {
diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c
index 83cea9a55e2..1b3924c2fff 100644
--- a/drivers/s390/char/tape_block.c
+++ b/drivers/s390/char/tape_block.c
@@ -236,7 +236,6 @@ tapeblock_setup_device(struct tape_device * device)
disk->major = tapeblock_major;
disk->first_minor = device->first_minor;
disk->fops = &tapeblock_fops;
- disk->events = DISK_EVENT_MEDIA_CHANGE;
disk->private_data = tape_get_device(device);
disk->queue = blkdat->request_queue;
set_capacity(disk, 0);
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index c532ba929cc..e8f267eb888 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -407,8 +407,11 @@ static inline void account_sbals(struct qdio_q *q, int count)
q->q_stats.nr_sbals[pos]++;
}
-static void announce_buffer_error(struct qdio_q *q, int count)
+static void process_buffer_error(struct qdio_q *q, int count)
{
+ unsigned char state = (q->is_input_q) ? SLSB_P_INPUT_NOT_INIT :
+ SLSB_P_OUTPUT_NOT_INIT;
+
q->qdio_error |= QDIO_ERROR_SLSB_STATE;
/* special handling for no target buffer empty */
@@ -426,6 +429,12 @@ static void announce_buffer_error(struct qdio_q *q, int count)
DBF_ERROR("F14:%2x F15:%2x",
q->sbal[q->first_to_check]->element[14].flags & 0xff,
q->sbal[q->first_to_check]->element[15].flags & 0xff);
+
+ /*
+ * Interrupts may be avoided as long as the error is present
+ * so change the buffer state immediately to avoid starvation.
+ */
+ set_buf_states(q, q->first_to_check, state, count);
}
static inline void inbound_primed(struct qdio_q *q, int count)
@@ -506,8 +515,7 @@ static int get_inbound_buffer_frontier(struct qdio_q *q)
account_sbals(q, count);
break;
case SLSB_P_INPUT_ERROR:
- announce_buffer_error(q, count);
- /* process the buffer, the upper layer will take care of it */
+ process_buffer_error(q, count);
q->first_to_check = add_buf(q->first_to_check, count);
atomic_sub(count, &q->nr_buf_used);
if (q->irq_ptr->perf_stat_enabled)
@@ -677,8 +685,7 @@ static int get_outbound_buffer_frontier(struct qdio_q *q)
account_sbals(q, count);
break;
case SLSB_P_OUTPUT_ERROR:
- announce_buffer_error(q, count);
- /* process the buffer, the upper layer will take care of it */
+ process_buffer_error(q, count);
q->first_to_check = add_buf(q->first_to_check, count);
atomic_sub(count, &q->nr_buf_used);
if (q->irq_ptr->perf_stat_enabled)
diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c
index 414427d64a8..607998f0b7d 100644
--- a/drivers/s390/kvm/kvm_virtio.c
+++ b/drivers/s390/kvm/kvm_virtio.c
@@ -381,10 +381,10 @@ static void kvm_extint_handler(unsigned int ext_int_code,
u16 subcode;
u32 param;
- kstat_cpu(smp_processor_id()).irqs[EXTINT_VRT]++;
subcode = ext_int_code >> 16;
if ((subcode & 0xff00) != VIRTIO_SUBCODE_64)
return;
+ kstat_cpu(smp_processor_id()).irqs[EXTINT_VRT]++;
/* The LSB might be overloaded, we have to mask it */
vq = (struct virtqueue *)(param64 & ~1UL);
diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c
index da7b9887ec4..f980600f78a 100644
--- a/drivers/scsi/arcmsr/arcmsr_hba.c
+++ b/drivers/scsi/arcmsr/arcmsr_hba.c
@@ -75,8 +75,10 @@ MODULE_AUTHOR("Nick Cheng <support@areca.com.tw>");
MODULE_DESCRIPTION("ARECA (ARC11xx/12xx/16xx/1880) SATA/SAS RAID Host Bus Adapter");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION(ARCMSR_DRIVER_VERSION);
-static int sleeptime = 10;
-static int retrycount = 12;
+
+#define ARCMSR_SLEEPTIME 10
+#define ARCMSR_RETRYCOUNT 12
+
wait_queue_head_t wait_q;
static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb,
struct scsi_cmnd *cmd);
@@ -171,24 +173,6 @@ static struct pci_driver arcmsr_pci_driver = {
****************************************************************************
****************************************************************************
*/
-int arcmsr_sleep_for_bus_reset(struct scsi_cmnd *cmd)
-{
- struct Scsi_Host *shost = NULL;
- int i, isleep;
- shost = cmd->device->host;
- isleep = sleeptime / 10;
- if (isleep > 0) {
- for (i = 0; i < isleep; i++) {
- msleep(10000);
- }
- }
-
- isleep = sleeptime % 10;
- if (isleep > 0) {
- msleep(isleep*1000);
- }
- return 0;
-}
static void arcmsr_free_hbb_mu(struct AdapterControlBlock *acb)
{
@@ -323,66 +307,64 @@ static void arcmsr_define_adapter_type(struct AdapterControlBlock *acb)
default: acb->adapter_type = ACB_ADAPTER_TYPE_A;
}
-}
+}
static uint8_t arcmsr_hba_wait_msgint_ready(struct AdapterControlBlock *acb)
{
struct MessageUnit_A __iomem *reg = acb->pmuA;
- uint32_t Index;
- uint8_t Retries = 0x00;
- do {
- for (Index = 0; Index < 100; Index++) {
- if (readl(&reg->outbound_intstatus) &
- ARCMSR_MU_OUTBOUND_MESSAGE0_INT) {
- writel(ARCMSR_MU_OUTBOUND_MESSAGE0_INT,
- &reg->outbound_intstatus);
- return true;
- }
- msleep(10);
- }/*max 1 seconds*/
+ int i;
+
+ for (i = 0; i < 2000; i++) {
+ if (readl(&reg->outbound_intstatus) &
+ ARCMSR_MU_OUTBOUND_MESSAGE0_INT) {
+ writel(ARCMSR_MU_OUTBOUND_MESSAGE0_INT,
+ &reg->outbound_intstatus);
+ return true;
+ }
+ msleep(10);
+ } /* max 20 seconds */
- } while (Retries++ < 20);/*max 20 sec*/
return false;
}
static uint8_t arcmsr_hbb_wait_msgint_ready(struct AdapterControlBlock *acb)
{
struct MessageUnit_B *reg = acb->pmuB;
- uint32_t Index;
- uint8_t Retries = 0x00;
- do {
- for (Index = 0; Index < 100; Index++) {
- if (readl(reg->iop2drv_doorbell)
- & ARCMSR_IOP2DRV_MESSAGE_CMD_DONE) {
- writel(ARCMSR_MESSAGE_INT_CLEAR_PATTERN
- , reg->iop2drv_doorbell);
- writel(ARCMSR_DRV2IOP_END_OF_INTERRUPT, reg->drv2iop_doorbell);
- return true;
- }
- msleep(10);
- }/*max 1 seconds*/
+ int i;
+
+ for (i = 0; i < 2000; i++) {
+ if (readl(reg->iop2drv_doorbell)
+ & ARCMSR_IOP2DRV_MESSAGE_CMD_DONE) {
+ writel(ARCMSR_MESSAGE_INT_CLEAR_PATTERN,
+ reg->iop2drv_doorbell);
+ writel(ARCMSR_DRV2IOP_END_OF_INTERRUPT,
+ reg->drv2iop_doorbell);
+ return true;
+ }
+ msleep(10);
+ } /* max 20 seconds */
- } while (Retries++ < 20);/*max 20 sec*/
return false;
}
static uint8_t arcmsr_hbc_wait_msgint_ready(struct AdapterControlBlock *pACB)
{
struct MessageUnit_C *phbcmu = (struct MessageUnit_C *)pACB->pmuC;
- unsigned char Retries = 0x00;
- uint32_t Index;
- do {
- for (Index = 0; Index < 100; Index++) {
- if (readl(&phbcmu->outbound_doorbell) & ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE) {
- writel(ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE_DOORBELL_CLEAR, &phbcmu->outbound_doorbell_clear);/*clear interrupt*/
- return true;
- }
- /* one us delay */
- msleep(10);
- } /*max 1 seconds*/
- } while (Retries++ < 20); /*max 20 sec*/
+ int i;
+
+ for (i = 0; i < 2000; i++) {
+ if (readl(&phbcmu->outbound_doorbell)
+ & ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE) {
+ writel(ARCMSR_HBCMU_IOP2DRV_MESSAGE_CMD_DONE_DOORBELL_CLEAR,
+ &phbcmu->outbound_doorbell_clear); /*clear interrupt*/
+ return true;
+ }
+ msleep(10);
+ } /* max 20 seconds */
+
return false;
}
+
static void arcmsr_flush_hba_cache(struct AdapterControlBlock *acb)
{
struct MessageUnit_A __iomem *reg = acb->pmuA;
@@ -459,10 +441,11 @@ static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb)
struct CommandControlBlock *ccb_tmp;
int i = 0, j = 0;
dma_addr_t cdb_phyaddr;
- unsigned long roundup_ccbsize = 0, offset;
+ unsigned long roundup_ccbsize;
unsigned long max_xfer_len;
unsigned long max_sg_entrys;
uint32_t firm_config_version;
+
for (i = 0; i < ARCMSR_MAX_TARGETID; i++)
for (j = 0; j < ARCMSR_MAX_TARGETLUN; j++)
acb->devstate[i][j] = ARECA_RAID_GONE;
@@ -472,23 +455,20 @@ static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb)
firm_config_version = acb->firm_cfg_version;
if((firm_config_version & 0xFF) >= 3){
max_xfer_len = (ARCMSR_CDB_SG_PAGE_LENGTH << ((firm_config_version >> 8) & 0xFF)) * 1024;/* max 4M byte */
- max_sg_entrys = (max_xfer_len/4096);
+ max_sg_entrys = (max_xfer_len/4096);
}
acb->host->max_sectors = max_xfer_len/512;
acb->host->sg_tablesize = max_sg_entrys;
roundup_ccbsize = roundup(sizeof(struct CommandControlBlock) + (max_sg_entrys - 1) * sizeof(struct SG64ENTRY), 32);
- acb->uncache_size = roundup_ccbsize * ARCMSR_MAX_FREECCB_NUM + 32;
+ acb->uncache_size = roundup_ccbsize * ARCMSR_MAX_FREECCB_NUM;
dma_coherent = dma_alloc_coherent(&pdev->dev, acb->uncache_size, &dma_coherent_handle, GFP_KERNEL);
if(!dma_coherent){
- printk(KERN_NOTICE "arcmsr%d: dma_alloc_coherent got error \n", acb->host->host_no);
+ printk(KERN_NOTICE "arcmsr%d: dma_alloc_coherent got error\n", acb->host->host_no);
return -ENOMEM;
}
acb->dma_coherent = dma_coherent;
acb->dma_coherent_handle = dma_coherent_handle;
memset(dma_coherent, 0, acb->uncache_size);
- offset = roundup((unsigned long)dma_coherent, 32) - (unsigned long)dma_coherent;
- dma_coherent_handle = dma_coherent_handle + offset;
- dma_coherent = (struct CommandControlBlock *)dma_coherent + offset;
ccb_tmp = dma_coherent;
acb->vir2phy_offset = (unsigned long)dma_coherent - (unsigned long)dma_coherent_handle;
for(i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++){
@@ -2602,12 +2582,8 @@ static int arcmsr_iop_confirm(struct AdapterControlBlock *acb)
if (cdb_phyaddr_hi32 != 0) {
struct MessageUnit_C *reg = (struct MessageUnit_C *)acb->pmuC;
- if (cdb_phyaddr_hi32 != 0) {
- unsigned char Retries = 0x00;
- do {
- printk(KERN_NOTICE "arcmsr%d: cdb_phyaddr_hi32=0x%x \n", acb->adapter_index, cdb_phyaddr_hi32);
- } while (Retries++ < 100);
- }
+ printk(KERN_NOTICE "arcmsr%d: cdb_phyaddr_hi32=0x%x\n",
+ acb->adapter_index, cdb_phyaddr_hi32);
writel(ARCMSR_SIGNATURE_SET_CONFIG, &reg->msgcode_rwbuffer[0]);
writel(cdb_phyaddr_hi32, &reg->msgcode_rwbuffer[1]);
writel(ARCMSR_INBOUND_MESG0_SET_CONFIG, &reg->inbound_msgaddr0);
@@ -2955,12 +2931,12 @@ static int arcmsr_bus_reset(struct scsi_cmnd *cmd)
arcmsr_hardware_reset(acb);
acb->acb_flags &= ~ACB_F_IOP_INITED;
sleep_again:
- arcmsr_sleep_for_bus_reset(cmd);
+ ssleep(ARCMSR_SLEEPTIME);
if ((readl(&reg->outbound_msgaddr1) & ARCMSR_OUTBOUND_MESG1_FIRMWARE_OK) == 0) {
- printk(KERN_ERR "arcmsr%d: waiting for hw bus reset return, retry=%d \n", acb->host->host_no, retry_count);
- if (retry_count > retrycount) {
+ printk(KERN_ERR "arcmsr%d: waiting for hw bus reset return, retry=%d\n", acb->host->host_no, retry_count);
+ if (retry_count > ARCMSR_RETRYCOUNT) {
acb->fw_flag = FW_DEADLOCK;
- printk(KERN_ERR "arcmsr%d: waiting for hw bus reset return, RETRY TERMINATED!! \n", acb->host->host_no);
+ printk(KERN_ERR "arcmsr%d: waiting for hw bus reset return, RETRY TERMINATED!!\n", acb->host->host_no);
return FAILED;
}
retry_count++;
@@ -3025,12 +3001,12 @@ sleep_again:
arcmsr_hardware_reset(acb);
acb->acb_flags &= ~ACB_F_IOP_INITED;
sleep:
- arcmsr_sleep_for_bus_reset(cmd);
+ ssleep(ARCMSR_SLEEPTIME);
if ((readl(&reg->host_diagnostic) & 0x04) != 0) {
- printk(KERN_ERR "arcmsr%d: waiting for hw bus reset return, retry=%d \n", acb->host->host_no, retry_count);
- if (retry_count > retrycount) {
+ printk(KERN_ERR "arcmsr%d: waiting for hw bus reset return, retry=%d\n", acb->host->host_no, retry_count);
+ if (retry_count > ARCMSR_RETRYCOUNT) {
acb->fw_flag = FW_DEADLOCK;
- printk(KERN_ERR "arcmsr%d: waiting for hw bus reset return, RETRY TERMINATED!! \n", acb->host->host_no);
+ printk(KERN_ERR "arcmsr%d: waiting for hw bus reset return, RETRY TERMINATED!!\n", acb->host->host_no);
return FAILED;
}
retry_count++;
diff --git a/drivers/scsi/be2iscsi/be.h b/drivers/scsi/be2iscsi/be.h
index 1cb8a5e85c7..1d7b976c850 100644
--- a/drivers/scsi/be2iscsi/be.h
+++ b/drivers/scsi/be2iscsi/be.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2010 ServerEngines
+ * Copyright (C) 2005 - 2011 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -8,11 +8,11 @@
* Public License is included in this distribution in the file called COPYING.
*
* Contact Information:
- * linux-drivers@serverengines.com
+ * linux-drivers@emulex.com
*
- * ServerEngines
- * 209 N. Fair Oaks Ave
- * Sunnyvale, CA 94085
+ * Emulex
+ * 3333 Susan Street
+ * Costa Mesa, CA 92626
*/
#ifndef BEISCSI_H
diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c
index ad246369d37..b8a82f2c62c 100644
--- a/drivers/scsi/be2iscsi/be_cmds.c
+++ b/drivers/scsi/be2iscsi/be_cmds.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2010 ServerEngines
+ * Copyright (C) 2005 - 2011 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -8,11 +8,11 @@
* Public License is included in this distribution in the file called COPYING.
*
* Contact Information:
- * linux-drivers@serverengines.com
+ * linux-drivers@emulex.com
*
- * ServerEngines
- * 209 N. Fair Oaks Ave
- * Sunnyvale, CA 94085
+ * Emulex
+ * 3333 Susan Street
+ * Costa Mesa, CA 92626
*/
#include "be.h"
@@ -458,6 +458,7 @@ void be_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr,
req_hdr->opcode = opcode;
req_hdr->subsystem = subsystem;
req_hdr->request_length = cpu_to_le32(cmd_len - sizeof(*req_hdr));
+ req_hdr->timeout = 120;
}
static void be_cmd_page_addrs_prepare(struct phys_addr *pages, u32 max_pages,
diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h
index fbd1dc2c15f..497eb29e5c9 100644
--- a/drivers/scsi/be2iscsi/be_cmds.h
+++ b/drivers/scsi/be2iscsi/be_cmds.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2010 ServerEngines
+ * Copyright (C) 2005 - 2011 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -8,11 +8,11 @@
* Public License is included in this distribution in the file called COPYING.
*
* Contact Information:
- * linux-drivers@serverengines.com
+ * linux-drivers@emulex.com
*
- * ServerEngines
- * 209 N. Fair Oaks Ave
- * Sunnyvale, CA 94085
+ * Emulex
+ * 3333 Susan Street
+ * Costa Mesa, CA 92626
*/
#ifndef BEISCSI_CMDS_H
diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c
index 868cc559014..3cad1060502 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.c
+++ b/drivers/scsi/be2iscsi/be_iscsi.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2010 ServerEngines
+ * Copyright (C) 2005 - 2011 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -7,15 +7,14 @@
* as published by the Free Software Foundation. The full GNU General
* Public License is included in this distribution in the file called COPYING.
*
- * Written by: Jayamohan Kallickal (jayamohank@serverengines.com)
+ * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
*
* Contact Information:
- * linux-drivers@serverengines.com
- *
- * ServerEngines
- * 209 N. Fair Oaks Ave
- * Sunnyvale, CA 94085
+ * linux-drivers@emulex.com
*
+ * Emulex
+ * 3333 Susan Street
+ * Costa Mesa, CA 92626
*/
#include <scsi/libiscsi.h>
diff --git a/drivers/scsi/be2iscsi/be_iscsi.h b/drivers/scsi/be2iscsi/be_iscsi.h
index 9c532797c29..ff60b7fd92d 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.h
+++ b/drivers/scsi/be2iscsi/be_iscsi.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2010 ServerEngines
+ * Copyright (C) 2005 - 2011 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -7,15 +7,14 @@
* as published by the Free Software Foundation. The full GNU General
* Public License is included in this distribution in the file called COPYING.
*
- * Written by: Jayamohan Kallickal (jayamohank@serverengines.com)
+ * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
*
* Contact Information:
- * linux-drivers@serverengines.com
- *
- * ServerEngines
- * 209 N. Fair Oaks Ave
- * Sunnyvale, CA 94085
+ * linux-drivers@emulex.com
*
+ * Emulex
+ * 3333 Susan Street
+ * Costa Mesa, CA 92626
*/
#ifndef _BE_ISCSI_
diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c
index 24e20ba9633..cea9b275965 100644
--- a/drivers/scsi/be2iscsi/be_main.c
+++ b/drivers/scsi/be2iscsi/be_main.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2010 ServerEngines
+ * Copyright (C) 2005 - 2011 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -7,16 +7,16 @@
* as published by the Free Software Foundation. The full GNU General
* Public License is included in this distribution in the file called COPYING.
*
- * Written by: Jayamohan Kallickal (jayamohank@serverengines.com)
+ * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
*
* Contact Information:
- * linux-drivers@serverengines.com
- *
- * ServerEngines
- * 209 N. Fair Oaks Ave
- * Sunnyvale, CA 94085
+ * linux-drivers@emulex.com
*
+ * Emulex
+ * 3333 Susan Street
+ * Costa Mesa, CA 92626
*/
+
#include <linux/reboot.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -420,7 +420,8 @@ static int beiscsi_setup_boot_info(struct beiscsi_hba *phba)
return 0;
free_kset:
- iscsi_boot_destroy_kset(phba->boot_kset);
+ if (phba->boot_kset)
+ iscsi_boot_destroy_kset(phba->boot_kset);
return -ENOMEM;
}
@@ -3464,23 +3465,23 @@ static void hwi_enable_intr(struct beiscsi_hba *phba)
addr = (u8 __iomem *) ((u8 __iomem *) ctrl->pcicfg +
PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET);
reg = ioread32(addr);
- SE_DEBUG(DBG_LVL_8, "reg =x%08x\n", reg);
enabled = reg & MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
if (!enabled) {
reg |= MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
SE_DEBUG(DBG_LVL_8, "reg =x%08x addr=%p\n", reg, addr);
iowrite32(reg, addr);
- if (!phba->msix_enabled) {
- eq = &phwi_context->be_eq[0].q;
+ }
+
+ if (!phba->msix_enabled) {
+ eq = &phwi_context->be_eq[0].q;
+ SE_DEBUG(DBG_LVL_8, "eq->id=%d\n", eq->id);
+ hwi_ring_eq_db(phba, eq->id, 0, 0, 1, 1);
+ } else {
+ for (i = 0; i <= phba->num_cpus; i++) {
+ eq = &phwi_context->be_eq[i].q;
SE_DEBUG(DBG_LVL_8, "eq->id=%d\n", eq->id);
hwi_ring_eq_db(phba, eq->id, 0, 0, 1, 1);
- } else {
- for (i = 0; i <= phba->num_cpus; i++) {
- eq = &phwi_context->be_eq[i].q;
- SE_DEBUG(DBG_LVL_8, "eq->id=%d\n", eq->id);
- hwi_ring_eq_db(phba, eq->id, 0, 0, 1, 1);
- }
}
}
}
@@ -4019,12 +4020,17 @@ static int beiscsi_mtask(struct iscsi_task *task)
hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_NOOP_OUT:
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
- INI_RD_CMD);
- if (task->hdr->ttt == ISCSI_RESERVED_TAG)
+ if (task->hdr->ttt != ISCSI_RESERVED_TAG) {
+ AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+ TGT_DM_CMD);
+ AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt,
+ pwrb, 0);
AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
- else
+ } else {
+ AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+ INI_RD_CMD);
AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 1);
+ }
hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_TEXT:
@@ -4144,10 +4150,11 @@ static void beiscsi_remove(struct pci_dev *pcidev)
phba->ctrl.mbox_mem_alloced.size,
phba->ctrl.mbox_mem_alloced.va,
phba->ctrl.mbox_mem_alloced.dma);
+ if (phba->boot_kset)
+ iscsi_boot_destroy_kset(phba->boot_kset);
iscsi_host_remove(phba->shost);
pci_dev_put(phba->pcidev);
iscsi_host_free(phba->shost);
- iscsi_boot_destroy_kset(phba->boot_kset);
}
static void beiscsi_msix_enable(struct beiscsi_hba *phba)
diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h
index 90eb74f6bca..081c171a1ed 100644
--- a/drivers/scsi/be2iscsi/be_main.h
+++ b/drivers/scsi/be2iscsi/be_main.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2010 ServerEngines
+ * Copyright (C) 2005 - 2011 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -7,15 +7,14 @@
* as published by the Free Software Foundation. The full GNU General
* Public License is included in this distribution in the file called COPYING.
*
- * Written by: Jayamohan Kallickal (jayamohank@serverengines.com)
+ * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
*
* Contact Information:
- * linux-drivers@serverengines.com
- *
- * ServerEngines
- * 209 N. Fair Oaks Ave
- * Sunnyvale, CA 94085
+ * linux-drivers@emulex.com
*
+ * Emulex
+ * 3333 Susan Street
+ * Costa Mesa, CA 92626
*/
#ifndef _BEISCSI_MAIN_
@@ -35,7 +34,7 @@
#include "be.h"
#define DRV_NAME "be2iscsi"
-#define BUILD_STR "2.0.549.0"
+#define BUILD_STR "2.103.298.0"
#define BE_NAME "ServerEngines BladeEngine2" \
"Linux iSCSI Driver version" BUILD_STR
#define DRV_DESC BE_NAME " " "Driver"
diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c
index 877324fc594..44762cfa3e1 100644
--- a/drivers/scsi/be2iscsi/be_mgmt.c
+++ b/drivers/scsi/be2iscsi/be_mgmt.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2010 ServerEngines
+ * Copyright (C) 2005 - 2011 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -7,15 +7,14 @@
* as published by the Free Software Foundation. The full GNU General
* Public License is included in this distribution in the file called COPYING.
*
- * Written by: Jayamohan Kallickal (jayamohank@serverengines.com)
+ * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
*
* Contact Information:
- * linux-drivers@serverengines.com
- *
- * ServerEngines
- * 209 N. Fair Oaks Ave
- * Sunnyvale, CA 94085
+ * linux-drivers@emulex.com
*
+ * Emulex
+ * 3333 Susan Street
+ * Costa Mesa, CA 92626
*/
#include "be_mgmt.h"
@@ -203,8 +202,8 @@ int mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short chute)
OPCODE_COMMON_ISCSI_CLEANUP, sizeof(*req));
req->chute = chute;
- req->hdr_ring_id = 0;
- req->data_ring_id = 0;
+ req->hdr_ring_id = cpu_to_le16(HWI_GET_DEF_HDRQ_ID(phba));
+ req->data_ring_id = cpu_to_le16(HWI_GET_DEF_BUFQ_ID(phba));
status = be_mcc_notify_wait(phba);
if (status)
diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h
index b9acedf7865..08428824ace 100644
--- a/drivers/scsi/be2iscsi/be_mgmt.h
+++ b/drivers/scsi/be2iscsi/be_mgmt.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2010 ServerEngines
+ * Copyright (C) 2005 - 2011 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -7,15 +7,14 @@
* as published by the Free Software Foundation. The full GNU General
* Public License is included in this distribution in the file called COPYING.
*
- * Written by: Jayamohan Kallickal (jayamohank@serverengines.com)
+ * Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
*
* Contact Information:
- * linux-drivers@serverengines.com
- *
- * ServerEngines
- * 209 N. Fair Oaks Ave
- * Sunnyvale, CA 94085
+ * linux-drivers@emulex.com
*
+ * Emulex
+ * 3333 Susan Street
+ * Costa Mesa, CA 92626
*/
#ifndef _BEISCSI_MGMT_
diff --git a/drivers/scsi/bfa/bfad.c b/drivers/scsi/bfa/bfad.c
index 0fd510a0156..59b5e9b61d7 100644
--- a/drivers/scsi/bfa/bfad.c
+++ b/drivers/scsi/bfa/bfad.c
@@ -57,9 +57,19 @@ int pcie_max_read_reqsz;
int bfa_debugfs_enable = 1;
int msix_disable_cb = 0, msix_disable_ct = 0;
+/* Firmware releated */
u32 bfi_image_ct_fc_size, bfi_image_ct_cna_size, bfi_image_cb_fc_size;
u32 *bfi_image_ct_fc, *bfi_image_ct_cna, *bfi_image_cb_fc;
+#define BFAD_FW_FILE_CT_FC "ctfw_fc.bin"
+#define BFAD_FW_FILE_CT_CNA "ctfw_cna.bin"
+#define BFAD_FW_FILE_CB_FC "cbfw_fc.bin"
+
+static u32 *bfad_load_fwimg(struct pci_dev *pdev);
+static void bfad_free_fwimg(void);
+static void bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image,
+ u32 *bfi_image_size, char *fw_name);
+
static const char *msix_name_ct[] = {
"cpe0", "cpe1", "cpe2", "cpe3",
"rme0", "rme1", "rme2", "rme3",
@@ -222,6 +232,9 @@ bfad_sm_created(struct bfad_s *bfad, enum bfad_sm_event event)
if ((bfad->bfad_flags & BFAD_HAL_INIT_DONE)) {
bfa_sm_send_event(bfad, BFAD_E_INIT_SUCCESS);
} else {
+ printk(KERN_WARNING
+ "bfa %s: bfa init failed\n",
+ bfad->pci_name);
bfad->bfad_flags |= BFAD_HAL_INIT_FAIL;
bfa_sm_send_event(bfad, BFAD_E_INIT_FAILED);
}
@@ -991,10 +1004,6 @@ bfad_cfg_pport(struct bfad_s *bfad, enum bfa_lport_role role)
bfad->pport.roles |= BFA_LPORT_ROLE_FCP_IM;
}
- /* Setup the debugfs node for this scsi_host */
- if (bfa_debugfs_enable)
- bfad_debugfs_init(&bfad->pport);
-
bfad->bfad_flags |= BFAD_CFG_PPORT_DONE;
out:
@@ -1004,10 +1013,6 @@ out:
void
bfad_uncfg_pport(struct bfad_s *bfad)
{
- /* Remove the debugfs node for this scsi_host */
- kfree(bfad->regdata);
- bfad_debugfs_exit(&bfad->pport);
-
if ((supported_fc4s & BFA_LPORT_ROLE_FCP_IM) &&
(bfad->pport.roles & BFA_LPORT_ROLE_FCP_IM)) {
bfad_im_scsi_host_free(bfad, bfad->pport.im_port);
@@ -1389,6 +1394,10 @@ bfad_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid)
bfad->pport.bfad = bfad;
INIT_LIST_HEAD(&bfad->pbc_vport_list);
+ /* Setup the debugfs node for this bfad */
+ if (bfa_debugfs_enable)
+ bfad_debugfs_init(&bfad->pport);
+
retval = bfad_drv_init(bfad);
if (retval != BFA_STATUS_OK)
goto out_drv_init_failure;
@@ -1404,6 +1413,9 @@ out_bfad_sm_failure:
bfa_detach(&bfad->bfa);
bfad_hal_mem_release(bfad);
out_drv_init_failure:
+ /* Remove the debugfs node for this bfad */
+ kfree(bfad->regdata);
+ bfad_debugfs_exit(&bfad->pport);
mutex_lock(&bfad_mutex);
bfad_inst--;
list_del(&bfad->list_entry);
@@ -1445,6 +1457,10 @@ bfad_pci_remove(struct pci_dev *pdev)
spin_unlock_irqrestore(&bfad->bfad_lock, flags);
bfad_hal_mem_release(bfad);
+ /* Remove the debugfs node for this bfad */
+ kfree(bfad->regdata);
+ bfad_debugfs_exit(&bfad->pport);
+
/* Cleaning the BFAD instance */
mutex_lock(&bfad_mutex);
bfad_inst--;
@@ -1550,7 +1566,7 @@ bfad_exit(void)
}
/* Firmware handling */
-u32 *
+static void
bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image,
u32 *bfi_image_size, char *fw_name)
{
@@ -1558,27 +1574,25 @@ bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image,
if (request_firmware(&fw, fw_name, &pdev->dev)) {
printk(KERN_ALERT "Can't locate firmware %s\n", fw_name);
- goto error;
+ *bfi_image = NULL;
+ goto out;
}
*bfi_image = vmalloc(fw->size);
if (NULL == *bfi_image) {
printk(KERN_ALERT "Fail to allocate buffer for fw image "
"size=%x!\n", (u32) fw->size);
- goto error;
+ goto out;
}
memcpy(*bfi_image, fw->data, fw->size);
*bfi_image_size = fw->size/sizeof(u32);
-
- return *bfi_image;
-
-error:
- return NULL;
+out:
+ release_firmware(fw);
}
-u32 *
-bfad_get_firmware_buf(struct pci_dev *pdev)
+static u32 *
+bfad_load_fwimg(struct pci_dev *pdev)
{
if (pdev->device == BFA_PCI_DEVICE_ID_CT_FC) {
if (bfi_image_ct_fc_size == 0)
@@ -1598,6 +1612,17 @@ bfad_get_firmware_buf(struct pci_dev *pdev)
}
}
+static void
+bfad_free_fwimg(void)
+{
+ if (bfi_image_ct_fc_size && bfi_image_ct_fc)
+ vfree(bfi_image_ct_fc);
+ if (bfi_image_ct_cna_size && bfi_image_ct_cna)
+ vfree(bfi_image_ct_cna);
+ if (bfi_image_cb_fc_size && bfi_image_cb_fc)
+ vfree(bfi_image_cb_fc);
+}
+
module_init(bfad_init);
module_exit(bfad_exit);
MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/bfa/bfad_debugfs.c b/drivers/scsi/bfa/bfad_debugfs.c
index c66e32eced7..48be0c54f2d 100644
--- a/drivers/scsi/bfa/bfad_debugfs.c
+++ b/drivers/scsi/bfa/bfad_debugfs.c
@@ -28,10 +28,10 @@
* mount -t debugfs none /sys/kernel/debug
*
* BFA Hierarchy:
- * - bfa/host#
- * where the host number corresponds to the one under /sys/class/scsi_host/host#
+ * - bfa/pci_dev:<pci_name>
+ * where the pci_name corresponds to the one under /sys/bus/pci/drivers/bfa
*
- * Debugging service available per host:
+ * Debugging service available per pci_dev:
* fwtrc: To collect current firmware trace.
* drvtrc: To collect current driver trace
* fwsave: To collect last saved fw trace as a result of firmware crash.
@@ -489,11 +489,9 @@ static atomic_t bfa_debugfs_port_count;
inline void
bfad_debugfs_init(struct bfad_port_s *port)
{
- struct bfad_im_port_s *im_port = port->im_port;
- struct bfad_s *bfad = im_port->bfad;
- struct Scsi_Host *shost = im_port->shost;
+ struct bfad_s *bfad = port->bfad;
const struct bfad_debugfs_entry *file;
- char name[16];
+ char name[64];
int i;
if (!bfa_debugfs_enable)
@@ -510,17 +508,15 @@ bfad_debugfs_init(struct bfad_port_s *port)
}
}
- /*
- * Setup the host# directory for the port,
- * corresponds to the scsi_host num of this port.
- */
- snprintf(name, sizeof(name), "host%d", shost->host_no);
+ /* Setup the pci_dev debugfs directory for the port */
+ snprintf(name, sizeof(name), "pci_dev:%s", bfad->pci_name);
if (!port->port_debugfs_root) {
port->port_debugfs_root =
debugfs_create_dir(name, bfa_debugfs_root);
if (!port->port_debugfs_root) {
printk(KERN_WARNING
- "BFA host root dir creation failed\n");
+ "bfa %s: debugfs root creation failed\n",
+ bfad->pci_name);
goto err;
}
@@ -536,8 +532,8 @@ bfad_debugfs_init(struct bfad_port_s *port)
file->fops);
if (!bfad->bfad_dentry_files[i]) {
printk(KERN_WARNING
- "BFA host%d: create %s entry failed\n",
- shost->host_no, file->name);
+ "bfa %s: debugfs %s creation failed\n",
+ bfad->pci_name, file->name);
goto err;
}
}
@@ -550,8 +546,7 @@ err:
inline void
bfad_debugfs_exit(struct bfad_port_s *port)
{
- struct bfad_im_port_s *im_port = port->im_port;
- struct bfad_s *bfad = im_port->bfad;
+ struct bfad_s *bfad = port->bfad;
int i;
for (i = 0; i < ARRAY_SIZE(bfad_debugfs_files); i++) {
@@ -562,9 +557,7 @@ bfad_debugfs_exit(struct bfad_port_s *port)
}
/*
- * Remove the host# directory for the port,
- * corresponds to the scsi_host num of this port.
- */
+ * Remove the pci_dev debugfs directory for the port */
if (port->port_debugfs_root) {
debugfs_remove(port->port_debugfs_root);
port->port_debugfs_root = NULL;
diff --git a/drivers/scsi/bfa/bfad_im.h b/drivers/scsi/bfa/bfad_im.h
index bfee63b16fa..c296c896851 100644
--- a/drivers/scsi/bfa/bfad_im.h
+++ b/drivers/scsi/bfa/bfad_im.h
@@ -141,29 +141,4 @@ extern struct device_attribute *bfad_im_vport_attrs[];
irqreturn_t bfad_intx(int irq, void *dev_id);
-/* Firmware releated */
-#define BFAD_FW_FILE_CT_FC "ctfw_fc.bin"
-#define BFAD_FW_FILE_CT_CNA "ctfw_cna.bin"
-#define BFAD_FW_FILE_CB_FC "cbfw_fc.bin"
-
-u32 *bfad_get_firmware_buf(struct pci_dev *pdev);
-u32 *bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image,
- u32 *bfi_image_size, char *fw_name);
-
-static inline u32 *
-bfad_load_fwimg(struct pci_dev *pdev)
-{
- return bfad_get_firmware_buf(pdev);
-}
-
-static inline void
-bfad_free_fwimg(void)
-{
- if (bfi_image_ct_fc_size && bfi_image_ct_fc)
- vfree(bfi_image_ct_fc);
- if (bfi_image_ct_cna_size && bfi_image_ct_cna)
- vfree(bfi_image_ct_cna);
- if (bfi_image_cb_fc_size && bfi_image_cb_fc)
- vfree(bfi_image_cb_fc);
-}
#endif
diff --git a/drivers/scsi/bnx2fc/bnx2fc.h b/drivers/scsi/bnx2fc/bnx2fc.h
index b6d350ac428..0a404bfb44f 100644
--- a/drivers/scsi/bnx2fc/bnx2fc.h
+++ b/drivers/scsi/bnx2fc/bnx2fc.h
@@ -130,7 +130,7 @@
#define BNX2FC_TM_TIMEOUT 60 /* secs */
#define BNX2FC_IO_TIMEOUT 20000UL /* msecs */
-#define BNX2FC_WAIT_CNT 120
+#define BNX2FC_WAIT_CNT 1200
#define BNX2FC_FW_TIMEOUT (3 * HZ)
#define PORT_MAX 2
diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
index cd050196a16..ab255fbc7f3 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
@@ -1133,7 +1133,7 @@ static void bnx2fc_interface_release(struct kref *kref)
struct net_device *phys_dev;
hba = container_of(kref, struct bnx2fc_hba, kref);
- BNX2FC_HBA_DBG(hba->ctlr.lp, "Interface is being released\n");
+ BNX2FC_MISC_DBG("Interface is being released\n");
netdev = hba->netdev;
phys_dev = hba->phys_dev;
@@ -1257,20 +1257,17 @@ setup_err:
static struct fc_lport *bnx2fc_if_create(struct bnx2fc_hba *hba,
struct device *parent, int npiv)
{
- struct fc_lport *lport = NULL;
+ struct fc_lport *lport, *n_port;
struct fcoe_port *port;
struct Scsi_Host *shost;
struct fc_vport *vport = dev_to_vport(parent);
int rc = 0;
/* Allocate Scsi_Host structure */
- if (!npiv) {
- lport = libfc_host_alloc(&bnx2fc_shost_template,
- sizeof(struct fcoe_port));
- } else {
- lport = libfc_vport_create(vport,
- sizeof(struct fcoe_port));
- }
+ if (!npiv)
+ lport = libfc_host_alloc(&bnx2fc_shost_template, sizeof(*port));
+ else
+ lport = libfc_vport_create(vport, sizeof(*port));
if (!lport) {
printk(KERN_ERR PFX "could not allocate scsi host structure\n");
@@ -1288,7 +1285,6 @@ static struct fc_lport *bnx2fc_if_create(struct bnx2fc_hba *hba,
goto lp_config_err;
if (npiv) {
- vport = dev_to_vport(parent);
printk(KERN_ERR PFX "Setting vport names, 0x%llX 0x%llX\n",
vport->node_name, vport->port_name);
fc_set_wwnn(lport, vport->node_name);
@@ -1317,12 +1313,17 @@ static struct fc_lport *bnx2fc_if_create(struct bnx2fc_hba *hba,
fc_host_port_type(lport->host) = FC_PORTTYPE_UNKNOWN;
/* Allocate exchange manager */
- if (!npiv) {
+ if (!npiv)
rc = bnx2fc_em_config(lport);
- if (rc) {
- printk(KERN_ERR PFX "Error on bnx2fc_em_config\n");
- goto shost_err;
- }
+ else {
+ shost = vport_to_shost(vport);
+ n_port = shost_priv(shost);
+ rc = fc_exch_mgr_list_clone(n_port, lport);
+ }
+
+ if (rc) {
+ printk(KERN_ERR PFX "Error on bnx2fc_em_config\n");
+ goto shost_err;
}
bnx2fc_interface_get(hba);
@@ -1355,8 +1356,6 @@ static void bnx2fc_if_destroy(struct fc_lport *lport)
/* Free existing transmit skbs */
fcoe_clean_pending_queue(lport);
- bnx2fc_interface_put(hba);
-
/* Free queued packets for the receive thread */
bnx2fc_clean_rx_queue(lport);
@@ -1375,6 +1374,8 @@ static void bnx2fc_if_destroy(struct fc_lport *lport)
/* Release Scsi_Host */
scsi_host_put(lport->host);
+
+ bnx2fc_interface_put(hba);
}
/**
diff --git a/drivers/scsi/bnx2fc/bnx2fc_hwi.c b/drivers/scsi/bnx2fc/bnx2fc_hwi.c
index 1b680e288c5..f756d5f85c7 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_hwi.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_hwi.c
@@ -522,6 +522,7 @@ void bnx2fc_process_l2_frame_compl(struct bnx2fc_rport *tgt,
fp = fc_frame_alloc(lport, payload_len);
if (!fp) {
printk(KERN_ERR PFX "fc_frame_alloc failure\n");
+ kfree(unsol_els);
return;
}
@@ -547,6 +548,7 @@ void bnx2fc_process_l2_frame_compl(struct bnx2fc_rport *tgt,
*/
printk(KERN_ERR PFX "dropping ELS 0x%x\n", op);
kfree_skb(skb);
+ kfree(unsol_els);
return;
}
}
@@ -563,6 +565,7 @@ void bnx2fc_process_l2_frame_compl(struct bnx2fc_rport *tgt,
} else {
BNX2FC_HBA_DBG(lport, "fh_r_ctl = 0x%x\n", fh->fh_r_ctl);
kfree_skb(skb);
+ kfree(unsol_els);
}
}
diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c
index 1decefbf32e..b5b5c346d77 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_io.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_io.c
@@ -1663,6 +1663,12 @@ int bnx2fc_queuecommand(struct Scsi_Host *host,
tgt = (struct bnx2fc_rport *)&rp[1];
if (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) {
+ if (test_bit(BNX2FC_FLAG_UPLD_REQ_COMPL, &tgt->flags)) {
+ sc_cmd->result = DID_NO_CONNECT << 16;
+ sc_cmd->scsi_done(sc_cmd);
+ return 0;
+
+ }
/*
* Session is not offloaded yet. Let SCSI-ml retry
* the command.
diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c
index d0c82340f0e..60d2ef29164 100644
--- a/drivers/scsi/constants.c
+++ b/drivers/scsi/constants.c
@@ -772,6 +772,7 @@ static const struct error_info additional[] =
{0x3802, "Esn - power management class event"},
{0x3804, "Esn - media class event"},
{0x3806, "Esn - device busy class event"},
+ {0x3807, "Thin Provisioning soft threshold reached"},
{0x3900, "Saving parameters not supported"},
diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c
index b10b3841535..f5b718d3c31 100644
--- a/drivers/scsi/dc395x.c
+++ b/drivers/scsi/dc395x.c
@@ -778,8 +778,8 @@ static void srb_free_insert(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
static void srb_waiting_insert(struct DeviceCtlBlk *dcb,
struct ScsiReqBlk *srb)
{
- dprintkdbg(DBG_0, "srb_waiting_insert: (pid#%li) <%02i-%i> srb=%p\n",
- srb->cmd->serial_number, dcb->target_id, dcb->target_lun, srb);
+ dprintkdbg(DBG_0, "srb_waiting_insert: (0x%p) <%02i-%i> srb=%p\n",
+ srb->cmd, dcb->target_id, dcb->target_lun, srb);
list_add(&srb->list, &dcb->srb_waiting_list);
}
@@ -787,16 +787,16 @@ static void srb_waiting_insert(struct DeviceCtlBlk *dcb,
static void srb_waiting_append(struct DeviceCtlBlk *dcb,
struct ScsiReqBlk *srb)
{
- dprintkdbg(DBG_0, "srb_waiting_append: (pid#%li) <%02i-%i> srb=%p\n",
- srb->cmd->serial_number, dcb->target_id, dcb->target_lun, srb);
+ dprintkdbg(DBG_0, "srb_waiting_append: (0x%p) <%02i-%i> srb=%p\n",
+ srb->cmd, dcb->target_id, dcb->target_lun, srb);
list_add_tail(&srb->list, &dcb->srb_waiting_list);
}
static void srb_going_append(struct DeviceCtlBlk *dcb, struct ScsiReqBlk *srb)
{
- dprintkdbg(DBG_0, "srb_going_append: (pid#%li) <%02i-%i> srb=%p\n",
- srb->cmd->serial_number, dcb->target_id, dcb->target_lun, srb);
+ dprintkdbg(DBG_0, "srb_going_append: (0x%p) <%02i-%i> srb=%p\n",
+ srb->cmd, dcb->target_id, dcb->target_lun, srb);
list_add_tail(&srb->list, &dcb->srb_going_list);
}
@@ -805,8 +805,8 @@ static void srb_going_remove(struct DeviceCtlBlk *dcb, struct ScsiReqBlk *srb)
{
struct ScsiReqBlk *i;
struct ScsiReqBlk *tmp;
- dprintkdbg(DBG_0, "srb_going_remove: (pid#%li) <%02i-%i> srb=%p\n",
- srb->cmd->serial_number, dcb->target_id, dcb->target_lun, srb);
+ dprintkdbg(DBG_0, "srb_going_remove: (0x%p) <%02i-%i> srb=%p\n",
+ srb->cmd, dcb->target_id, dcb->target_lun, srb);
list_for_each_entry_safe(i, tmp, &dcb->srb_going_list, list)
if (i == srb) {
@@ -821,8 +821,8 @@ static void srb_waiting_remove(struct DeviceCtlBlk *dcb,
{
struct ScsiReqBlk *i;
struct ScsiReqBlk *tmp;
- dprintkdbg(DBG_0, "srb_waiting_remove: (pid#%li) <%02i-%i> srb=%p\n",
- srb->cmd->serial_number, dcb->target_id, dcb->target_lun, srb);
+ dprintkdbg(DBG_0, "srb_waiting_remove: (0x%p) <%02i-%i> srb=%p\n",
+ srb->cmd, dcb->target_id, dcb->target_lun, srb);
list_for_each_entry_safe(i, tmp, &dcb->srb_waiting_list, list)
if (i == srb) {
@@ -836,8 +836,8 @@ static void srb_going_to_waiting_move(struct DeviceCtlBlk *dcb,
struct ScsiReqBlk *srb)
{
dprintkdbg(DBG_0,
- "srb_going_to_waiting_move: (pid#%li) <%02i-%i> srb=%p\n",
- srb->cmd->serial_number, dcb->target_id, dcb->target_lun, srb);
+ "srb_going_to_waiting_move: (0x%p) <%02i-%i> srb=%p\n",
+ srb->cmd, dcb->target_id, dcb->target_lun, srb);
list_move(&srb->list, &dcb->srb_waiting_list);
}
@@ -846,8 +846,8 @@ static void srb_waiting_to_going_move(struct DeviceCtlBlk *dcb,
struct ScsiReqBlk *srb)
{
dprintkdbg(DBG_0,
- "srb_waiting_to_going_move: (pid#%li) <%02i-%i> srb=%p\n",
- srb->cmd->serial_number, dcb->target_id, dcb->target_lun, srb);
+ "srb_waiting_to_going_move: (0x%p) <%02i-%i> srb=%p\n",
+ srb->cmd, dcb->target_id, dcb->target_lun, srb);
list_move(&srb->list, &dcb->srb_going_list);
}
@@ -982,8 +982,8 @@ static void build_srb(struct scsi_cmnd *cmd, struct DeviceCtlBlk *dcb,
{
int nseg;
enum dma_data_direction dir = cmd->sc_data_direction;
- dprintkdbg(DBG_0, "build_srb: (pid#%li) <%02i-%i>\n",
- cmd->serial_number, dcb->target_id, dcb->target_lun);
+ dprintkdbg(DBG_0, "build_srb: (0x%p) <%02i-%i>\n",
+ cmd, dcb->target_id, dcb->target_lun);
srb->dcb = dcb;
srb->cmd = cmd;
@@ -1086,8 +1086,8 @@ static int dc395x_queue_command_lck(struct scsi_cmnd *cmd, void (*done)(struct s
struct ScsiReqBlk *srb;
struct AdapterCtlBlk *acb =
(struct AdapterCtlBlk *)cmd->device->host->hostdata;
- dprintkdbg(DBG_0, "queue_command: (pid#%li) <%02i-%i> cmnd=0x%02x\n",
- cmd->serial_number, cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
+ dprintkdbg(DBG_0, "queue_command: (0x%p) <%02i-%i> cmnd=0x%02x\n",
+ cmd, cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
/* Assume BAD_TARGET; will be cleared later */
cmd->result = DID_BAD_TARGET << 16;
@@ -1140,7 +1140,7 @@ static int dc395x_queue_command_lck(struct scsi_cmnd *cmd, void (*done)(struct s
/* process immediately */
send_srb(acb, srb);
}
- dprintkdbg(DBG_1, "queue_command: (pid#%li) done\n", cmd->serial_number);
+ dprintkdbg(DBG_1, "queue_command: (0x%p) done\n", cmd);
return 0;
complete:
@@ -1203,9 +1203,9 @@ static void dump_register_info(struct AdapterCtlBlk *acb,
dprintkl(KERN_INFO, "dump: srb=%p cmd=%p OOOPS!\n",
srb, srb->cmd);
else
- dprintkl(KERN_INFO, "dump: srb=%p cmd=%p (pid#%li) "
+ dprintkl(KERN_INFO, "dump: srb=%p cmd=%p "
"cmnd=0x%02x <%02i-%i>\n",
- srb, srb->cmd, srb->cmd->serial_number,
+ srb, srb->cmd,
srb->cmd->cmnd[0], srb->cmd->device->id,
srb->cmd->device->lun);
printk(" sglist=%p cnt=%i idx=%i len=%zu\n",
@@ -1301,8 +1301,8 @@ static int __dc395x_eh_bus_reset(struct scsi_cmnd *cmd)
struct AdapterCtlBlk *acb =
(struct AdapterCtlBlk *)cmd->device->host->hostdata;
dprintkl(KERN_INFO,
- "eh_bus_reset: (pid#%li) target=<%02i-%i> cmd=%p\n",
- cmd->serial_number, cmd->device->id, cmd->device->lun, cmd);
+ "eh_bus_reset: (0%p) target=<%02i-%i> cmd=%p\n",
+ cmd, cmd->device->id, cmd->device->lun, cmd);
if (timer_pending(&acb->waiting_timer))
del_timer(&acb->waiting_timer);
@@ -1368,8 +1368,8 @@ static int dc395x_eh_abort(struct scsi_cmnd *cmd)
(struct AdapterCtlBlk *)cmd->device->host->hostdata;
struct DeviceCtlBlk *dcb;
struct ScsiReqBlk *srb;
- dprintkl(KERN_INFO, "eh_abort: (pid#%li) target=<%02i-%i> cmd=%p\n",
- cmd->serial_number, cmd->device->id, cmd->device->lun, cmd);
+ dprintkl(KERN_INFO, "eh_abort: (0x%p) target=<%02i-%i> cmd=%p\n",
+ cmd, cmd->device->id, cmd->device->lun, cmd);
dcb = find_dcb(acb, cmd->device->id, cmd->device->lun);
if (!dcb) {
@@ -1495,8 +1495,8 @@ static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb,
u16 s_stat2, return_code;
u8 s_stat, scsicommand, i, identify_message;
u8 *ptr;
- dprintkdbg(DBG_0, "start_scsi: (pid#%li) <%02i-%i> srb=%p\n",
- srb->cmd->serial_number, dcb->target_id, dcb->target_lun, srb);
+ dprintkdbg(DBG_0, "start_scsi: (0x%p) <%02i-%i> srb=%p\n",
+ dcb->target_id, dcb->target_lun, srb);
srb->tag_number = TAG_NONE; /* acb->tag_max_num: had error read in eeprom */
@@ -1505,8 +1505,8 @@ static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb,
s_stat2 = DC395x_read16(acb, TRM_S1040_SCSI_STATUS);
#if 1
if (s_stat & 0x20 /* s_stat2 & 0x02000 */ ) {
- dprintkdbg(DBG_KG, "start_scsi: (pid#%li) BUSY %02x %04x\n",
- srb->cmd->serial_number, s_stat, s_stat2);
+ dprintkdbg(DBG_KG, "start_scsi: (0x%p) BUSY %02x %04x\n",
+ s_stat, s_stat2);
/*
* Try anyway?
*
@@ -1522,16 +1522,15 @@ static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb,
}
#endif
if (acb->active_dcb) {
- dprintkl(KERN_DEBUG, "start_scsi: (pid#%li) Attempt to start a"
- "command while another command (pid#%li) is active.",
- srb->cmd->serial_number,
+ dprintkl(KERN_DEBUG, "start_scsi: (0x%p) Attempt to start a"
+ "command while another command (0x%p) is active.",
+ srb->cmd,
acb->active_dcb->active_srb ?
- acb->active_dcb->active_srb->cmd->serial_number : 0);
+ acb->active_dcb->active_srb->cmd : 0);
return 1;
}
if (DC395x_read16(acb, TRM_S1040_SCSI_STATUS) & SCSIINTERRUPT) {
- dprintkdbg(DBG_KG, "start_scsi: (pid#%li) Failed (busy)\n",
- srb->cmd->serial_number);
+ dprintkdbg(DBG_KG, "start_scsi: (0x%p) Failed (busy)\n", srb->cmd);
return 1;
}
/* Allow starting of SCSI commands half a second before we allow the mid-level
@@ -1603,9 +1602,9 @@ static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb,
tag_number++;
}
if (tag_number >= dcb->max_command) {
- dprintkl(KERN_WARNING, "start_scsi: (pid#%li) "
+ dprintkl(KERN_WARNING, "start_scsi: (0x%p) "
"Out of tags target=<%02i-%i>)\n",
- srb->cmd->serial_number, srb->cmd->device->id,
+ srb->cmd, srb->cmd->device->id,
srb->cmd->device->lun);
srb->state = SRB_READY;
DC395x_write16(acb, TRM_S1040_SCSI_CONTROL,
@@ -1623,8 +1622,8 @@ static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb,
#endif
/*polling:*/
/* Send CDB ..command block ......... */
- dprintkdbg(DBG_KG, "start_scsi: (pid#%li) <%02i-%i> cmnd=0x%02x tag=%i\n",
- srb->cmd->serial_number, srb->cmd->device->id, srb->cmd->device->lun,
+ dprintkdbg(DBG_KG, "start_scsi: (0x%p) <%02i-%i> cmnd=0x%02x tag=%i\n",
+ srb->cmd, srb->cmd->device->id, srb->cmd->device->lun,
srb->cmd->cmnd[0], srb->tag_number);
if (srb->flag & AUTO_REQSENSE) {
DC395x_write8(acb, TRM_S1040_SCSI_FIFO, REQUEST_SENSE);
@@ -1647,8 +1646,8 @@ static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb,
* we caught an interrupt (must be reset or reselection ... )
* : Let's process it first!
*/
- dprintkdbg(DBG_0, "start_scsi: (pid#%li) <%02i-%i> Failed - busy\n",
- srb->cmd->serial_number, dcb->target_id, dcb->target_lun);
+ dprintkdbg(DBG_0, "start_scsi: (0x%p) <%02i-%i> Failed - busy\n",
+ srb->cmd, dcb->target_id, dcb->target_lun);
srb->state = SRB_READY;
free_tag(dcb, srb);
srb->msg_count = 0;
@@ -1843,7 +1842,7 @@ static irqreturn_t dc395x_interrupt(int irq, void *dev_id)
static void msgout_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
u16 *pscsi_status)
{
- dprintkdbg(DBG_0, "msgout_phase0: (pid#%li)\n", srb->cmd->serial_number);
+ dprintkdbg(DBG_0, "msgout_phase0: (0x%p)\n", srb->cmd);
if (srb->state & (SRB_UNEXPECT_RESEL + SRB_ABORT_SENT))
*pscsi_status = PH_BUS_FREE; /*.. initial phase */
@@ -1857,18 +1856,18 @@ static void msgout_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
{
u16 i;
u8 *ptr;
- dprintkdbg(DBG_0, "msgout_phase1: (pid#%li)\n", srb->cmd->serial_number);
+ dprintkdbg(DBG_0, "msgout_phase1: (0x%p)\n", srb->cmd);
clear_fifo(acb, "msgout_phase1");
if (!(srb->state & SRB_MSGOUT)) {
srb->state |= SRB_MSGOUT;
dprintkl(KERN_DEBUG,
- "msgout_phase1: (pid#%li) Phase unexpected\n",
- srb->cmd->serial_number); /* So what ? */
+ "msgout_phase1: (0x%p) Phase unexpected\n",
+ srb->cmd); /* So what ? */
}
if (!srb->msg_count) {
- dprintkdbg(DBG_0, "msgout_phase1: (pid#%li) NOP msg\n",
- srb->cmd->serial_number);
+ dprintkdbg(DBG_0, "msgout_phase1: (0x%p) NOP msg\n",
+ srb->cmd);
DC395x_write8(acb, TRM_S1040_SCSI_FIFO, MSG_NOP);
DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */
DC395x_write8(acb, TRM_S1040_SCSI_COMMAND, SCMD_FIFO_OUT);
@@ -1888,7 +1887,7 @@ static void msgout_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
static void command_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
u16 *pscsi_status)
{
- dprintkdbg(DBG_0, "command_phase0: (pid#%li)\n", srb->cmd->serial_number);
+ dprintkdbg(DBG_0, "command_phase0: (0x%p)\n", srb->cmd);
DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH);
}
@@ -1899,7 +1898,7 @@ static void command_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
struct DeviceCtlBlk *dcb;
u8 *ptr;
u16 i;
- dprintkdbg(DBG_0, "command_phase1: (pid#%li)\n", srb->cmd->serial_number);
+ dprintkdbg(DBG_0, "command_phase1: (0x%p)\n", srb->cmd);
clear_fifo(acb, "command_phase1");
DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_CLRATN);
@@ -2041,8 +2040,8 @@ static void data_out_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
struct DeviceCtlBlk *dcb = srb->dcb;
u16 scsi_status = *pscsi_status;
u32 d_left_counter = 0;
- dprintkdbg(DBG_0, "data_out_phase0: (pid#%li) <%02i-%i>\n",
- srb->cmd->serial_number, srb->cmd->device->id, srb->cmd->device->lun);
+ dprintkdbg(DBG_0, "data_out_phase0: (0x%p) <%02i-%i>\n",
+ srb->cmd, srb->cmd->device->id, srb->cmd->device->lun);
/*
* KG: We need to drain the buffers before we draw any conclusions!
@@ -2171,8 +2170,8 @@ static void data_out_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
static void data_out_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
u16 *pscsi_status)
{
- dprintkdbg(DBG_0, "data_out_phase1: (pid#%li) <%02i-%i>\n",
- srb->cmd->serial_number, srb->cmd->device->id, srb->cmd->device->lun);
+ dprintkdbg(DBG_0, "data_out_phase1: (0x%p) <%02i-%i>\n",
+ srb->cmd, srb->cmd->device->id, srb->cmd->device->lun);
clear_fifo(acb, "data_out_phase1");
/* do prepare before transfer when data out phase */
data_io_transfer(acb, srb, XFERDATAOUT);
@@ -2183,8 +2182,8 @@ static void data_in_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
{
u16 scsi_status = *pscsi_status;
- dprintkdbg(DBG_0, "data_in_phase0: (pid#%li) <%02i-%i>\n",
- srb->cmd->serial_number, srb->cmd->device->id, srb->cmd->device->lun);
+ dprintkdbg(DBG_0, "data_in_phase0: (0x%p) <%02i-%i>\n",
+ srb->cmd, srb->cmd->device->id, srb->cmd->device->lun);
/*
* KG: DataIn is much more tricky than DataOut. When the device is finished
@@ -2204,8 +2203,8 @@ static void data_in_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
unsigned int sc, fc;
if (scsi_status & PARITYERROR) {
- dprintkl(KERN_INFO, "data_in_phase0: (pid#%li) "
- "Parity Error\n", srb->cmd->serial_number);
+ dprintkl(KERN_INFO, "data_in_phase0: (0x%p) "
+ "Parity Error\n", srb->cmd);
srb->status |= PARITY_ERROR;
}
/*
@@ -2394,8 +2393,8 @@ static void data_in_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
static void data_in_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
u16 *pscsi_status)
{
- dprintkdbg(DBG_0, "data_in_phase1: (pid#%li) <%02i-%i>\n",
- srb->cmd->serial_number, srb->cmd->device->id, srb->cmd->device->lun);
+ dprintkdbg(DBG_0, "data_in_phase1: (0x%p) <%02i-%i>\n",
+ srb->cmd, srb->cmd->device->id, srb->cmd->device->lun);
data_io_transfer(acb, srb, XFERDATAIN);
}
@@ -2406,8 +2405,8 @@ static void data_io_transfer(struct AdapterCtlBlk *acb,
struct DeviceCtlBlk *dcb = srb->dcb;
u8 bval;
dprintkdbg(DBG_0,
- "data_io_transfer: (pid#%li) <%02i-%i> %c len=%i, sg=(%i/%i)\n",
- srb->cmd->serial_number, srb->cmd->device->id, srb->cmd->device->lun,
+ "data_io_transfer: (0x%p) <%02i-%i> %c len=%i, sg=(%i/%i)\n",
+ srb->cmd, srb->cmd->device->id, srb->cmd->device->lun,
((io_dir & DMACMD_DIR) ? 'r' : 'w'),
srb->total_xfer_length, srb->sg_index, srb->sg_count);
if (srb == acb->tmp_srb)
@@ -2579,8 +2578,8 @@ static void data_io_transfer(struct AdapterCtlBlk *acb,
static void status_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
u16 *pscsi_status)
{
- dprintkdbg(DBG_0, "status_phase0: (pid#%li) <%02i-%i>\n",
- srb->cmd->serial_number, srb->cmd->device->id, srb->cmd->device->lun);
+ dprintkdbg(DBG_0, "status_phase0: (0x%p) <%02i-%i>\n",
+ srb->cmd, srb->cmd->device->id, srb->cmd->device->lun);
srb->target_status = DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
srb->end_message = DC395x_read8(acb, TRM_S1040_SCSI_FIFO); /* get message */
srb->state = SRB_COMPLETED;
@@ -2593,8 +2592,8 @@ static void status_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
static void status_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
u16 *pscsi_status)
{
- dprintkdbg(DBG_0, "status_phase1: (pid#%li) <%02i-%i>\n",
- srb->cmd->serial_number, srb->cmd->device->id, srb->cmd->device->lun);
+ dprintkdbg(DBG_0, "status_phase1: (0x%p) <%02i-%i>\n",
+ srb->cmd, srb->cmd->device->id, srb->cmd->device->lun);
srb->state = SRB_STATUS;
DC395x_write16(acb, TRM_S1040_SCSI_CONTROL, DO_DATALATCH); /* it's important for atn stop */
DC395x_write8(acb, TRM_S1040_SCSI_COMMAND, SCMD_COMP);
@@ -2635,8 +2634,8 @@ static struct ScsiReqBlk *msgin_qtag(struct AdapterCtlBlk *acb,
{
struct ScsiReqBlk *srb = NULL;
struct ScsiReqBlk *i;
- dprintkdbg(DBG_0, "msgin_qtag: (pid#%li) tag=%i srb=%p\n",
- srb->cmd->serial_number, tag, srb);
+ dprintkdbg(DBG_0, "msgin_qtag: (0x%p) tag=%i srb=%p\n",
+ srb->cmd, tag, srb);
if (!(dcb->tag_mask & (1 << tag)))
dprintkl(KERN_DEBUG,
@@ -2654,8 +2653,8 @@ static struct ScsiReqBlk *msgin_qtag(struct AdapterCtlBlk *acb,
if (!srb)
goto mingx0;
- dprintkdbg(DBG_0, "msgin_qtag: (pid#%li) <%02i-%i>\n",
- srb->cmd->serial_number, srb->dcb->target_id, srb->dcb->target_lun);
+ dprintkdbg(DBG_0, "msgin_qtag: (0x%p) <%02i-%i>\n",
+ srb->cmd, srb->dcb->target_id, srb->dcb->target_lun);
if (dcb->flag & ABORT_DEV_) {
/*srb->state = SRB_ABORT_SENT; */
enable_msgout_abort(acb, srb);
@@ -2865,7 +2864,7 @@ static void msgin_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
u16 *pscsi_status)
{
struct DeviceCtlBlk *dcb = acb->active_dcb;
- dprintkdbg(DBG_0, "msgin_phase0: (pid#%li)\n", srb->cmd->serial_number);
+ dprintkdbg(DBG_0, "msgin_phase0: (0x%p)\n", srb->cmd);
srb->msgin_buf[acb->msg_len++] = DC395x_read8(acb, TRM_S1040_SCSI_FIFO);
if (msgin_completed(srb->msgin_buf, acb->msg_len)) {
@@ -2931,9 +2930,9 @@ static void msgin_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
* SAVE POINTER may be ignored as we have the struct
* ScsiReqBlk* associated with the scsi command.
*/
- dprintkdbg(DBG_0, "msgin_phase0: (pid#%li) "
+ dprintkdbg(DBG_0, "msgin_phase0: (0x%p) "
"SAVE POINTER rem=%i Ignore\n",
- srb->cmd->serial_number, srb->total_xfer_length);
+ srb->cmd, srb->total_xfer_length);
break;
case RESTORE_POINTERS:
@@ -2941,9 +2940,9 @@ static void msgin_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
break;
case ABORT:
- dprintkdbg(DBG_0, "msgin_phase0: (pid#%li) "
+ dprintkdbg(DBG_0, "msgin_phase0: (0x%p) "
"<%02i-%i> ABORT msg\n",
- srb->cmd->serial_number, dcb->target_id,
+ srb->cmd, dcb->target_id,
dcb->target_lun);
dcb->flag |= ABORT_DEV_;
enable_msgout_abort(acb, srb);
@@ -2975,7 +2974,7 @@ static void msgin_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
static void msgin_phase1(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb,
u16 *pscsi_status)
{
- dprintkdbg(DBG_0, "msgin_phase1: (pid#%li)\n", srb->cmd->serial_number);
+ dprintkdbg(DBG_0, "msgin_phase1: (0x%p)\n", srb->cmd);
clear_fifo(acb, "msgin_phase1");
DC395x_write32(acb, TRM_S1040_SCSI_COUNTER, 1);
if (!(srb->state & SRB_MSGIN)) {
@@ -3041,7 +3040,7 @@ static void disconnect(struct AdapterCtlBlk *acb)
}
srb = dcb->active_srb;
acb->active_dcb = NULL;
- dprintkdbg(DBG_0, "disconnect: (pid#%li)\n", srb->cmd->serial_number);
+ dprintkdbg(DBG_0, "disconnect: (0x%p)\n", srb->cmd);
srb->scsi_phase = PH_BUS_FREE; /* initial phase */
clear_fifo(acb, "disconnect");
@@ -3071,14 +3070,14 @@ static void disconnect(struct AdapterCtlBlk *acb)
&& srb->state != SRB_MSGOUT) {
srb->state = SRB_READY;
dprintkl(KERN_DEBUG,
- "disconnect: (pid#%li) Unexpected\n",
- srb->cmd->serial_number);
+ "disconnect: (0x%p) Unexpected\n",
+ srb->cmd);
srb->target_status = SCSI_STAT_SEL_TIMEOUT;
goto disc1;
} else {
/* Normal selection timeout */
- dprintkdbg(DBG_KG, "disconnect: (pid#%li) "
- "<%02i-%i> SelTO\n", srb->cmd->serial_number,
+ dprintkdbg(DBG_KG, "disconnect: (0x%p) "
+ "<%02i-%i> SelTO\n", srb->cmd,
dcb->target_id, dcb->target_lun);
if (srb->retry_count++ > DC395x_MAX_RETRIES
|| acb->scan_devices) {
@@ -3089,8 +3088,8 @@ static void disconnect(struct AdapterCtlBlk *acb)
free_tag(dcb, srb);
srb_going_to_waiting_move(dcb, srb);
dprintkdbg(DBG_KG,
- "disconnect: (pid#%li) Retry\n",
- srb->cmd->serial_number);
+ "disconnect: (0x%p) Retry\n",
+ srb->cmd);
waiting_set_timer(acb, HZ / 20);
}
} else if (srb->state & SRB_DISCONNECT) {
@@ -3142,9 +3141,9 @@ static void reselect(struct AdapterCtlBlk *acb)
}
/* Why the if ? */
if (!acb->scan_devices) {
- dprintkdbg(DBG_KG, "reselect: (pid#%li) <%02i-%i> "
+ dprintkdbg(DBG_KG, "reselect: (0x%p) <%02i-%i> "
"Arb lost but Resel win rsel=%i stat=0x%04x\n",
- srb->cmd->serial_number, dcb->target_id,
+ srb->cmd, dcb->target_id,
dcb->target_lun, rsel_tar_lun_id,
DC395x_read16(acb, TRM_S1040_SCSI_STATUS));
arblostflag = 1;
@@ -3318,7 +3317,7 @@ static void srb_done(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
enum dma_data_direction dir = cmd->sc_data_direction;
int ckc_only = 1;
- dprintkdbg(DBG_1, "srb_done: (pid#%li) <%02i-%i>\n", srb->cmd->serial_number,
+ dprintkdbg(DBG_1, "srb_done: (0x%p) <%02i-%i>\n", srb->cmd,
srb->cmd->device->id, srb->cmd->device->lun);
dprintkdbg(DBG_SG, "srb_done: srb=%p sg=%i(%i/%i) buf=%p\n",
srb, scsi_sg_count(cmd), srb->sg_index, srb->sg_count,
@@ -3497,9 +3496,9 @@ static void srb_done(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
cmd->SCp.buffers_residual = 0;
if (debug_enabled(DBG_KG)) {
if (srb->total_xfer_length)
- dprintkdbg(DBG_KG, "srb_done: (pid#%li) <%02i-%i> "
+ dprintkdbg(DBG_KG, "srb_done: (0x%p) <%02i-%i> "
"cmnd=0x%02x Missed %i bytes\n",
- cmd->serial_number, cmd->device->id, cmd->device->lun,
+ cmd, cmd->device->id, cmd->device->lun,
cmd->cmnd[0], srb->total_xfer_length);
}
@@ -3508,8 +3507,8 @@ static void srb_done(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
if (srb == acb->tmp_srb)
dprintkl(KERN_ERR, "srb_done: ERROR! Completed cmd with tmp_srb\n");
else {
- dprintkdbg(DBG_0, "srb_done: (pid#%li) done result=0x%08x\n",
- cmd->serial_number, cmd->result);
+ dprintkdbg(DBG_0, "srb_done: (0x%p) done result=0x%08x\n",
+ cmd, cmd->result);
srb_free_insert(acb, srb);
}
pci_unmap_srb(acb, srb);
@@ -3538,7 +3537,7 @@ static void doing_srb_done(struct AdapterCtlBlk *acb, u8 did_flag,
p = srb->cmd;
dir = p->sc_data_direction;
result = MK_RES(0, did_flag, 0, 0);
- printk("G:%li(%02i-%i) ", p->serial_number,
+ printk("G:%p(%02i-%i) ", p,
p->device->id, p->device->lun);
srb_going_remove(dcb, srb);
free_tag(dcb, srb);
@@ -3568,7 +3567,7 @@ static void doing_srb_done(struct AdapterCtlBlk *acb, u8 did_flag,
p = srb->cmd;
result = MK_RES(0, did_flag, 0, 0);
- printk("W:%li<%02i-%i>", p->serial_number, p->device->id,
+ printk("W:%p<%02i-%i>", p, p->device->id,
p->device->lun);
srb_waiting_remove(dcb, srb);
srb_free_insert(acb, srb);
@@ -3677,8 +3676,8 @@ static void request_sense(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
struct ScsiReqBlk *srb)
{
struct scsi_cmnd *cmd = srb->cmd;
- dprintkdbg(DBG_1, "request_sense: (pid#%li) <%02i-%i>\n",
- cmd->serial_number, cmd->device->id, cmd->device->lun);
+ dprintkdbg(DBG_1, "request_sense: (0x%p) <%02i-%i>\n",
+ cmd, cmd->device->id, cmd->device->lun);
srb->flag |= AUTO_REQSENSE;
srb->adapter_status = 0;
@@ -3708,8 +3707,8 @@ static void request_sense(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
if (start_scsi(acb, dcb, srb)) { /* Should only happen, if sb. else grabs the bus */
dprintkl(KERN_DEBUG,
- "request_sense: (pid#%li) failed <%02i-%i>\n",
- srb->cmd->serial_number, dcb->target_id, dcb->target_lun);
+ "request_sense: (0x%p) failed <%02i-%i>\n",
+ srb->cmd, dcb->target_id, dcb->target_lun);
srb_going_to_waiting_move(dcb, srb);
waiting_set_timer(acb, HZ / 100);
}
@@ -4717,13 +4716,13 @@ static int dc395x_proc_info(struct Scsi_Host *host, char *buffer,
dcb->target_id, dcb->target_lun,
list_size(&dcb->srb_waiting_list));
list_for_each_entry(srb, &dcb->srb_waiting_list, list)
- SPRINTF(" %li", srb->cmd->serial_number);
+ SPRINTF(" %p", srb->cmd);
if (!list_empty(&dcb->srb_going_list))
SPRINTF("\nDCB (%02i-%i): Going : %i:",
dcb->target_id, dcb->target_lun,
list_size(&dcb->srb_going_list));
list_for_each_entry(srb, &dcb->srb_going_list, list)
- SPRINTF(" %li", srb->cmd->serial_number);
+ SPRINTF(" %p", srb->cmd);
if (!list_empty(&dcb->srb_waiting_list) || !list_empty(&dcb->srb_going_list))
SPRINTF("\n");
}
diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c
index 564e6ecd17c..0119b814779 100644
--- a/drivers/scsi/device_handler/scsi_dh.c
+++ b/drivers/scsi/device_handler/scsi_dh.c
@@ -394,12 +394,14 @@ int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data)
unsigned long flags;
struct scsi_device *sdev;
struct scsi_device_handler *scsi_dh = NULL;
+ struct device *dev = NULL;
spin_lock_irqsave(q->queue_lock, flags);
sdev = q->queuedata;
if (sdev && sdev->scsi_dh_data)
scsi_dh = sdev->scsi_dh_data->scsi_dh;
- if (!scsi_dh || !get_device(&sdev->sdev_gendev) ||
+ dev = get_device(&sdev->sdev_gendev);
+ if (!scsi_dh || !dev ||
sdev->sdev_state == SDEV_CANCEL ||
sdev->sdev_state == SDEV_DEL)
err = SCSI_DH_NOSYS;
@@ -410,12 +412,13 @@ int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data)
if (err) {
if (fn)
fn(data, err);
- return err;
+ goto out;
}
if (scsi_dh->activate)
err = scsi_dh->activate(sdev, fn, data);
- put_device(&sdev->sdev_gendev);
+out:
+ put_device(dev);
return err;
}
EXPORT_SYMBOL_GPL(scsi_dh_activate);
diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c
index 42fe52902ad..6fec9fe5dc3 100644
--- a/drivers/scsi/device_handler/scsi_dh_alua.c
+++ b/drivers/scsi/device_handler/scsi_dh_alua.c
@@ -782,7 +782,7 @@ static int alua_bus_attach(struct scsi_device *sdev)
h->sdev = sdev;
err = alua_initialize(sdev, h);
- if (err != SCSI_DH_OK)
+ if ((err != SCSI_DH_OK) && (err != SCSI_DH_DEV_OFFLINED))
goto failed;
if (!try_module_get(THIS_MODULE))
diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c
index 293c183dfe6..e7fc70d6b47 100644
--- a/drivers/scsi/device_handler/scsi_dh_rdac.c
+++ b/drivers/scsi/device_handler/scsi_dh_rdac.c
@@ -182,14 +182,24 @@ struct rdac_dh_data {
struct rdac_controller *ctlr;
#define UNINITIALIZED_LUN (1 << 8)
unsigned lun;
+
+#define RDAC_MODE 0
+#define RDAC_MODE_AVT 1
+#define RDAC_MODE_IOSHIP 2
+ unsigned char mode;
+
#define RDAC_STATE_ACTIVE 0
#define RDAC_STATE_PASSIVE 1
unsigned char state;
#define RDAC_LUN_UNOWNED 0
#define RDAC_LUN_OWNED 1
-#define RDAC_LUN_AVT 2
char lun_state;
+
+#define RDAC_PREFERRED 0
+#define RDAC_NON_PREFERRED 1
+ char preferred;
+
unsigned char sense[SCSI_SENSE_BUFFERSIZE];
union {
struct c2_inquiry c2;
@@ -199,11 +209,15 @@ struct rdac_dh_data {
} inq;
};
+static const char *mode[] = {
+ "RDAC",
+ "AVT",
+ "IOSHIP",
+};
static const char *lun_state[] =
{
"unowned",
"owned",
- "owned (AVT mode)",
};
struct rdac_queue_data {
@@ -458,25 +472,33 @@ static int check_ownership(struct scsi_device *sdev, struct rdac_dh_data *h)
int err;
struct c9_inquiry *inqp;
- h->lun_state = RDAC_LUN_UNOWNED;
h->state = RDAC_STATE_ACTIVE;
err = submit_inquiry(sdev, 0xC9, sizeof(struct c9_inquiry), h);
if (err == SCSI_DH_OK) {
inqp = &h->inq.c9;
- if ((inqp->avte_cvp >> 7) == 0x1) {
- /* LUN in AVT mode */
- sdev_printk(KERN_NOTICE, sdev,
- "%s: AVT mode detected\n",
- RDAC_NAME);
- h->lun_state = RDAC_LUN_AVT;
- } else if ((inqp->avte_cvp & 0x1) != 0) {
- /* LUN was owned by the controller */
+ /* detect the operating mode */
+ if ((inqp->avte_cvp >> 5) & 0x1)
+ h->mode = RDAC_MODE_IOSHIP; /* LUN in IOSHIP mode */
+ else if (inqp->avte_cvp >> 7)
+ h->mode = RDAC_MODE_AVT; /* LUN in AVT mode */
+ else
+ h->mode = RDAC_MODE; /* LUN in RDAC mode */
+
+ /* Update ownership */
+ if (inqp->avte_cvp & 0x1)
h->lun_state = RDAC_LUN_OWNED;
+ else {
+ h->lun_state = RDAC_LUN_UNOWNED;
+ if (h->mode == RDAC_MODE)
+ h->state = RDAC_STATE_PASSIVE;
}
- }
- if (h->lun_state == RDAC_LUN_UNOWNED)
- h->state = RDAC_STATE_PASSIVE;
+ /* Update path prio*/
+ if (inqp->path_prio & 0x1)
+ h->preferred = RDAC_PREFERRED;
+ else
+ h->preferred = RDAC_NON_PREFERRED;
+ }
return err;
}
@@ -648,12 +670,27 @@ static int rdac_activate(struct scsi_device *sdev,
{
struct rdac_dh_data *h = get_rdac_data(sdev);
int err = SCSI_DH_OK;
+ int act = 0;
err = check_ownership(sdev, h);
if (err != SCSI_DH_OK)
goto done;
- if (h->lun_state == RDAC_LUN_UNOWNED) {
+ switch (h->mode) {
+ case RDAC_MODE:
+ if (h->lun_state == RDAC_LUN_UNOWNED)
+ act = 1;
+ break;
+ case RDAC_MODE_IOSHIP:
+ if ((h->lun_state == RDAC_LUN_UNOWNED) &&
+ (h->preferred == RDAC_PREFERRED))
+ act = 1;
+ break;
+ default:
+ break;
+ }
+
+ if (act) {
err = queue_mode_select(sdev, fn, data);
if (err == SCSI_DH_OK)
return 0;
@@ -836,8 +873,9 @@ static int rdac_bus_attach(struct scsi_device *sdev)
spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
sdev_printk(KERN_NOTICE, sdev,
- "%s: LUN %d (%s)\n",
- RDAC_NAME, h->lun, lun_state[(int)h->lun_state]);
+ "%s: LUN %d (%s) (%s)\n",
+ RDAC_NAME, h->lun, mode[(int)h->mode],
+ lun_state[(int)h->lun_state]);
return 0;
diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c
index cffcb108ac9..b4f6c9a84e7 100644
--- a/drivers/scsi/dpt_i2o.c
+++ b/drivers/scsi/dpt_i2o.c
@@ -780,7 +780,7 @@ static int adpt_abort(struct scsi_cmnd * cmd)
return FAILED;
}
pHba = (adpt_hba*) cmd->device->host->hostdata[0];
- printk(KERN_INFO"%s: Trying to Abort cmd=%ld\n",pHba->name, cmd->serial_number);
+ printk(KERN_INFO"%s: Trying to Abort\n",pHba->name);
if ((dptdevice = (void*) (cmd->device->hostdata)) == NULL) {
printk(KERN_ERR "%s: Unable to abort: No device in cmnd\n",pHba->name);
return FAILED;
@@ -802,10 +802,10 @@ static int adpt_abort(struct scsi_cmnd * cmd)
printk(KERN_INFO"%s: Abort cmd not supported\n",pHba->name);
return FAILED;
}
- printk(KERN_INFO"%s: Abort cmd=%ld failed.\n",pHba->name, cmd->serial_number);
+ printk(KERN_INFO"%s: Abort failed.\n",pHba->name);
return FAILED;
}
- printk(KERN_INFO"%s: Abort cmd=%ld complete.\n",pHba->name, cmd->serial_number);
+ printk(KERN_INFO"%s: Abort complete.\n",pHba->name);
return SUCCESS;
}
diff --git a/drivers/scsi/eata.c b/drivers/scsi/eata.c
index 0eb4fe6a4c8..94de88955a9 100644
--- a/drivers/scsi/eata.c
+++ b/drivers/scsi/eata.c
@@ -1766,8 +1766,8 @@ static int eata2x_queuecommand_lck(struct scsi_cmnd *SCpnt,
struct mscp *cpp;
if (SCpnt->host_scribble)
- panic("%s: qcomm, pid %ld, SCpnt %p already active.\n",
- ha->board_name, SCpnt->serial_number, SCpnt);
+ panic("%s: qcomm, SCpnt %p already active.\n",
+ ha->board_name, SCpnt);
/* i is the mailbox number, look for the first free mailbox
starting from last_cp_used */
@@ -1801,7 +1801,7 @@ static int eata2x_queuecommand_lck(struct scsi_cmnd *SCpnt,
if (do_trace)
scmd_printk(KERN_INFO, SCpnt,
- "qcomm, mbox %d, pid %ld.\n", i, SCpnt->serial_number);
+ "qcomm, mbox %d.\n", i);
cpp->reqsen = 1;
cpp->dispri = 1;
@@ -1833,8 +1833,7 @@ static int eata2x_queuecommand_lck(struct scsi_cmnd *SCpnt,
if (do_dma(shost->io_port, cpp->cp_dma_addr, SEND_CP_DMA)) {
unmap_dma(i, ha);
SCpnt->host_scribble = NULL;
- scmd_printk(KERN_INFO, SCpnt,
- "qcomm, pid %ld, adapter busy.\n", SCpnt->serial_number);
+ scmd_printk(KERN_INFO, SCpnt, "qcomm, adapter busy.\n");
return 1;
}
@@ -1851,14 +1850,12 @@ static int eata2x_eh_abort(struct scsi_cmnd *SCarg)
unsigned int i;
if (SCarg->host_scribble == NULL) {
- scmd_printk(KERN_INFO, SCarg,
- "abort, pid %ld inactive.\n", SCarg->serial_number);
+ scmd_printk(KERN_INFO, SCarg, "abort, cmd inactive.\n");
return SUCCESS;
}
i = *(unsigned int *)SCarg->host_scribble;
- scmd_printk(KERN_WARNING, SCarg,
- "abort, mbox %d, pid %ld.\n", i, SCarg->serial_number);
+ scmd_printk(KERN_WARNING, SCarg, "abort, mbox %d.\n", i);
if (i >= shost->can_queue)
panic("%s: abort, invalid SCarg->host_scribble.\n", ha->board_name);
@@ -1902,8 +1899,8 @@ static int eata2x_eh_abort(struct scsi_cmnd *SCarg)
SCarg->result = DID_ABORT << 16;
SCarg->host_scribble = NULL;
ha->cp_stat[i] = FREE;
- printk("%s, abort, mbox %d ready, DID_ABORT, pid %ld done.\n",
- ha->board_name, i, SCarg->serial_number);
+ printk("%s, abort, mbox %d ready, DID_ABORT, done.\n",
+ ha->board_name, i);
SCarg->scsi_done(SCarg);
return SUCCESS;
}
@@ -1919,13 +1916,12 @@ static int eata2x_eh_host_reset(struct scsi_cmnd *SCarg)
struct Scsi_Host *shost = SCarg->device->host;
struct hostdata *ha = (struct hostdata *)shost->hostdata;
- scmd_printk(KERN_INFO, SCarg,
- "reset, enter, pid %ld.\n", SCarg->serial_number);
+ scmd_printk(KERN_INFO, SCarg, "reset, enter.\n");
spin_lock_irq(shost->host_lock);
if (SCarg->host_scribble == NULL)
- printk("%s: reset, pid %ld inactive.\n", ha->board_name, SCarg->serial_number);
+ printk("%s: reset, inactive.\n", ha->board_name);
if (ha->in_reset) {
printk("%s: reset, exit, already in reset.\n", ha->board_name);
@@ -1964,14 +1960,14 @@ static int eata2x_eh_host_reset(struct scsi_cmnd *SCarg)
if (ha->cp_stat[i] == READY || ha->cp_stat[i] == ABORTING) {
ha->cp_stat[i] = ABORTING;
- printk("%s: reset, mbox %d aborting, pid %ld.\n",
- ha->board_name, i, SCpnt->serial_number);
+ printk("%s: reset, mbox %d aborting.\n",
+ ha->board_name, i);
}
else {
ha->cp_stat[i] = IN_RESET;
- printk("%s: reset, mbox %d in reset, pid %ld.\n",
- ha->board_name, i, SCpnt->serial_number);
+ printk("%s: reset, mbox %d in reset.\n",
+ ha->board_name, i);
}
if (SCpnt->host_scribble == NULL)
@@ -2025,8 +2021,8 @@ static int eata2x_eh_host_reset(struct scsi_cmnd *SCarg)
ha->cp_stat[i] = LOCKED;
printk
- ("%s, reset, mbox %d locked, DID_RESET, pid %ld done.\n",
- ha->board_name, i, SCpnt->serial_number);
+ ("%s, reset, mbox %d locked, DID_RESET, done.\n",
+ ha->board_name, i);
}
else if (ha->cp_stat[i] == ABORTING) {
@@ -2039,8 +2035,8 @@ static int eata2x_eh_host_reset(struct scsi_cmnd *SCarg)
ha->cp_stat[i] = FREE;
printk
- ("%s, reset, mbox %d aborting, DID_RESET, pid %ld done.\n",
- ha->board_name, i, SCpnt->serial_number);
+ ("%s, reset, mbox %d aborting, DID_RESET, done.\n",
+ ha->board_name, i);
}
else
@@ -2054,7 +2050,7 @@ static int eata2x_eh_host_reset(struct scsi_cmnd *SCarg)
do_trace = 0;
if (arg_done)
- printk("%s: reset, exit, pid %ld done.\n", ha->board_name, SCarg->serial_number);
+ printk("%s: reset, exit, done.\n", ha->board_name);
else
printk("%s: reset, exit.\n", ha->board_name);
@@ -2238,10 +2234,10 @@ static int reorder(struct hostdata *ha, unsigned long cursec,
cpp = &ha->cp[k];
SCpnt = cpp->SCpnt;
scmd_printk(KERN_INFO, SCpnt,
- "%s pid %ld mb %d fc %d nr %d sec %ld ns %u"
+ "%s mb %d fc %d nr %d sec %ld ns %u"
" cur %ld s:%c r:%c rev:%c in:%c ov:%c xd %d.\n",
(ihdlr ? "ihdlr" : "qcomm"),
- SCpnt->serial_number, k, flushcount,
+ k, flushcount,
n_ready, blk_rq_pos(SCpnt->request),
blk_rq_sectors(SCpnt->request), cursec, YESNO(s),
YESNO(r), YESNO(rev), YESNO(input_only),
@@ -2285,10 +2281,10 @@ static void flush_dev(struct scsi_device *dev, unsigned long cursec,
if (do_dma(dev->host->io_port, cpp->cp_dma_addr, SEND_CP_DMA)) {
scmd_printk(KERN_INFO, SCpnt,
- "%s, pid %ld, mbox %d, adapter"
+ "%s, mbox %d, adapter"
" busy, will abort.\n",
(ihdlr ? "ihdlr" : "qcomm"),
- SCpnt->serial_number, k);
+ k);
ha->cp_stat[k] = ABORTING;
continue;
}
@@ -2398,12 +2394,12 @@ static irqreturn_t ihdlr(struct Scsi_Host *shost)
panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", ha->board_name, i);
if (SCpnt->host_scribble == NULL)
- panic("%s: ihdlr, mbox %d, pid %ld, SCpnt %p garbled.\n", ha->board_name,
- i, SCpnt->serial_number, SCpnt);
+ panic("%s: ihdlr, mbox %d, SCpnt %p garbled.\n", ha->board_name,
+ i, SCpnt);
if (*(unsigned int *)SCpnt->host_scribble != i)
- panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d.\n",
- ha->board_name, i, SCpnt->serial_number,
+ panic("%s: ihdlr, mbox %d, index mismatch %d.\n",
+ ha->board_name, i,
*(unsigned int *)SCpnt->host_scribble);
sync_dma(i, ha);
@@ -2449,11 +2445,11 @@ static irqreturn_t ihdlr(struct Scsi_Host *shost)
if (spp->target_status && SCpnt->device->type == TYPE_DISK &&
(!(tstatus == CHECK_CONDITION && ha->iocount <= 1000 &&
(SCpnt->sense_buffer[2] & 0xf) == NOT_READY)))
- printk("%s: ihdlr, target %d.%d:%d, pid %ld, "
+ printk("%s: ihdlr, target %d.%d:%d, "
"target_status 0x%x, sense key 0x%x.\n",
ha->board_name,
SCpnt->device->channel, SCpnt->device->id,
- SCpnt->device->lun, SCpnt->serial_number,
+ SCpnt->device->lun,
spp->target_status, SCpnt->sense_buffer[2]);
ha->target_to[SCpnt->device->id][SCpnt->device->channel] = 0;
@@ -2522,9 +2518,9 @@ static irqreturn_t ihdlr(struct Scsi_Host *shost)
do_trace || msg_byte(spp->target_status))
#endif
scmd_printk(KERN_INFO, SCpnt, "ihdlr, mbox %2d, err 0x%x:%x,"
- " pid %ld, reg 0x%x, count %d.\n",
+ " reg 0x%x, count %d.\n",
i, spp->adapter_status, spp->target_status,
- SCpnt->serial_number, reg, ha->iocount);
+ reg, ha->iocount);
unmap_dma(i, ha);
diff --git a/drivers/scsi/eata_pio.c b/drivers/scsi/eata_pio.c
index 4a9641e69f5..d5f8362335d 100644
--- a/drivers/scsi/eata_pio.c
+++ b/drivers/scsi/eata_pio.c
@@ -372,8 +372,7 @@ static int eata_pio_queue_lck(struct scsi_cmnd *cmd,
cp->status = USED; /* claim free slot */
DBG(DBG_QUEUE, scmd_printk(KERN_DEBUG, cmd,
- "eata_pio_queue pid %ld, y %d\n",
- cmd->serial_number, y));
+ "eata_pio_queue 0x%p, y %d\n", cmd, y));
cmd->scsi_done = (void *) done;
@@ -417,8 +416,8 @@ static int eata_pio_queue_lck(struct scsi_cmnd *cmd,
if (eata_pio_send_command(base, EATA_CMD_PIO_SEND_CP)) {
cmd->result = DID_BUS_BUSY << 16;
scmd_printk(KERN_NOTICE, cmd,
- "eata_pio_queue pid %ld, HBA busy, "
- "returning DID_BUS_BUSY, done.\n", cmd->serial_number);
+ "eata_pio_queue pid 0x%p, HBA busy, "
+ "returning DID_BUS_BUSY, done.\n", cmd);
done(cmd);
cp->status = FREE;
return 0;
@@ -432,8 +431,8 @@ static int eata_pio_queue_lck(struct scsi_cmnd *cmd,
outw(0, base + HA_RDATA);
DBG(DBG_QUEUE, scmd_printk(KERN_DEBUG, cmd,
- "Queued base %#.4lx pid: %ld "
- "slot %d irq %d\n", sh->base, cmd->serial_number, y, sh->irq));
+ "Queued base %#.4lx cmd: 0x%p "
+ "slot %d irq %d\n", sh->base, cmd, y, sh->irq));
return 0;
}
@@ -445,8 +444,7 @@ static int eata_pio_abort(struct scsi_cmnd *cmd)
unsigned int loop = 100;
DBG(DBG_ABNORM, scmd_printk(KERN_WARNING, cmd,
- "eata_pio_abort called pid: %ld\n",
- cmd->serial_number));
+ "eata_pio_abort called pid: 0x%p\n", cmd));
while (inb(cmd->device->host->base + HA_RAUXSTAT) & HA_ABUSY)
if (--loop == 0) {
@@ -481,8 +479,7 @@ static int eata_pio_host_reset(struct scsi_cmnd *cmd)
struct Scsi_Host *host = cmd->device->host;
DBG(DBG_ABNORM, scmd_printk(KERN_WARNING, cmd,
- "eata_pio_reset called pid:%ld\n",
- cmd->serial_number));
+ "eata_pio_reset called\n"));
spin_lock_irq(host->host_lock);
@@ -501,7 +498,7 @@ static int eata_pio_host_reset(struct scsi_cmnd *cmd)
sp = HD(cmd)->ccb[x].cmd;
HD(cmd)->ccb[x].status = RESET;
- printk(KERN_WARNING "eata_pio_reset: slot %d in reset, pid %ld.\n", x, sp->serial_number);
+ printk(KERN_WARNING "eata_pio_reset: slot %d in reset.\n", x);
if (sp == NULL)
panic("eata_pio_reset: slot %d, sp==NULL.\n", x);
diff --git a/drivers/scsi/esp_scsi.c b/drivers/scsi/esp_scsi.c
index 57558523c1b..9a1af1d6071 100644
--- a/drivers/scsi/esp_scsi.c
+++ b/drivers/scsi/esp_scsi.c
@@ -708,8 +708,7 @@ static void esp_maybe_execute_command(struct esp *esp)
tp = &esp->target[tgt];
lp = dev->hostdata;
- list_del(&ent->list);
- list_add(&ent->list, &esp->active_cmds);
+ list_move(&ent->list, &esp->active_cmds);
esp->active_cmd = ent;
@@ -1244,8 +1243,7 @@ static int esp_finish_select(struct esp *esp)
/* Now that the state is unwound properly, put back onto
* the issue queue. This command is no longer active.
*/
- list_del(&ent->list);
- list_add(&ent->list, &esp->queued_cmds);
+ list_move(&ent->list, &esp->queued_cmds);
esp->active_cmd = NULL;
/* Return value ignored by caller, it directly invokes
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 04f346b562d..cc23bd9480b 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -381,6 +381,42 @@ out:
}
/**
+ * fcoe_interface_release() - fcoe_port kref release function
+ * @kref: Embedded reference count in an fcoe_interface struct
+ */
+static void fcoe_interface_release(struct kref *kref)
+{
+ struct fcoe_interface *fcoe;
+ struct net_device *netdev;
+
+ fcoe = container_of(kref, struct fcoe_interface, kref);
+ netdev = fcoe->netdev;
+ /* tear-down the FCoE controller */
+ fcoe_ctlr_destroy(&fcoe->ctlr);
+ kfree(fcoe);
+ dev_put(netdev);
+ module_put(THIS_MODULE);
+}
+
+/**
+ * fcoe_interface_get() - Get a reference to a FCoE interface
+ * @fcoe: The FCoE interface to be held
+ */
+static inline void fcoe_interface_get(struct fcoe_interface *fcoe)
+{
+ kref_get(&fcoe->kref);
+}
+
+/**
+ * fcoe_interface_put() - Put a reference to a FCoE interface
+ * @fcoe: The FCoE interface to be released
+ */
+static inline void fcoe_interface_put(struct fcoe_interface *fcoe)
+{
+ kref_put(&fcoe->kref, fcoe_interface_release);
+}
+
+/**
* fcoe_interface_cleanup() - Clean up a FCoE interface
* @fcoe: The FCoE interface to be cleaned up
*
@@ -392,6 +428,21 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
struct fcoe_ctlr *fip = &fcoe->ctlr;
u8 flogi_maddr[ETH_ALEN];
const struct net_device_ops *ops;
+ struct fcoe_port *port = lport_priv(fcoe->ctlr.lp);
+
+ FCOE_NETDEV_DBG(netdev, "Destroying interface\n");
+
+ /* Logout of the fabric */
+ fc_fabric_logoff(fcoe->ctlr.lp);
+
+ /* Cleanup the fc_lport */
+ fc_lport_destroy(fcoe->ctlr.lp);
+
+ /* Stop the transmit retry timer */
+ del_timer_sync(&port->timer);
+
+ /* Free existing transmit skbs */
+ fcoe_clean_pending_queue(fcoe->ctlr.lp);
/*
* Don't listen for Ethernet packets anymore.
@@ -414,6 +465,9 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
} else
dev_mc_del(netdev, FIP_ALL_ENODE_MACS);
+ if (!is_zero_ether_addr(port->data_src_addr))
+ dev_uc_del(netdev, port->data_src_addr);
+
/* Tell the LLD we are done w/ FCoE */
ops = netdev->netdev_ops;
if (ops->ndo_fcoe_disable) {
@@ -421,42 +475,7 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
FCOE_NETDEV_DBG(netdev, "Failed to disable FCoE"
" specific feature for LLD.\n");
}
-}
-
-/**
- * fcoe_interface_release() - fcoe_port kref release function
- * @kref: Embedded reference count in an fcoe_interface struct
- */
-static void fcoe_interface_release(struct kref *kref)
-{
- struct fcoe_interface *fcoe;
- struct net_device *netdev;
-
- fcoe = container_of(kref, struct fcoe_interface, kref);
- netdev = fcoe->netdev;
- /* tear-down the FCoE controller */
- fcoe_ctlr_destroy(&fcoe->ctlr);
- kfree(fcoe);
- dev_put(netdev);
- module_put(THIS_MODULE);
-}
-
-/**
- * fcoe_interface_get() - Get a reference to a FCoE interface
- * @fcoe: The FCoE interface to be held
- */
-static inline void fcoe_interface_get(struct fcoe_interface *fcoe)
-{
- kref_get(&fcoe->kref);
-}
-
-/**
- * fcoe_interface_put() - Put a reference to a FCoE interface
- * @fcoe: The FCoE interface to be released
- */
-static inline void fcoe_interface_put(struct fcoe_interface *fcoe)
-{
- kref_put(&fcoe->kref, fcoe_interface_release);
+ fcoe_interface_put(fcoe);
}
/**
@@ -821,39 +840,9 @@ skip_oem:
* fcoe_if_destroy() - Tear down a SW FCoE instance
* @lport: The local port to be destroyed
*
- * Locking: must be called with the RTNL mutex held and RTNL mutex
- * needed to be dropped by this function since not dropping RTNL
- * would cause circular locking warning on synchronous fip worker
- * cancelling thru fcoe_interface_put invoked by this function.
- *
*/
static void fcoe_if_destroy(struct fc_lport *lport)
{
- struct fcoe_port *port = lport_priv(lport);
- struct fcoe_interface *fcoe = port->priv;
- struct net_device *netdev = fcoe->netdev;
-
- FCOE_NETDEV_DBG(netdev, "Destroying interface\n");
-
- /* Logout of the fabric */
- fc_fabric_logoff(lport);
-
- /* Cleanup the fc_lport */
- fc_lport_destroy(lport);
-
- /* Stop the transmit retry timer */
- del_timer_sync(&port->timer);
-
- /* Free existing transmit skbs */
- fcoe_clean_pending_queue(lport);
-
- if (!is_zero_ether_addr(port->data_src_addr))
- dev_uc_del(netdev, port->data_src_addr);
- rtnl_unlock();
-
- /* receives may not be stopped until after this */
- fcoe_interface_put(fcoe);
-
/* Free queued packets for the per-CPU receive threads */
fcoe_percpu_clean(lport);
@@ -1783,23 +1772,8 @@ static int fcoe_disable(struct net_device *netdev)
int rc = 0;
mutex_lock(&fcoe_config_mutex);
-#ifdef CONFIG_FCOE_MODULE
- /*
- * Make sure the module has been initialized, and is not about to be
- * removed. Module paramter sysfs files are writable before the
- * module_init function is called and after module_exit.
- */
- if (THIS_MODULE->state != MODULE_STATE_LIVE) {
- rc = -ENODEV;
- goto out_nodev;
- }
-#endif
-
- if (!rtnl_trylock()) {
- mutex_unlock(&fcoe_config_mutex);
- return -ERESTARTSYS;
- }
+ rtnl_lock();
fcoe = fcoe_hostlist_lookup_port(netdev);
rtnl_unlock();
@@ -1809,7 +1783,6 @@ static int fcoe_disable(struct net_device *netdev)
} else
rc = -ENODEV;
-out_nodev:
mutex_unlock(&fcoe_config_mutex);
return rc;
}
@@ -1828,22 +1801,7 @@ static int fcoe_enable(struct net_device *netdev)
int rc = 0;
mutex_lock(&fcoe_config_mutex);
-#ifdef CONFIG_FCOE_MODULE
- /*
- * Make sure the module has been initialized, and is not about to be
- * removed. Module paramter sysfs files are writable before the
- * module_init function is called and after module_exit.
- */
- if (THIS_MODULE->state != MODULE_STATE_LIVE) {
- rc = -ENODEV;
- goto out_nodev;
- }
-#endif
- if (!rtnl_trylock()) {
- mutex_unlock(&fcoe_config_mutex);
- return -ERESTARTSYS;
- }
-
+ rtnl_lock();
fcoe = fcoe_hostlist_lookup_port(netdev);
rtnl_unlock();
@@ -1852,7 +1810,6 @@ static int fcoe_enable(struct net_device *netdev)
else if (!fcoe_link_ok(fcoe->ctlr.lp))
fcoe_ctlr_link_up(&fcoe->ctlr);
-out_nodev:
mutex_unlock(&fcoe_config_mutex);
return rc;
}
@@ -1868,35 +1825,22 @@ out_nodev:
static int fcoe_destroy(struct net_device *netdev)
{
struct fcoe_interface *fcoe;
+ struct fc_lport *lport;
int rc = 0;
mutex_lock(&fcoe_config_mutex);
-#ifdef CONFIG_FCOE_MODULE
- /*
- * Make sure the module has been initialized, and is not about to be
- * removed. Module paramter sysfs files are writable before the
- * module_init function is called and after module_exit.
- */
- if (THIS_MODULE->state != MODULE_STATE_LIVE) {
- rc = -ENODEV;
- goto out_nodev;
- }
-#endif
- if (!rtnl_trylock()) {
- mutex_unlock(&fcoe_config_mutex);
- return -ERESTARTSYS;
- }
-
+ rtnl_lock();
fcoe = fcoe_hostlist_lookup_port(netdev);
if (!fcoe) {
rtnl_unlock();
rc = -ENODEV;
goto out_nodev;
}
- fcoe_interface_cleanup(fcoe);
+ lport = fcoe->ctlr.lp;
list_del(&fcoe->list);
- /* RTNL mutex is dropped by fcoe_if_destroy */
- fcoe_if_destroy(fcoe->ctlr.lp);
+ fcoe_interface_cleanup(fcoe);
+ rtnl_unlock();
+ fcoe_if_destroy(lport);
out_nodev:
mutex_unlock(&fcoe_config_mutex);
return rc;
@@ -1912,8 +1856,6 @@ static void fcoe_destroy_work(struct work_struct *work)
port = container_of(work, struct fcoe_port, destroy_work);
mutex_lock(&fcoe_config_mutex);
- rtnl_lock();
- /* RTNL mutex is dropped by fcoe_if_destroy */
fcoe_if_destroy(port->lport);
mutex_unlock(&fcoe_config_mutex);
}
@@ -1948,23 +1890,7 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
struct fc_lport *lport;
mutex_lock(&fcoe_config_mutex);
-
- if (!rtnl_trylock()) {
- mutex_unlock(&fcoe_config_mutex);
- return -ERESTARTSYS;
- }
-
-#ifdef CONFIG_FCOE_MODULE
- /*
- * Make sure the module has been initialized, and is not about to be
- * removed. Module paramter sysfs files are writable before the
- * module_init function is called and after module_exit.
- */
- if (THIS_MODULE->state != MODULE_STATE_LIVE) {
- rc = -ENODEV;
- goto out_nodev;
- }
-#endif
+ rtnl_lock();
/* look for existing lport */
if (fcoe_hostlist_lookup(netdev)) {
diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c
index 9d38be2a41f..229e4af5508 100644
--- a/drivers/scsi/fcoe/fcoe_ctlr.c
+++ b/drivers/scsi/fcoe/fcoe_ctlr.c
@@ -978,10 +978,8 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
* the FCF that answers multicast solicitations, not the others that
* are sending periodic multicast advertisements.
*/
- if (mtu_valid) {
- list_del(&fcf->list);
- list_add(&fcf->list, &fip->fcfs);
- }
+ if (mtu_valid)
+ list_move(&fcf->list, &fip->fcfs);
/*
* If this is the first validated FCF, note the time and
diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c
index 258684101bf..f81f77c8569 100644
--- a/drivers/scsi/fcoe/fcoe_transport.c
+++ b/drivers/scsi/fcoe/fcoe_transport.c
@@ -335,7 +335,7 @@ out_attach:
EXPORT_SYMBOL(fcoe_transport_attach);
/**
- * fcoe_transport_attach - Detaches an FCoE transport
+ * fcoe_transport_detach - Detaches an FCoE transport
* @ft: The fcoe transport to be attached
*
* Returns : 0 for success
@@ -343,6 +343,7 @@ EXPORT_SYMBOL(fcoe_transport_attach);
int fcoe_transport_detach(struct fcoe_transport *ft)
{
int rc = 0;
+ struct fcoe_netdev_mapping *nm = NULL, *tmp;
mutex_lock(&ft_mutex);
if (!ft->attached) {
@@ -352,6 +353,19 @@ int fcoe_transport_detach(struct fcoe_transport *ft)
goto out_attach;
}
+ /* remove netdev mapping for this transport as it is going away */
+ mutex_lock(&fn_mutex);
+ list_for_each_entry_safe(nm, tmp, &fcoe_netdevs, list) {
+ if (nm->ft == ft) {
+ LIBFCOE_TRANSPORT_DBG("transport %s going away, "
+ "remove its netdev mapping for %s\n",
+ ft->name, nm->netdev->name);
+ list_del(&nm->list);
+ kfree(nm);
+ }
+ }
+ mutex_unlock(&fn_mutex);
+
list_del(&ft->list);
ft->attached = false;
LIBFCOE_TRANSPORT_DBG("detaching transport %s\n", ft->name);
@@ -371,9 +385,9 @@ static int fcoe_transport_show(char *buffer, const struct kernel_param *kp)
i = j = sprintf(buffer, "Attached FCoE transports:");
mutex_lock(&ft_mutex);
list_for_each_entry(ft, &fcoe_transports, list) {
- i += snprintf(&buffer[i], IFNAMSIZ, "%s ", ft->name);
- if (i >= PAGE_SIZE)
+ if (i >= PAGE_SIZE - IFNAMSIZ)
break;
+ i += snprintf(&buffer[i], IFNAMSIZ, "%s ", ft->name);
}
mutex_unlock(&ft_mutex);
if (i == j)
@@ -530,9 +544,6 @@ static int fcoe_transport_create(const char *buffer, struct kernel_param *kp)
struct fcoe_transport *ft = NULL;
enum fip_state fip_mode = (enum fip_state)(long)kp->arg;
- if (!mutex_trylock(&ft_mutex))
- return restart_syscall();
-
#ifdef CONFIG_LIBFCOE_MODULE
/*
* Make sure the module has been initialized, and is not about to be
@@ -543,6 +554,8 @@ static int fcoe_transport_create(const char *buffer, struct kernel_param *kp)
goto out_nodev;
#endif
+ mutex_lock(&ft_mutex);
+
netdev = fcoe_if_to_netdev(buffer);
if (!netdev) {
LIBFCOE_TRANSPORT_DBG("Invalid device %s.\n", buffer);
@@ -586,10 +599,7 @@ out_putdev:
dev_put(netdev);
out_nodev:
mutex_unlock(&ft_mutex);
- if (rc == -ERESTARTSYS)
- return restart_syscall();
- else
- return rc;
+ return rc;
}
/**
@@ -608,9 +618,6 @@ static int fcoe_transport_destroy(const char *buffer, struct kernel_param *kp)
struct net_device *netdev = NULL;
struct fcoe_transport *ft = NULL;
- if (!mutex_trylock(&ft_mutex))
- return restart_syscall();
-
#ifdef CONFIG_LIBFCOE_MODULE
/*
* Make sure the module has been initialized, and is not about to be
@@ -621,6 +628,8 @@ static int fcoe_transport_destroy(const char *buffer, struct kernel_param *kp)
goto out_nodev;
#endif
+ mutex_lock(&ft_mutex);
+
netdev = fcoe_if_to_netdev(buffer);
if (!netdev) {
LIBFCOE_TRANSPORT_DBG("invalid device %s.\n", buffer);
@@ -645,11 +654,7 @@ out_putdev:
dev_put(netdev);
out_nodev:
mutex_unlock(&ft_mutex);
-
- if (rc == -ERESTARTSYS)
- return restart_syscall();
- else
- return rc;
+ return rc;
}
/**
@@ -667,9 +672,6 @@ static int fcoe_transport_disable(const char *buffer, struct kernel_param *kp)
struct net_device *netdev = NULL;
struct fcoe_transport *ft = NULL;
- if (!mutex_trylock(&ft_mutex))
- return restart_syscall();
-
#ifdef CONFIG_LIBFCOE_MODULE
/*
* Make sure the module has been initialized, and is not about to be
@@ -680,6 +682,8 @@ static int fcoe_transport_disable(const char *buffer, struct kernel_param *kp)
goto out_nodev;
#endif
+ mutex_lock(&ft_mutex);
+
netdev = fcoe_if_to_netdev(buffer);
if (!netdev)
goto out_nodev;
@@ -716,9 +720,6 @@ static int fcoe_transport_enable(const char *buffer, struct kernel_param *kp)
struct net_device *netdev = NULL;
struct fcoe_transport *ft = NULL;
- if (!mutex_trylock(&ft_mutex))
- return restart_syscall();
-
#ifdef CONFIG_LIBFCOE_MODULE
/*
* Make sure the module has been initialized, and is not about to be
@@ -729,6 +730,8 @@ static int fcoe_transport_enable(const char *buffer, struct kernel_param *kp)
goto out_nodev;
#endif
+ mutex_lock(&ft_mutex);
+
netdev = fcoe_if_to_netdev(buffer);
if (!netdev)
goto out_nodev;
@@ -743,10 +746,7 @@ out_putdev:
dev_put(netdev);
out_nodev:
mutex_unlock(&ft_mutex);
- if (rc == -ERESTARTSYS)
- return restart_syscall();
- else
- return rc;
+ return rc;
}
/**
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index 415ad4fb50d..c6c0434d803 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -273,7 +273,7 @@ static ssize_t host_show_transport_mode(struct device *dev,
"performant" : "simple");
}
-/* List of controllers which cannot be reset on kexec with reset_devices */
+/* List of controllers which cannot be hard reset on kexec with reset_devices */
static u32 unresettable_controller[] = {
0x324a103C, /* Smart Array P712m */
0x324b103C, /* SmartArray P711m */
@@ -291,16 +291,45 @@ static u32 unresettable_controller[] = {
0x409D0E11, /* Smart Array 6400 EM */
};
-static int ctlr_is_resettable(struct ctlr_info *h)
+/* List of controllers which cannot even be soft reset */
+static u32 soft_unresettable_controller[] = {
+ /* Exclude 640x boards. These are two pci devices in one slot
+ * which share a battery backed cache module. One controls the
+ * cache, the other accesses the cache through the one that controls
+ * it. If we reset the one controlling the cache, the other will
+ * likely not be happy. Just forbid resetting this conjoined mess.
+ * The 640x isn't really supported by hpsa anyway.
+ */
+ 0x409C0E11, /* Smart Array 6400 */
+ 0x409D0E11, /* Smart Array 6400 EM */
+};
+
+static int ctlr_is_hard_resettable(u32 board_id)
{
int i;
for (i = 0; i < ARRAY_SIZE(unresettable_controller); i++)
- if (unresettable_controller[i] == h->board_id)
+ if (unresettable_controller[i] == board_id)
+ return 0;
+ return 1;
+}
+
+static int ctlr_is_soft_resettable(u32 board_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(soft_unresettable_controller); i++)
+ if (soft_unresettable_controller[i] == board_id)
return 0;
return 1;
}
+static int ctlr_is_resettable(u32 board_id)
+{
+ return ctlr_is_hard_resettable(board_id) ||
+ ctlr_is_soft_resettable(board_id);
+}
+
static ssize_t host_show_resettable(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -308,7 +337,7 @@ static ssize_t host_show_resettable(struct device *dev,
struct Scsi_Host *shost = class_to_shost(dev);
h = shost_to_hba(shost);
- return snprintf(buf, 20, "%d\n", ctlr_is_resettable(h));
+ return snprintf(buf, 20, "%d\n", ctlr_is_resettable(h->board_id));
}
static inline int is_logical_dev_addr_mode(unsigned char scsi3addr[])
@@ -929,13 +958,6 @@ static void hpsa_slave_destroy(struct scsi_device *sdev)
/* nothing to do. */
}
-static void hpsa_scsi_setup(struct ctlr_info *h)
-{
- h->ndevices = 0;
- h->scsi_host = NULL;
- spin_lock_init(&h->devlock);
-}
-
static void hpsa_free_sg_chain_blocks(struct ctlr_info *h)
{
int i;
@@ -1006,8 +1028,7 @@ static void hpsa_unmap_sg_chain_block(struct ctlr_info *h,
pci_unmap_single(h->pdev, temp64.val, chain_sg->Len, PCI_DMA_TODEVICE);
}
-static void complete_scsi_command(struct CommandList *cp,
- int timeout, u32 tag)
+static void complete_scsi_command(struct CommandList *cp)
{
struct scsi_cmnd *cmd;
struct ctlr_info *h;
@@ -1308,7 +1329,7 @@ static void hpsa_scsi_do_simple_cmd_with_retry(struct ctlr_info *h,
int retry_count = 0;
do {
- memset(c->err_info, 0, sizeof(c->err_info));
+ memset(c->err_info, 0, sizeof(*c->err_info));
hpsa_scsi_do_simple_cmd_core(h, c);
retry_count++;
} while (check_for_unit_attention(h, c) && retry_count <= 3);
@@ -1570,6 +1591,7 @@ static unsigned char *msa2xxx_model[] = {
"MSA2024",
"MSA2312",
"MSA2324",
+ "P2000 G3 SAS",
NULL,
};
@@ -2751,6 +2773,26 @@ static int hpsa_ioctl(struct scsi_device *dev, int cmd, void *arg)
}
}
+static int __devinit hpsa_send_host_reset(struct ctlr_info *h,
+ unsigned char *scsi3addr, u8 reset_type)
+{
+ struct CommandList *c;
+
+ c = cmd_alloc(h);
+ if (!c)
+ return -ENOMEM;
+ fill_cmd(c, HPSA_DEVICE_RESET_MSG, h, NULL, 0, 0,
+ RAID_CTLR_LUNID, TYPE_MSG);
+ c->Request.CDB[1] = reset_type; /* fill_cmd defaults to target reset */
+ c->waiting = NULL;
+ enqueue_cmd_and_start_io(h, c);
+ /* Don't wait for completion, the reset won't complete. Don't free
+ * the command either. This is the last command we will send before
+ * re-initializing everything, so it doesn't matter and won't leak.
+ */
+ return 0;
+}
+
static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
void *buff, size_t size, u8 page_code, unsigned char *scsi3addr,
int cmd_type)
@@ -2828,7 +2870,8 @@ static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
c->Request.Type.Attribute = ATTR_SIMPLE;
c->Request.Type.Direction = XFER_NONE;
c->Request.Timeout = 0; /* Don't time out */
- c->Request.CDB[0] = 0x01; /* RESET_MSG is 0x01 */
+ memset(&c->Request.CDB[0], 0, sizeof(c->Request.CDB));
+ c->Request.CDB[0] = cmd;
c->Request.CDB[1] = 0x03; /* Reset target above */
/* If bytes 4-7 are zero, it means reset the */
/* LunID device */
@@ -2936,7 +2979,7 @@ static inline void finish_cmd(struct CommandList *c, u32 raw_tag)
{
removeQ(c);
if (likely(c->cmd_type == CMD_SCSI))
- complete_scsi_command(c, 0, raw_tag);
+ complete_scsi_command(c);
else if (c->cmd_type == CMD_IOCTL_PEND)
complete(c->waiting);
}
@@ -2994,6 +3037,63 @@ static inline u32 process_nonindexed_cmd(struct ctlr_info *h,
return next_command(h);
}
+/* Some controllers, like p400, will give us one interrupt
+ * after a soft reset, even if we turned interrupts off.
+ * Only need to check for this in the hpsa_xxx_discard_completions
+ * functions.
+ */
+static int ignore_bogus_interrupt(struct ctlr_info *h)
+{
+ if (likely(!reset_devices))
+ return 0;
+
+ if (likely(h->interrupts_enabled))
+ return 0;
+
+ dev_info(&h->pdev->dev, "Received interrupt while interrupts disabled "
+ "(known firmware bug.) Ignoring.\n");
+
+ return 1;
+}
+
+static irqreturn_t hpsa_intx_discard_completions(int irq, void *dev_id)
+{
+ struct ctlr_info *h = dev_id;
+ unsigned long flags;
+ u32 raw_tag;
+
+ if (ignore_bogus_interrupt(h))
+ return IRQ_NONE;
+
+ if (interrupt_not_for_us(h))
+ return IRQ_NONE;
+ spin_lock_irqsave(&h->lock, flags);
+ while (interrupt_pending(h)) {
+ raw_tag = get_next_completion(h);
+ while (raw_tag != FIFO_EMPTY)
+ raw_tag = next_command(h);
+ }
+ spin_unlock_irqrestore(&h->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t hpsa_msix_discard_completions(int irq, void *dev_id)
+{
+ struct ctlr_info *h = dev_id;
+ unsigned long flags;
+ u32 raw_tag;
+
+ if (ignore_bogus_interrupt(h))
+ return IRQ_NONE;
+
+ spin_lock_irqsave(&h->lock, flags);
+ raw_tag = get_next_completion(h);
+ while (raw_tag != FIFO_EMPTY)
+ raw_tag = next_command(h);
+ spin_unlock_irqrestore(&h->lock, flags);
+ return IRQ_HANDLED;
+}
+
static irqreturn_t do_hpsa_intr_intx(int irq, void *dev_id)
{
struct ctlr_info *h = dev_id;
@@ -3132,11 +3232,10 @@ static __devinit int hpsa_message(struct pci_dev *pdev, unsigned char opcode,
return 0;
}
-#define hpsa_soft_reset_controller(p) hpsa_message(p, 1, 0)
#define hpsa_noop(p) hpsa_message(p, 3, 0)
static int hpsa_controller_hard_reset(struct pci_dev *pdev,
- void * __iomem vaddr, bool use_doorbell)
+ void * __iomem vaddr, u32 use_doorbell)
{
u16 pmcsr;
int pos;
@@ -3147,8 +3246,7 @@ static int hpsa_controller_hard_reset(struct pci_dev *pdev,
* other way using the doorbell register.
*/
dev_info(&pdev->dev, "using doorbell to reset controller\n");
- writel(DOORBELL_CTLR_RESET, vaddr + SA5_DOORBELL);
- msleep(1000);
+ writel(use_doorbell, vaddr + SA5_DOORBELL);
} else { /* Try to do it the PCI power state way */
/* Quoting from the Open CISS Specification: "The Power
@@ -3179,12 +3277,63 @@ static int hpsa_controller_hard_reset(struct pci_dev *pdev,
pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
pmcsr |= PCI_D0;
pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
-
- msleep(500);
}
return 0;
}
+static __devinit void init_driver_version(char *driver_version, int len)
+{
+ memset(driver_version, 0, len);
+ strncpy(driver_version, "hpsa " HPSA_DRIVER_VERSION, len - 1);
+}
+
+static __devinit int write_driver_ver_to_cfgtable(
+ struct CfgTable __iomem *cfgtable)
+{
+ char *driver_version;
+ int i, size = sizeof(cfgtable->driver_version);
+
+ driver_version = kmalloc(size, GFP_KERNEL);
+ if (!driver_version)
+ return -ENOMEM;
+
+ init_driver_version(driver_version, size);
+ for (i = 0; i < size; i++)
+ writeb(driver_version[i], &cfgtable->driver_version[i]);
+ kfree(driver_version);
+ return 0;
+}
+
+static __devinit void read_driver_ver_from_cfgtable(
+ struct CfgTable __iomem *cfgtable, unsigned char *driver_ver)
+{
+ int i;
+
+ for (i = 0; i < sizeof(cfgtable->driver_version); i++)
+ driver_ver[i] = readb(&cfgtable->driver_version[i]);
+}
+
+static __devinit int controller_reset_failed(
+ struct CfgTable __iomem *cfgtable)
+{
+
+ char *driver_ver, *old_driver_ver;
+ int rc, size = sizeof(cfgtable->driver_version);
+
+ old_driver_ver = kmalloc(2 * size, GFP_KERNEL);
+ if (!old_driver_ver)
+ return -ENOMEM;
+ driver_ver = old_driver_ver + size;
+
+ /* After a reset, the 32 bytes of "driver version" in the cfgtable
+ * should have been changed, otherwise we know the reset failed.
+ */
+ init_driver_version(old_driver_ver, size);
+ read_driver_ver_from_cfgtable(cfgtable, driver_ver);
+ rc = !memcmp(driver_ver, old_driver_ver, size);
+ kfree(old_driver_ver);
+ return rc;
+}
/* This does a hard reset of the controller using PCI power management
* states or the using the doorbell register.
*/
@@ -3195,10 +3344,10 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
u64 cfg_base_addr_index;
void __iomem *vaddr;
unsigned long paddr;
- u32 misc_fw_support, active_transport;
+ u32 misc_fw_support;
int rc;
struct CfgTable __iomem *cfgtable;
- bool use_doorbell;
+ u32 use_doorbell;
u32 board_id;
u16 command_register;
@@ -3215,20 +3364,15 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
* using the doorbell register.
*/
- /* Exclude 640x boards. These are two pci devices in one slot
- * which share a battery backed cache module. One controls the
- * cache, the other accesses the cache through the one that controls
- * it. If we reset the one controlling the cache, the other will
- * likely not be happy. Just forbid resetting this conjoined mess.
- * The 640x isn't really supported by hpsa anyway.
- */
rc = hpsa_lookup_board_id(pdev, &board_id);
- if (rc < 0) {
+ if (rc < 0 || !ctlr_is_resettable(board_id)) {
dev_warn(&pdev->dev, "Not resetting device.\n");
return -ENODEV;
}
- if (board_id == 0x409C0E11 || board_id == 0x409D0E11)
- return -ENOTSUPP;
+
+ /* if controller is soft- but not hard resettable... */
+ if (!ctlr_is_hard_resettable(board_id))
+ return -ENOTSUPP; /* try soft reset later. */
/* Save the PCI command register */
pci_read_config_word(pdev, 4, &command_register);
@@ -3257,10 +3401,28 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
rc = -ENOMEM;
goto unmap_vaddr;
}
+ rc = write_driver_ver_to_cfgtable(cfgtable);
+ if (rc)
+ goto unmap_vaddr;
- /* If reset via doorbell register is supported, use that. */
+ /* If reset via doorbell register is supported, use that.
+ * There are two such methods. Favor the newest method.
+ */
misc_fw_support = readl(&cfgtable->misc_fw_support);
- use_doorbell = misc_fw_support & MISC_FW_DOORBELL_RESET;
+ use_doorbell = misc_fw_support & MISC_FW_DOORBELL_RESET2;
+ if (use_doorbell) {
+ use_doorbell = DOORBELL_CTLR_RESET2;
+ } else {
+ use_doorbell = misc_fw_support & MISC_FW_DOORBELL_RESET;
+ if (use_doorbell) {
+ dev_warn(&pdev->dev, "Controller claims that "
+ "'Bit 2 doorbell reset' is "
+ "supported, but not 'bit 5 doorbell reset'. "
+ "Firmware update is recommended.\n");
+ rc = -ENOTSUPP; /* try soft reset */
+ goto unmap_cfgtable;
+ }
+ }
rc = hpsa_controller_hard_reset(pdev, vaddr, use_doorbell);
if (rc)
@@ -3279,30 +3441,32 @@ static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
msleep(HPSA_POST_RESET_PAUSE_MSECS);
/* Wait for board to become not ready, then ready. */
- dev_info(&pdev->dev, "Waiting for board to become ready.\n");
+ dev_info(&pdev->dev, "Waiting for board to reset.\n");
rc = hpsa_wait_for_board_state(pdev, vaddr, BOARD_NOT_READY);
- if (rc)
+ if (rc) {
dev_warn(&pdev->dev,
- "failed waiting for board to become not ready\n");
+ "failed waiting for board to reset."
+ " Will try soft reset.\n");
+ rc = -ENOTSUPP; /* Not expected, but try soft reset later */
+ goto unmap_cfgtable;
+ }
rc = hpsa_wait_for_board_state(pdev, vaddr, BOARD_READY);
if (rc) {
dev_warn(&pdev->dev,
- "failed waiting for board to become ready\n");
+ "failed waiting for board to become ready "
+ "after hard reset\n");
goto unmap_cfgtable;
}
- dev_info(&pdev->dev, "board ready.\n");
- /* Controller should be in simple mode at this point. If it's not,
- * It means we're on one of those controllers which doesn't support
- * the doorbell reset method and on which the PCI power management reset
- * method doesn't work (P800, for example.)
- * In those cases, don't try to proceed, as it generally doesn't work.
- */
- active_transport = readl(&cfgtable->TransportActive);
- if (active_transport & PERFORMANT_MODE) {
- dev_warn(&pdev->dev, "Unable to successfully reset controller,"
- " Ignoring controller.\n");
- rc = -ENODEV;
+ rc = controller_reset_failed(vaddr);
+ if (rc < 0)
+ goto unmap_cfgtable;
+ if (rc) {
+ dev_warn(&pdev->dev, "Unable to successfully reset "
+ "controller. Will try soft reset.\n");
+ rc = -ENOTSUPP;
+ } else {
+ dev_info(&pdev->dev, "board ready after hard reset.\n");
}
unmap_cfgtable:
@@ -3543,6 +3707,9 @@ static int __devinit hpsa_find_cfgtables(struct ctlr_info *h)
cfg_base_addr_index) + cfg_offset, sizeof(*h->cfgtable));
if (!h->cfgtable)
return -ENOMEM;
+ rc = write_driver_ver_to_cfgtable(h->cfgtable);
+ if (rc)
+ return rc;
/* Find performant mode table. */
trans_offset = readl(&h->cfgtable->TransMethodOffset);
h->transtable = remap_pci_mem(pci_resource_start(h->pdev,
@@ -3777,11 +3944,12 @@ static __devinit int hpsa_init_reset_devices(struct pci_dev *pdev)
* due to concerns about shared bbwc between 6402/6404 pair.
*/
if (rc == -ENOTSUPP)
- return 0; /* just try to do the kdump anyhow. */
+ return rc; /* just try to do the kdump anyhow. */
if (rc)
return -ENODEV;
/* Now try to get the controller to respond to a no-op */
+ dev_warn(&pdev->dev, "Waiting for controller to respond to no-op\n");
for (i = 0; i < HPSA_POST_RESET_NOOP_RETRIES; i++) {
if (hpsa_noop(pdev) == 0)
break;
@@ -3792,18 +3960,133 @@ static __devinit int hpsa_init_reset_devices(struct pci_dev *pdev)
return 0;
}
+static __devinit int hpsa_allocate_cmd_pool(struct ctlr_info *h)
+{
+ h->cmd_pool_bits = kzalloc(
+ DIV_ROUND_UP(h->nr_cmds, BITS_PER_LONG) *
+ sizeof(unsigned long), GFP_KERNEL);
+ h->cmd_pool = pci_alloc_consistent(h->pdev,
+ h->nr_cmds * sizeof(*h->cmd_pool),
+ &(h->cmd_pool_dhandle));
+ h->errinfo_pool = pci_alloc_consistent(h->pdev,
+ h->nr_cmds * sizeof(*h->errinfo_pool),
+ &(h->errinfo_pool_dhandle));
+ if ((h->cmd_pool_bits == NULL)
+ || (h->cmd_pool == NULL)
+ || (h->errinfo_pool == NULL)) {
+ dev_err(&h->pdev->dev, "out of memory in %s", __func__);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void hpsa_free_cmd_pool(struct ctlr_info *h)
+{
+ kfree(h->cmd_pool_bits);
+ if (h->cmd_pool)
+ pci_free_consistent(h->pdev,
+ h->nr_cmds * sizeof(struct CommandList),
+ h->cmd_pool, h->cmd_pool_dhandle);
+ if (h->errinfo_pool)
+ pci_free_consistent(h->pdev,
+ h->nr_cmds * sizeof(struct ErrorInfo),
+ h->errinfo_pool,
+ h->errinfo_pool_dhandle);
+}
+
+static int hpsa_request_irq(struct ctlr_info *h,
+ irqreturn_t (*msixhandler)(int, void *),
+ irqreturn_t (*intxhandler)(int, void *))
+{
+ int rc;
+
+ if (h->msix_vector || h->msi_vector)
+ rc = request_irq(h->intr[h->intr_mode], msixhandler,
+ IRQF_DISABLED, h->devname, h);
+ else
+ rc = request_irq(h->intr[h->intr_mode], intxhandler,
+ IRQF_DISABLED, h->devname, h);
+ if (rc) {
+ dev_err(&h->pdev->dev, "unable to get irq %d for %s\n",
+ h->intr[h->intr_mode], h->devname);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int __devinit hpsa_kdump_soft_reset(struct ctlr_info *h)
+{
+ if (hpsa_send_host_reset(h, RAID_CTLR_LUNID,
+ HPSA_RESET_TYPE_CONTROLLER)) {
+ dev_warn(&h->pdev->dev, "Resetting array controller failed.\n");
+ return -EIO;
+ }
+
+ dev_info(&h->pdev->dev, "Waiting for board to soft reset.\n");
+ if (hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_NOT_READY)) {
+ dev_warn(&h->pdev->dev, "Soft reset had no effect.\n");
+ return -1;
+ }
+
+ dev_info(&h->pdev->dev, "Board reset, awaiting READY status.\n");
+ if (hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY)) {
+ dev_warn(&h->pdev->dev, "Board failed to become ready "
+ "after soft reset.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void hpsa_undo_allocations_after_kdump_soft_reset(struct ctlr_info *h)
+{
+ free_irq(h->intr[h->intr_mode], h);
+#ifdef CONFIG_PCI_MSI
+ if (h->msix_vector)
+ pci_disable_msix(h->pdev);
+ else if (h->msi_vector)
+ pci_disable_msi(h->pdev);
+#endif /* CONFIG_PCI_MSI */
+ hpsa_free_sg_chain_blocks(h);
+ hpsa_free_cmd_pool(h);
+ kfree(h->blockFetchTable);
+ pci_free_consistent(h->pdev, h->reply_pool_size,
+ h->reply_pool, h->reply_pool_dhandle);
+ if (h->vaddr)
+ iounmap(h->vaddr);
+ if (h->transtable)
+ iounmap(h->transtable);
+ if (h->cfgtable)
+ iounmap(h->cfgtable);
+ pci_release_regions(h->pdev);
+ kfree(h);
+}
+
static int __devinit hpsa_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
int dac, rc;
struct ctlr_info *h;
+ int try_soft_reset = 0;
+ unsigned long flags;
if (number_of_controllers == 0)
printk(KERN_INFO DRIVER_NAME "\n");
rc = hpsa_init_reset_devices(pdev);
- if (rc)
- return rc;
+ if (rc) {
+ if (rc != -ENOTSUPP)
+ return rc;
+ /* If the reset fails in a particular way (it has no way to do
+ * a proper hard reset, so returns -ENOTSUPP) we can try to do
+ * a soft reset once we get the controller configured up to the
+ * point that it can accept a command.
+ */
+ try_soft_reset = 1;
+ rc = 0;
+ }
+
+reinit_after_soft_reset:
/* Command structures must be aligned on a 32-byte boundary because
* the 5 lower bits of the address are used by the hardware. and by
@@ -3847,54 +4130,82 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev,
/* make sure the board interrupts are off */
h->access.set_intr_mask(h, HPSA_INTR_OFF);
- if (h->msix_vector || h->msi_vector)
- rc = request_irq(h->intr[h->intr_mode], do_hpsa_intr_msi,
- IRQF_DISABLED, h->devname, h);
- else
- rc = request_irq(h->intr[h->intr_mode], do_hpsa_intr_intx,
- IRQF_DISABLED, h->devname, h);
- if (rc) {
- dev_err(&pdev->dev, "unable to get irq %d for %s\n",
- h->intr[h->intr_mode], h->devname);
+ if (hpsa_request_irq(h, do_hpsa_intr_msi, do_hpsa_intr_intx))
goto clean2;
- }
-
dev_info(&pdev->dev, "%s: <0x%x> at IRQ %d%s using DAC\n",
h->devname, pdev->device,
h->intr[h->intr_mode], dac ? "" : " not");
-
- h->cmd_pool_bits =
- kmalloc(((h->nr_cmds + BITS_PER_LONG -
- 1) / BITS_PER_LONG) * sizeof(unsigned long), GFP_KERNEL);
- h->cmd_pool = pci_alloc_consistent(h->pdev,
- h->nr_cmds * sizeof(*h->cmd_pool),
- &(h->cmd_pool_dhandle));
- h->errinfo_pool = pci_alloc_consistent(h->pdev,
- h->nr_cmds * sizeof(*h->errinfo_pool),
- &(h->errinfo_pool_dhandle));
- if ((h->cmd_pool_bits == NULL)
- || (h->cmd_pool == NULL)
- || (h->errinfo_pool == NULL)) {
- dev_err(&pdev->dev, "out of memory");
- rc = -ENOMEM;
+ if (hpsa_allocate_cmd_pool(h))
goto clean4;
- }
if (hpsa_allocate_sg_chain_blocks(h))
goto clean4;
init_waitqueue_head(&h->scan_wait_queue);
h->scan_finished = 1; /* no scan currently in progress */
pci_set_drvdata(pdev, h);
- memset(h->cmd_pool_bits, 0,
- ((h->nr_cmds + BITS_PER_LONG -
- 1) / BITS_PER_LONG) * sizeof(unsigned long));
+ h->ndevices = 0;
+ h->scsi_host = NULL;
+ spin_lock_init(&h->devlock);
+ hpsa_put_ctlr_into_performant_mode(h);
+
+ /* At this point, the controller is ready to take commands.
+ * Now, if reset_devices and the hard reset didn't work, try
+ * the soft reset and see if that works.
+ */
+ if (try_soft_reset) {
+
+ /* This is kind of gross. We may or may not get a completion
+ * from the soft reset command, and if we do, then the value
+ * from the fifo may or may not be valid. So, we wait 10 secs
+ * after the reset throwing away any completions we get during
+ * that time. Unregister the interrupt handler and register
+ * fake ones to scoop up any residual completions.
+ */
+ spin_lock_irqsave(&h->lock, flags);
+ h->access.set_intr_mask(h, HPSA_INTR_OFF);
+ spin_unlock_irqrestore(&h->lock, flags);
+ free_irq(h->intr[h->intr_mode], h);
+ rc = hpsa_request_irq(h, hpsa_msix_discard_completions,
+ hpsa_intx_discard_completions);
+ if (rc) {
+ dev_warn(&h->pdev->dev, "Failed to request_irq after "
+ "soft reset.\n");
+ goto clean4;
+ }
+
+ rc = hpsa_kdump_soft_reset(h);
+ if (rc)
+ /* Neither hard nor soft reset worked, we're hosed. */
+ goto clean4;
+
+ dev_info(&h->pdev->dev, "Board READY.\n");
+ dev_info(&h->pdev->dev,
+ "Waiting for stale completions to drain.\n");
+ h->access.set_intr_mask(h, HPSA_INTR_ON);
+ msleep(10000);
+ h->access.set_intr_mask(h, HPSA_INTR_OFF);
+
+ rc = controller_reset_failed(h->cfgtable);
+ if (rc)
+ dev_info(&h->pdev->dev,
+ "Soft reset appears to have failed.\n");
+
+ /* since the controller's reset, we have to go back and re-init
+ * everything. Easiest to just forget what we've done and do it
+ * all over again.
+ */
+ hpsa_undo_allocations_after_kdump_soft_reset(h);
+ try_soft_reset = 0;
+ if (rc)
+ /* don't go to clean4, we already unallocated */
+ return -ENODEV;
- hpsa_scsi_setup(h);
+ goto reinit_after_soft_reset;
+ }
/* Turn the interrupts on so we can service requests */
h->access.set_intr_mask(h, HPSA_INTR_ON);
- hpsa_put_ctlr_into_performant_mode(h);
hpsa_hba_inquiry(h);
hpsa_register_scsi(h); /* hook ourselves into SCSI subsystem */
h->busy_initializing = 0;
@@ -3902,16 +4213,7 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev,
clean4:
hpsa_free_sg_chain_blocks(h);
- kfree(h->cmd_pool_bits);
- if (h->cmd_pool)
- pci_free_consistent(h->pdev,
- h->nr_cmds * sizeof(struct CommandList),
- h->cmd_pool, h->cmd_pool_dhandle);
- if (h->errinfo_pool)
- pci_free_consistent(h->pdev,
- h->nr_cmds * sizeof(struct ErrorInfo),
- h->errinfo_pool,
- h->errinfo_pool_dhandle);
+ hpsa_free_cmd_pool(h);
free_irq(h->intr[h->intr_mode], h);
clean2:
clean1:
diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h
index 621a1530054..6d8dcd4dd06 100644
--- a/drivers/scsi/hpsa.h
+++ b/drivers/scsi/hpsa.h
@@ -127,10 +127,12 @@ struct ctlr_info {
};
#define HPSA_ABORT_MSG 0
#define HPSA_DEVICE_RESET_MSG 1
-#define HPSA_BUS_RESET_MSG 2
-#define HPSA_HOST_RESET_MSG 3
+#define HPSA_RESET_TYPE_CONTROLLER 0x00
+#define HPSA_RESET_TYPE_BUS 0x01
+#define HPSA_RESET_TYPE_TARGET 0x03
+#define HPSA_RESET_TYPE_LUN 0x04
#define HPSA_MSG_SEND_RETRY_LIMIT 10
-#define HPSA_MSG_SEND_RETRY_INTERVAL_MSECS 1000
+#define HPSA_MSG_SEND_RETRY_INTERVAL_MSECS (10000)
/* Maximum time in seconds driver will wait for command completions
* when polling before giving up.
@@ -155,7 +157,7 @@ struct ctlr_info {
* HPSA_BOARD_READY_ITERATIONS are derived from those.
*/
#define HPSA_BOARD_READY_WAIT_SECS (120)
-#define HPSA_BOARD_NOT_READY_WAIT_SECS (10)
+#define HPSA_BOARD_NOT_READY_WAIT_SECS (100)
#define HPSA_BOARD_READY_POLL_INTERVAL_MSECS (100)
#define HPSA_BOARD_READY_POLL_INTERVAL \
((HPSA_BOARD_READY_POLL_INTERVAL_MSECS * HZ) / 1000)
@@ -212,6 +214,7 @@ static void SA5_submit_command(struct ctlr_info *h,
dev_dbg(&h->pdev->dev, "Sending %x, tag = %x\n", c->busaddr,
c->Header.Tag.lower);
writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
+ (void) readl(h->vaddr + SA5_REQUEST_PORT_OFFSET);
h->commands_outstanding++;
if (h->commands_outstanding > h->max_outstanding)
h->max_outstanding = h->commands_outstanding;
@@ -227,10 +230,12 @@ static void SA5_intr_mask(struct ctlr_info *h, unsigned long val)
if (val) { /* Turn interrupts on */
h->interrupts_enabled = 1;
writel(0, h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
+ (void) readl(h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
} else { /* Turn them off */
h->interrupts_enabled = 0;
writel(SA5_INTR_OFF,
h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
+ (void) readl(h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
}
}
@@ -239,10 +244,12 @@ static void SA5_performant_intr_mask(struct ctlr_info *h, unsigned long val)
if (val) { /* turn on interrupts */
h->interrupts_enabled = 1;
writel(0, h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
+ (void) readl(h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
} else {
h->interrupts_enabled = 0;
writel(SA5_PERF_INTR_OFF,
h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
+ (void) readl(h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
}
}
diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h
index 18464900e76..55d741b019d 100644
--- a/drivers/scsi/hpsa_cmd.h
+++ b/drivers/scsi/hpsa_cmd.h
@@ -101,6 +101,7 @@
#define CFGTBL_ChangeReq 0x00000001l
#define CFGTBL_AccCmds 0x00000001l
#define DOORBELL_CTLR_RESET 0x00000004l
+#define DOORBELL_CTLR_RESET2 0x00000020l
#define CFGTBL_Trans_Simple 0x00000002l
#define CFGTBL_Trans_Performant 0x00000004l
@@ -256,14 +257,6 @@ struct ErrorInfo {
#define CMD_IOCTL_PEND 0x01
#define CMD_SCSI 0x03
-/* This structure needs to be divisible by 32 for new
- * indexing method and performant mode.
- */
-#define PAD32 32
-#define PAD64DIFF 0
-#define USEEXTRA ((sizeof(void *) - 4)/4)
-#define PADSIZE (PAD32 + PAD64DIFF * USEEXTRA)
-
#define DIRECT_LOOKUP_SHIFT 5
#define DIRECT_LOOKUP_BIT 0x10
#define DIRECT_LOOKUP_MASK (~((1 << DIRECT_LOOKUP_SHIFT) - 1))
@@ -345,6 +338,8 @@ struct CfgTable {
u8 reserved[0x78 - 0x58];
u32 misc_fw_support; /* offset 0x78 */
#define MISC_FW_DOORBELL_RESET (0x02)
+#define MISC_FW_DOORBELL_RESET2 (0x010)
+ u8 driver_version[32];
};
#define NUM_BLOCKFETCH_ENTRIES 8
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c
index 041958453e2..3d391dc3f11 100644
--- a/drivers/scsi/ibmvscsi/ibmvscsi.c
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.c
@@ -1849,8 +1849,7 @@ static void ibmvscsi_do_work(struct ibmvscsi_host_data *hostdata)
rc = ibmvscsi_ops->reset_crq_queue(&hostdata->queue, hostdata);
if (!rc)
rc = ibmvscsi_ops->send_crq(hostdata, 0xC001000000000000LL, 0);
- if (!rc)
- rc = vio_enable_interrupts(to_vio_dev(hostdata->dev));
+ vio_enable_interrupts(to_vio_dev(hostdata->dev));
} else if (hostdata->reenable_crq) {
smp_rmb();
action = "enable";
diff --git a/drivers/scsi/in2000.c b/drivers/scsi/in2000.c
index 6568aab745a..92109b12639 100644
--- a/drivers/scsi/in2000.c
+++ b/drivers/scsi/in2000.c
@@ -343,7 +343,7 @@ static int in2000_queuecommand_lck(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
instance = cmd->device->host;
hostdata = (struct IN2000_hostdata *) instance->hostdata;
- DB(DB_QUEUE_COMMAND, scmd_printk(KERN_DEBUG, cmd, "Q-%02x-%ld(", cmd->cmnd[0], cmd->serial_number))
+ DB(DB_QUEUE_COMMAND, scmd_printk(KERN_DEBUG, cmd, "Q-%02x(", cmd->cmnd[0]))
/* Set up a few fields in the Scsi_Cmnd structure for our own use:
* - host_scribble is the pointer to the next cmd in the input queue
@@ -427,7 +427,7 @@ static int in2000_queuecommand_lck(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
in2000_execute(cmd->device->host);
- DB(DB_QUEUE_COMMAND, printk(")Q-%ld ", cmd->serial_number))
+ DB(DB_QUEUE_COMMAND, printk(")Q "))
return 0;
}
@@ -705,7 +705,7 @@ static void in2000_execute(struct Scsi_Host *instance)
* to search the input_Q again...
*/
- DB(DB_EXECUTE, printk("%s%ld)EX-2 ", (cmd->SCp.phase) ? "d:" : "", cmd->serial_number))
+ DB(DB_EXECUTE, printk("%s)EX-2 ", (cmd->SCp.phase) ? "d:" : ""))
}
@@ -1149,7 +1149,7 @@ static irqreturn_t in2000_intr(int irqnum, void *dev_id)
case CSR_XFER_DONE | PHS_COMMAND:
case CSR_UNEXP | PHS_COMMAND:
case CSR_SRV_REQ | PHS_COMMAND:
- DB(DB_INTR, printk("CMND-%02x,%ld", cmd->cmnd[0], cmd->serial_number))
+ DB(DB_INTR, printk("CMND-%02x", cmd->cmnd[0]))
transfer_pio(cmd->cmnd, cmd->cmd_len, DATA_OUT_DIR, hostdata);
hostdata->state = S_CONNECTED;
break;
@@ -1191,7 +1191,7 @@ static irqreturn_t in2000_intr(int irqnum, void *dev_id)
switch (msg) {
case COMMAND_COMPLETE:
- DB(DB_INTR, printk("CCMP-%ld", cmd->serial_number))
+ DB(DB_INTR, printk("CCMP"))
write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);
hostdata->state = S_PRE_CMP_DISC;
break;
@@ -1329,7 +1329,7 @@ static irqreturn_t in2000_intr(int irqnum, void *dev_id)
write_3393(hostdata, WD_SOURCE_ID, SRCID_ER);
if (phs == 0x60) {
- DB(DB_INTR, printk("SX-DONE-%ld", cmd->serial_number))
+ DB(DB_INTR, printk("SX-DONE"))
cmd->SCp.Message = COMMAND_COMPLETE;
lun = read_3393(hostdata, WD_TARGET_LUN);
DB(DB_INTR, printk(":%d.%d", cmd->SCp.Status, lun))
@@ -1350,7 +1350,7 @@ static irqreturn_t in2000_intr(int irqnum, void *dev_id)
in2000_execute(instance);
} else {
- printk("%02x:%02x:%02x-%ld: Unknown SEL_XFER_DONE phase!!---", asr, sr, phs, cmd->serial_number);
+ printk("%02x:%02x:%02x: Unknown SEL_XFER_DONE phase!!---", asr, sr, phs);
}
break;
@@ -1417,7 +1417,7 @@ static irqreturn_t in2000_intr(int irqnum, void *dev_id)
spin_unlock_irqrestore(instance->host_lock, flags);
return IRQ_HANDLED;
}
- DB(DB_INTR, printk("UNEXP_DISC-%ld", cmd->serial_number))
+ DB(DB_INTR, printk("UNEXP_DISC"))
hostdata->connected = NULL;
hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
hostdata->state = S_UNCONNECTED;
@@ -1442,7 +1442,7 @@ static irqreturn_t in2000_intr(int irqnum, void *dev_id)
*/
write_3393(hostdata, WD_SOURCE_ID, SRCID_ER);
- DB(DB_INTR, printk("DISC-%ld", cmd->serial_number))
+ DB(DB_INTR, printk("DISC"))
if (cmd == NULL) {
printk(" - Already disconnected! ");
hostdata->state = S_UNCONNECTED;
@@ -1575,7 +1575,6 @@ static irqreturn_t in2000_intr(int irqnum, void *dev_id)
} else
hostdata->state = S_CONNECTED;
- DB(DB_INTR, printk("-%ld", cmd->serial_number))
break;
default:
@@ -1704,7 +1703,7 @@ static int __in2000_abort(Scsi_Cmnd * cmd)
prev->host_scribble = cmd->host_scribble;
cmd->host_scribble = NULL;
cmd->result = DID_ABORT << 16;
- printk(KERN_WARNING "scsi%d: Abort - removing command %ld from input_Q. ", instance->host_no, cmd->serial_number);
+ printk(KERN_WARNING "scsi%d: Abort - removing command from input_Q. ", instance->host_no);
cmd->scsi_done(cmd);
return SUCCESS;
}
@@ -1725,7 +1724,7 @@ static int __in2000_abort(Scsi_Cmnd * cmd)
if (hostdata->connected == cmd) {
- printk(KERN_WARNING "scsi%d: Aborting connected command %ld - ", instance->host_no, cmd->serial_number);
+ printk(KERN_WARNING "scsi%d: Aborting connected command - ", instance->host_no);
printk("sending wd33c93 ABORT command - ");
write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
@@ -2270,7 +2269,7 @@ static int in2000_proc_info(struct Scsi_Host *instance, char *buf, char **start,
strcat(bp, "\nconnected: ");
if (hd->connected) {
cmd = (Scsi_Cmnd *) hd->connected;
- sprintf(tbuf, " %ld-%d:%d(%02x)", cmd->serial_number, cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
+ sprintf(tbuf, " %d:%d(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
strcat(bp, tbuf);
}
}
@@ -2278,7 +2277,7 @@ static int in2000_proc_info(struct Scsi_Host *instance, char *buf, char **start,
strcat(bp, "\ninput_Q: ");
cmd = (Scsi_Cmnd *) hd->input_Q;
while (cmd) {
- sprintf(tbuf, " %ld-%d:%d(%02x)", cmd->serial_number, cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
+ sprintf(tbuf, " %d:%d(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
strcat(bp, tbuf);
cmd = (Scsi_Cmnd *) cmd->host_scribble;
}
@@ -2287,7 +2286,7 @@ static int in2000_proc_info(struct Scsi_Host *instance, char *buf, char **start,
strcat(bp, "\ndisconnected_Q:");
cmd = (Scsi_Cmnd *) hd->disconnected_Q;
while (cmd) {
- sprintf(tbuf, " %ld-%d:%d(%02x)", cmd->serial_number, cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
+ sprintf(tbuf, " %d:%d(%02x)", cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
strcat(bp, tbuf);
cmd = (Scsi_Cmnd *) cmd->host_scribble;
}
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index 0621238fac4..12868ca4611 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -60,6 +60,7 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/slab.h>
+#include <linux/vmalloc.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/pci.h>
@@ -2717,13 +2718,18 @@ static int ipr_sdt_copy(struct ipr_ioa_cfg *ioa_cfg,
unsigned long pci_address, u32 length)
{
int bytes_copied = 0;
- int cur_len, rc, rem_len, rem_page_len;
+ int cur_len, rc, rem_len, rem_page_len, max_dump_size;
__be32 *page;
unsigned long lock_flags = 0;
struct ipr_ioa_dump *ioa_dump = &ioa_cfg->dump->ioa_dump;
+ if (ioa_cfg->sis64)
+ max_dump_size = IPR_FMT3_MAX_IOA_DUMP_SIZE;
+ else
+ max_dump_size = IPR_FMT2_MAX_IOA_DUMP_SIZE;
+
while (bytes_copied < length &&
- (ioa_dump->hdr.len + bytes_copied) < IPR_MAX_IOA_DUMP_SIZE) {
+ (ioa_dump->hdr.len + bytes_copied) < max_dump_size) {
if (ioa_dump->page_offset >= PAGE_SIZE ||
ioa_dump->page_offset == 0) {
page = (__be32 *)__get_free_page(GFP_ATOMIC);
@@ -2885,8 +2891,8 @@ static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)
unsigned long lock_flags = 0;
struct ipr_driver_dump *driver_dump = &dump->driver_dump;
struct ipr_ioa_dump *ioa_dump = &dump->ioa_dump;
- u32 num_entries, start_off, end_off;
- u32 bytes_to_copy, bytes_copied, rc;
+ u32 num_entries, max_num_entries, start_off, end_off;
+ u32 max_dump_size, bytes_to_copy, bytes_copied, rc;
struct ipr_sdt *sdt;
int valid = 1;
int i;
@@ -2947,8 +2953,18 @@ static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)
on entries in this table */
sdt = &ioa_dump->sdt;
+ if (ioa_cfg->sis64) {
+ max_num_entries = IPR_FMT3_NUM_SDT_ENTRIES;
+ max_dump_size = IPR_FMT3_MAX_IOA_DUMP_SIZE;
+ } else {
+ max_num_entries = IPR_FMT2_NUM_SDT_ENTRIES;
+ max_dump_size = IPR_FMT2_MAX_IOA_DUMP_SIZE;
+ }
+
+ bytes_to_copy = offsetof(struct ipr_sdt, entry) +
+ (max_num_entries * sizeof(struct ipr_sdt_entry));
rc = ipr_get_ldump_data_section(ioa_cfg, start_addr, (__be32 *)sdt,
- sizeof(struct ipr_sdt) / sizeof(__be32));
+ bytes_to_copy / sizeof(__be32));
/* Smart Dump table is ready to use and the first entry is valid */
if (rc || ((be32_to_cpu(sdt->hdr.state) != IPR_FMT3_SDT_READY_TO_USE) &&
@@ -2964,13 +2980,20 @@ static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)
num_entries = be32_to_cpu(sdt->hdr.num_entries_used);
- if (num_entries > IPR_NUM_SDT_ENTRIES)
- num_entries = IPR_NUM_SDT_ENTRIES;
+ if (num_entries > max_num_entries)
+ num_entries = max_num_entries;
+
+ /* Update dump length to the actual data to be copied */
+ dump->driver_dump.hdr.len += sizeof(struct ipr_sdt_header);
+ if (ioa_cfg->sis64)
+ dump->driver_dump.hdr.len += num_entries * sizeof(struct ipr_sdt_entry);
+ else
+ dump->driver_dump.hdr.len += max_num_entries * sizeof(struct ipr_sdt_entry);
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
for (i = 0; i < num_entries; i++) {
- if (ioa_dump->hdr.len > IPR_MAX_IOA_DUMP_SIZE) {
+ if (ioa_dump->hdr.len > max_dump_size) {
driver_dump->hdr.status = IPR_DUMP_STATUS_QUAL_SUCCESS;
break;
}
@@ -2989,7 +3012,7 @@ static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)
valid = 0;
}
if (valid) {
- if (bytes_to_copy > IPR_MAX_IOA_DUMP_SIZE) {
+ if (bytes_to_copy > max_dump_size) {
sdt->entry[i].flags &= ~IPR_SDT_VALID_ENTRY;
continue;
}
@@ -3044,6 +3067,7 @@ static void ipr_release_dump(struct kref *kref)
for (i = 0; i < dump->ioa_dump.next_page_index; i++)
free_page((unsigned long) dump->ioa_dump.ioa_data[i]);
+ vfree(dump->ioa_dump.ioa_data);
kfree(dump);
LEAVE;
}
@@ -3835,7 +3859,7 @@ static ssize_t ipr_read_dump(struct file *filp, struct kobject *kobj,
struct ipr_dump *dump;
unsigned long lock_flags = 0;
char *src;
- int len;
+ int len, sdt_end;
size_t rc = count;
if (!capable(CAP_SYS_ADMIN))
@@ -3875,9 +3899,17 @@ static ssize_t ipr_read_dump(struct file *filp, struct kobject *kobj,
off -= sizeof(dump->driver_dump);
- if (count && off < offsetof(struct ipr_ioa_dump, ioa_data)) {
- if (off + count > offsetof(struct ipr_ioa_dump, ioa_data))
- len = offsetof(struct ipr_ioa_dump, ioa_data) - off;
+ if (ioa_cfg->sis64)
+ sdt_end = offsetof(struct ipr_ioa_dump, sdt.entry) +
+ (be32_to_cpu(dump->ioa_dump.sdt.hdr.num_entries_used) *
+ sizeof(struct ipr_sdt_entry));
+ else
+ sdt_end = offsetof(struct ipr_ioa_dump, sdt.entry) +
+ (IPR_FMT2_NUM_SDT_ENTRIES * sizeof(struct ipr_sdt_entry));
+
+ if (count && off < sdt_end) {
+ if (off + count > sdt_end)
+ len = sdt_end - off;
else
len = count;
src = (u8 *)&dump->ioa_dump + off;
@@ -3887,7 +3919,7 @@ static ssize_t ipr_read_dump(struct file *filp, struct kobject *kobj,
count -= len;
}
- off -= offsetof(struct ipr_ioa_dump, ioa_data);
+ off -= sdt_end;
while (count) {
if ((off & PAGE_MASK) != ((off + count) & PAGE_MASK))
@@ -3916,6 +3948,7 @@ static ssize_t ipr_read_dump(struct file *filp, struct kobject *kobj,
static int ipr_alloc_dump(struct ipr_ioa_cfg *ioa_cfg)
{
struct ipr_dump *dump;
+ __be32 **ioa_data;
unsigned long lock_flags = 0;
dump = kzalloc(sizeof(struct ipr_dump), GFP_KERNEL);
@@ -3925,6 +3958,19 @@ static int ipr_alloc_dump(struct ipr_ioa_cfg *ioa_cfg)
return -ENOMEM;
}
+ if (ioa_cfg->sis64)
+ ioa_data = vmalloc(IPR_FMT3_MAX_NUM_DUMP_PAGES * sizeof(__be32 *));
+ else
+ ioa_data = vmalloc(IPR_FMT2_MAX_NUM_DUMP_PAGES * sizeof(__be32 *));
+
+ if (!ioa_data) {
+ ipr_err("Dump memory allocation failed\n");
+ kfree(dump);
+ return -ENOMEM;
+ }
+
+ dump->ioa_dump.ioa_data = ioa_data;
+
kref_init(&dump->kref);
dump->ioa_cfg = ioa_cfg;
@@ -3932,6 +3978,7 @@ static int ipr_alloc_dump(struct ipr_ioa_cfg *ioa_cfg)
if (INACTIVE != ioa_cfg->sdt_state) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
+ vfree(dump->ioa_dump.ioa_data);
kfree(dump);
return 0;
}
@@ -4953,9 +5000,35 @@ static int ipr_eh_abort(struct scsi_cmnd * scsi_cmd)
* IRQ_NONE / IRQ_HANDLED
**/
static irqreturn_t ipr_handle_other_interrupt(struct ipr_ioa_cfg *ioa_cfg,
- volatile u32 int_reg)
+ u32 int_reg)
{
irqreturn_t rc = IRQ_HANDLED;
+ u32 int_mask_reg;
+
+ int_mask_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg32);
+ int_reg &= ~int_mask_reg;
+
+ /* If an interrupt on the adapter did not occur, ignore it.
+ * Or in the case of SIS 64, check for a stage change interrupt.
+ */
+ if ((int_reg & IPR_PCII_OPER_INTERRUPTS) == 0) {
+ if (ioa_cfg->sis64) {
+ int_mask_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg);
+ int_reg = readl(ioa_cfg->regs.sense_interrupt_reg) & ~int_mask_reg;
+ if (int_reg & IPR_PCII_IPL_STAGE_CHANGE) {
+
+ /* clear stage change */
+ writel(IPR_PCII_IPL_STAGE_CHANGE, ioa_cfg->regs.clr_interrupt_reg);
+ int_reg = readl(ioa_cfg->regs.sense_interrupt_reg) & ~int_mask_reg;
+ list_del(&ioa_cfg->reset_cmd->queue);
+ del_timer(&ioa_cfg->reset_cmd->timer);
+ ipr_reset_ioa_job(ioa_cfg->reset_cmd);
+ return IRQ_HANDLED;
+ }
+ }
+
+ return IRQ_NONE;
+ }
if (int_reg & IPR_PCII_IOA_TRANS_TO_OPER) {
/* Mask the interrupt */
@@ -4968,6 +5041,13 @@ static irqreturn_t ipr_handle_other_interrupt(struct ipr_ioa_cfg *ioa_cfg,
list_del(&ioa_cfg->reset_cmd->queue);
del_timer(&ioa_cfg->reset_cmd->timer);
ipr_reset_ioa_job(ioa_cfg->reset_cmd);
+ } else if ((int_reg & IPR_PCII_HRRQ_UPDATED) == int_reg) {
+ if (ipr_debug && printk_ratelimit())
+ dev_err(&ioa_cfg->pdev->dev,
+ "Spurious interrupt detected. 0x%08X\n", int_reg);
+ writel(IPR_PCII_HRRQ_UPDATED, ioa_cfg->regs.clr_interrupt_reg32);
+ int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32);
+ return IRQ_NONE;
} else {
if (int_reg & IPR_PCII_IOA_UNIT_CHECKED)
ioa_cfg->ioa_unit_checked = 1;
@@ -5016,10 +5096,11 @@ static irqreturn_t ipr_isr(int irq, void *devp)
{
struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)devp;
unsigned long lock_flags = 0;
- volatile u32 int_reg, int_mask_reg;
+ u32 int_reg = 0;
u32 ioasc;
u16 cmd_index;
int num_hrrq = 0;
+ int irq_none = 0;
struct ipr_cmnd *ipr_cmd;
irqreturn_t rc = IRQ_NONE;
@@ -5031,33 +5112,6 @@ static irqreturn_t ipr_isr(int irq, void *devp)
return IRQ_NONE;
}
- int_mask_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg32);
- int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32) & ~int_mask_reg;
-
- /* If an interrupt on the adapter did not occur, ignore it.
- * Or in the case of SIS 64, check for a stage change interrupt.
- */
- if (unlikely((int_reg & IPR_PCII_OPER_INTERRUPTS) == 0)) {
- if (ioa_cfg->sis64) {
- int_mask_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg);
- int_reg = readl(ioa_cfg->regs.sense_interrupt_reg) & ~int_mask_reg;
- if (int_reg & IPR_PCII_IPL_STAGE_CHANGE) {
-
- /* clear stage change */
- writel(IPR_PCII_IPL_STAGE_CHANGE, ioa_cfg->regs.clr_interrupt_reg);
- int_reg = readl(ioa_cfg->regs.sense_interrupt_reg) & ~int_mask_reg;
- list_del(&ioa_cfg->reset_cmd->queue);
- del_timer(&ioa_cfg->reset_cmd->timer);
- ipr_reset_ioa_job(ioa_cfg->reset_cmd);
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
- return IRQ_HANDLED;
- }
- }
-
- spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
- return IRQ_NONE;
- }
-
while (1) {
ipr_cmd = NULL;
@@ -5097,7 +5151,7 @@ static irqreturn_t ipr_isr(int irq, void *devp)
/* Clear the PCI interrupt */
do {
writel(IPR_PCII_HRRQ_UPDATED, ioa_cfg->regs.clr_interrupt_reg32);
- int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32) & ~int_mask_reg;
+ int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32);
} while (int_reg & IPR_PCII_HRRQ_UPDATED &&
num_hrrq++ < IPR_MAX_HRRQ_RETRIES);
@@ -5107,6 +5161,9 @@ static irqreturn_t ipr_isr(int irq, void *devp)
return IRQ_HANDLED;
}
+ } else if (rc == IRQ_NONE && irq_none == 0) {
+ int_reg = readl(ioa_cfg->regs.sense_interrupt_reg32);
+ irq_none++;
} else
break;
}
@@ -5143,7 +5200,8 @@ static int ipr_build_ioadl64(struct ipr_ioa_cfg *ioa_cfg,
nseg = scsi_dma_map(scsi_cmd);
if (nseg < 0) {
- dev_err(&ioa_cfg->pdev->dev, "pci_map_sg failed!\n");
+ if (printk_ratelimit())
+ dev_err(&ioa_cfg->pdev->dev, "pci_map_sg failed!\n");
return -1;
}
@@ -5773,7 +5831,8 @@ static int ipr_queuecommand_lck(struct scsi_cmnd *scsi_cmd,
}
ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_LINK_DESC;
- ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_DELAY_AFTER_RST;
+ if (ipr_is_gscsi(res))
+ ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_DELAY_AFTER_RST;
ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_ALIGNED_BFR;
ioarcb->cmd_pkt.flags_lo |= ipr_get_task_attributes(scsi_cmd);
}
@@ -7516,7 +7575,7 @@ static int ipr_reset_get_unit_check_job(struct ipr_cmnd *ipr_cmd)
static int ipr_reset_restore_cfg_space(struct ipr_cmnd *ipr_cmd)
{
struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
- volatile u32 int_reg;
+ u32 int_reg;
ENTER;
ioa_cfg->pdev->state_saved = true;
@@ -7555,7 +7614,10 @@ static int ipr_reset_restore_cfg_space(struct ipr_cmnd *ipr_cmd)
ipr_cmd->job_step = ipr_reset_enable_ioa;
if (GET_DUMP == ioa_cfg->sdt_state) {
- ipr_reset_start_timer(ipr_cmd, IPR_DUMP_TIMEOUT);
+ if (ioa_cfg->sis64)
+ ipr_reset_start_timer(ipr_cmd, IPR_SIS64_DUMP_TIMEOUT);
+ else
+ ipr_reset_start_timer(ipr_cmd, IPR_SIS32_DUMP_TIMEOUT);
ipr_cmd->job_step = ipr_reset_wait_for_dump;
schedule_work(&ioa_cfg->work_q);
return IPR_RC_JOB_RETURN;
diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h
index 13f425fb885..f93f8637c5a 100644
--- a/drivers/scsi/ipr.h
+++ b/drivers/scsi/ipr.h
@@ -38,8 +38,8 @@
/*
* Literals
*/
-#define IPR_DRIVER_VERSION "2.5.1"
-#define IPR_DRIVER_DATE "(August 10, 2010)"
+#define IPR_DRIVER_VERSION "2.5.2"
+#define IPR_DRIVER_DATE "(April 27, 2011)"
/*
* IPR_MAX_CMD_PER_LUN: This defines the maximum number of outstanding
@@ -217,7 +217,8 @@
#define IPR_CHECK_FOR_RESET_TIMEOUT (HZ / 10)
#define IPR_WAIT_FOR_BIST_TIMEOUT (2 * HZ)
#define IPR_PCI_RESET_TIMEOUT (HZ / 2)
-#define IPR_DUMP_TIMEOUT (15 * HZ)
+#define IPR_SIS32_DUMP_TIMEOUT (15 * HZ)
+#define IPR_SIS64_DUMP_TIMEOUT (40 * HZ)
#define IPR_DUMP_DELAY_SECONDS 4
#define IPR_DUMP_DELAY_TIMEOUT (IPR_DUMP_DELAY_SECONDS * HZ)
@@ -285,9 +286,12 @@ IPR_PCII_NO_HOST_RRQ | IPR_PCII_IOARRIN_LOST | IPR_PCII_MMIO_ERROR)
/*
* Dump literals
*/
-#define IPR_MAX_IOA_DUMP_SIZE (4 * 1024 * 1024)
-#define IPR_NUM_SDT_ENTRIES 511
-#define IPR_MAX_NUM_DUMP_PAGES ((IPR_MAX_IOA_DUMP_SIZE / PAGE_SIZE) + 1)
+#define IPR_FMT2_MAX_IOA_DUMP_SIZE (4 * 1024 * 1024)
+#define IPR_FMT3_MAX_IOA_DUMP_SIZE (32 * 1024 * 1024)
+#define IPR_FMT2_NUM_SDT_ENTRIES 511
+#define IPR_FMT3_NUM_SDT_ENTRIES 0xFFF
+#define IPR_FMT2_MAX_NUM_DUMP_PAGES ((IPR_FMT2_MAX_IOA_DUMP_SIZE / PAGE_SIZE) + 1)
+#define IPR_FMT3_MAX_NUM_DUMP_PAGES ((IPR_FMT3_MAX_IOA_DUMP_SIZE / PAGE_SIZE) + 1)
/*
* Misc literals
@@ -474,7 +478,7 @@ struct ipr_cmd_pkt {
u8 flags_lo;
#define IPR_FLAGS_LO_ALIGNED_BFR 0x20
-#define IPR_FLAGS_LO_DELAY_AFTER_RST 0x10
+#define IPR_FLAGS_LO_DELAY_AFTER_RST 0x10
#define IPR_FLAGS_LO_UNTAGGED_TASK 0x00
#define IPR_FLAGS_LO_SIMPLE_TASK 0x02
#define IPR_FLAGS_LO_ORDERED_TASK 0x04
@@ -1164,7 +1168,7 @@ struct ipr_sdt_header {
struct ipr_sdt {
struct ipr_sdt_header hdr;
- struct ipr_sdt_entry entry[IPR_NUM_SDT_ENTRIES];
+ struct ipr_sdt_entry entry[IPR_FMT3_NUM_SDT_ENTRIES];
}__attribute__((packed, aligned (4)));
struct ipr_uc_sdt {
@@ -1608,7 +1612,7 @@ struct ipr_driver_dump {
struct ipr_ioa_dump {
struct ipr_dump_entry_header hdr;
struct ipr_sdt sdt;
- __be32 *ioa_data[IPR_MAX_NUM_DUMP_PAGES];
+ __be32 **ioa_data;
u32 reserved;
u32 next_page_index;
u32 page_offset;
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index 5b799a37ad0..2a3a4720a77 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -57,9 +57,6 @@ static struct kmem_cache *scsi_pkt_cachep;
#define FC_SRB_READ (1 << 1)
#define FC_SRB_WRITE (1 << 0)
-/* constant added to e_d_tov timeout to get rec_tov value */
-#define REC_TOV_CONST 1
-
/*
* The SCp.ptr should be tested and set under the scsi_pkt_queue lock
*/
@@ -248,7 +245,7 @@ static inline void fc_fcp_unlock_pkt(struct fc_fcp_pkt *fsp)
/**
* fc_fcp_timer_set() - Start a timer for a fcp_pkt
* @fsp: The FCP packet to start a timer for
- * @delay: The timeout period for the timer
+ * @delay: The timeout period in jiffies
*/
static void fc_fcp_timer_set(struct fc_fcp_pkt *fsp, unsigned long delay)
{
@@ -335,22 +332,23 @@ static void fc_fcp_ddp_done(struct fc_fcp_pkt *fsp)
/**
* fc_fcp_can_queue_ramp_up() - increases can_queue
* @lport: lport to ramp up can_queue
- *
- * Locking notes: Called with Scsi_Host lock held
*/
static void fc_fcp_can_queue_ramp_up(struct fc_lport *lport)
{
struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
+ unsigned long flags;
int can_queue;
+ spin_lock_irqsave(lport->host->host_lock, flags);
+
if (si->last_can_queue_ramp_up_time &&
(time_before(jiffies, si->last_can_queue_ramp_up_time +
FC_CAN_QUEUE_PERIOD)))
- return;
+ goto unlock;
if (time_before(jiffies, si->last_can_queue_ramp_down_time +
FC_CAN_QUEUE_PERIOD))
- return;
+ goto unlock;
si->last_can_queue_ramp_up_time = jiffies;
@@ -362,6 +360,9 @@ static void fc_fcp_can_queue_ramp_up(struct fc_lport *lport)
lport->host->can_queue = can_queue;
shost_printk(KERN_ERR, lport->host, "libfc: increased "
"can_queue to %d.\n", can_queue);
+
+unlock:
+ spin_unlock_irqrestore(lport->host->host_lock, flags);
}
/**
@@ -373,18 +374,19 @@ static void fc_fcp_can_queue_ramp_up(struct fc_lport *lport)
* commands complete or timeout, then try again with a reduced
* can_queue. Eventually we will hit the point where we run
* on all reserved structs.
- *
- * Locking notes: Called with Scsi_Host lock held
*/
static void fc_fcp_can_queue_ramp_down(struct fc_lport *lport)
{
struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
+ unsigned long flags;
int can_queue;
+ spin_lock_irqsave(lport->host->host_lock, flags);
+
if (si->last_can_queue_ramp_down_time &&
(time_before(jiffies, si->last_can_queue_ramp_down_time +
FC_CAN_QUEUE_PERIOD)))
- return;
+ goto unlock;
si->last_can_queue_ramp_down_time = jiffies;
@@ -395,6 +397,9 @@ static void fc_fcp_can_queue_ramp_down(struct fc_lport *lport)
lport->host->can_queue = can_queue;
shost_printk(KERN_ERR, lport->host, "libfc: Could not allocate frame.\n"
"Reducing can_queue to %d.\n", can_queue);
+
+unlock:
+ spin_unlock_irqrestore(lport->host->host_lock, flags);
}
/*
@@ -409,16 +414,13 @@ static inline struct fc_frame *fc_fcp_frame_alloc(struct fc_lport *lport,
size_t len)
{
struct fc_frame *fp;
- unsigned long flags;
fp = fc_frame_alloc(lport, len);
if (likely(fp))
return fp;
/* error case */
- spin_lock_irqsave(lport->host->host_lock, flags);
fc_fcp_can_queue_ramp_down(lport);
- spin_unlock_irqrestore(lport->host->host_lock, flags);
return NULL;
}
@@ -1093,16 +1095,14 @@ static int fc_fcp_pkt_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp)
/**
* get_fsp_rec_tov() - Helper function to get REC_TOV
* @fsp: the FCP packet
+ *
+ * Returns rec tov in jiffies as rpriv->e_d_tov + 1 second
*/
static inline unsigned int get_fsp_rec_tov(struct fc_fcp_pkt *fsp)
{
- struct fc_rport *rport;
- struct fc_rport_libfc_priv *rpriv;
-
- rport = fsp->rport;
- rpriv = rport->dd_data;
+ struct fc_rport_libfc_priv *rpriv = fsp->rport->dd_data;
- return rpriv->e_d_tov + REC_TOV_CONST;
+ return msecs_to_jiffies(rpriv->e_d_tov) + HZ;
}
/**
@@ -1122,7 +1122,6 @@ static int fc_fcp_cmd_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp,
struct fc_rport_libfc_priv *rpriv;
const size_t len = sizeof(fsp->cdb_cmd);
int rc = 0;
- unsigned int rec_tov;
if (fc_fcp_lock_pkt(fsp))
return 0;
@@ -1153,12 +1152,9 @@ static int fc_fcp_cmd_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp,
fsp->seq_ptr = seq;
fc_fcp_pkt_hold(fsp); /* hold for fc_fcp_pkt_destroy */
- rec_tov = get_fsp_rec_tov(fsp);
-
setup_timer(&fsp->timer, fc_fcp_timeout, (unsigned long)fsp);
-
if (rpriv->flags & FC_RP_FLAGS_REC_SUPPORTED)
- fc_fcp_timer_set(fsp, rec_tov);
+ fc_fcp_timer_set(fsp, get_fsp_rec_tov(fsp));
unlock:
fc_fcp_unlock_pkt(fsp);
@@ -1235,16 +1231,14 @@ static void fc_lun_reset_send(unsigned long data)
{
struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data;
struct fc_lport *lport = fsp->lp;
- unsigned int rec_tov;
if (lport->tt.fcp_cmd_send(lport, fsp, fc_tm_done)) {
if (fsp->recov_retry++ >= FC_MAX_RECOV_RETRY)
return;
if (fc_fcp_lock_pkt(fsp))
return;
- rec_tov = get_fsp_rec_tov(fsp);
setup_timer(&fsp->timer, fc_lun_reset_send, (unsigned long)fsp);
- fc_fcp_timer_set(fsp, rec_tov);
+ fc_fcp_timer_set(fsp, get_fsp_rec_tov(fsp));
fc_fcp_unlock_pkt(fsp);
}
}
@@ -1536,12 +1530,11 @@ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
}
fc_fcp_srr(fsp, r_ctl, offset);
} else if (e_stat & ESB_ST_SEQ_INIT) {
- unsigned int rec_tov = get_fsp_rec_tov(fsp);
/*
* The remote port has the initiative, so just
* keep waiting for it to complete.
*/
- fc_fcp_timer_set(fsp, rec_tov);
+ fc_fcp_timer_set(fsp, get_fsp_rec_tov(fsp));
} else {
/*
@@ -1705,7 +1698,6 @@ static void fc_fcp_srr_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
{
struct fc_fcp_pkt *fsp = arg;
struct fc_frame_header *fh;
- unsigned int rec_tov;
if (IS_ERR(fp)) {
fc_fcp_srr_error(fsp, fp);
@@ -1732,8 +1724,7 @@ static void fc_fcp_srr_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
switch (fc_frame_payload_op(fp)) {
case ELS_LS_ACC:
fsp->recov_retry = 0;
- rec_tov = get_fsp_rec_tov(fsp);
- fc_fcp_timer_set(fsp, rec_tov);
+ fc_fcp_timer_set(fsp, get_fsp_rec_tov(fsp));
break;
case ELS_LS_RJT:
default:
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index 906bbcad0e2..389ab80aef0 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -1590,7 +1590,6 @@ void fc_lport_enter_flogi(struct fc_lport *lport)
*/
int fc_lport_config(struct fc_lport *lport)
{
- INIT_LIST_HEAD(&lport->ema_list);
INIT_DELAYED_WORK(&lport->retry_work, fc_lport_timeout);
mutex_init(&lport->lp_mutex);
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 60e98a62f30..02d53d89534 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -805,6 +805,8 @@ struct lpfc_hba {
struct dentry *idiag_root;
struct dentry *idiag_pci_cfg;
struct dentry *idiag_que_info;
+ struct dentry *idiag_que_acc;
+ struct dentry *idiag_drb_acc;
#endif
/* Used for deferred freeing of ELS data buffers */
diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c
index 77b2871d96b..37e2a1272f8 100644
--- a/drivers/scsi/lpfc/lpfc_bsg.c
+++ b/drivers/scsi/lpfc/lpfc_bsg.c
@@ -2426,6 +2426,7 @@ lpfc_bsg_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
{
struct bsg_job_data *dd_data;
struct fc_bsg_job *job;
+ struct lpfc_mbx_nembed_cmd *nembed_sge;
uint32_t size;
unsigned long flags;
uint8_t *to;
@@ -2469,9 +2470,8 @@ lpfc_bsg_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
memcpy(to, from, size);
} else if ((phba->sli_rev == LPFC_SLI_REV4) &&
(pmboxq->u.mb.mbxCommand == MBX_SLI4_CONFIG)) {
- struct lpfc_mbx_nembed_cmd *nembed_sge =
- (struct lpfc_mbx_nembed_cmd *)
- &pmboxq->u.mb.un.varWords[0];
+ nembed_sge = (struct lpfc_mbx_nembed_cmd *)
+ &pmboxq->u.mb.un.varWords[0];
from = (uint8_t *)dd_data->context_un.mbox.dmp->dma.
virt;
@@ -2496,16 +2496,18 @@ lpfc_bsg_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
job->reply_payload.sg_cnt,
from, size);
job->reply->result = 0;
-
+ /* need to hold the lock until we set job->dd_data to NULL
+ * to hold off the timeout handler returning to the mid-layer
+ * while we are still processing the job.
+ */
job->dd_data = NULL;
+ dd_data->context_un.mbox.set_job = NULL;
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
job->job_done(job);
+ } else {
+ dd_data->context_un.mbox.set_job = NULL;
+ spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
}
- dd_data->context_un.mbox.set_job = NULL;
- /* need to hold the lock until we call job done to hold off
- * the timeout handler returning to the midlayer while
- * we are stillprocessing the job
- */
- spin_unlock_irqrestore(&phba->ct_ev_lock, flags);
kfree(dd_data->context_un.mbox.mb);
mempool_free(dd_data->context_un.mbox.pmboxq, phba->mbox_mem_pool);
@@ -2644,6 +2646,11 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
struct ulp_bde64 *rxbpl = NULL;
struct dfc_mbox_req *mbox_req = (struct dfc_mbox_req *)
job->request->rqst_data.h_vendor.vendor_cmd;
+ struct READ_EVENT_LOG_VAR *rdEventLog;
+ uint32_t transmit_length, receive_length, mode;
+ struct lpfc_mbx_nembed_cmd *nembed_sge;
+ struct mbox_header *header;
+ struct ulp_bde64 *bde;
uint8_t *ext = NULL;
int rc = 0;
uint8_t *from;
@@ -2651,9 +2658,16 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
/* in case no data is transferred */
job->reply->reply_payload_rcv_len = 0;
+ /* sanity check to protect driver */
+ if (job->reply_payload.payload_len > BSG_MBOX_SIZE ||
+ job->request_payload.payload_len > BSG_MBOX_SIZE) {
+ rc = -ERANGE;
+ goto job_done;
+ }
+
/* check if requested extended data lengths are valid */
- if ((mbox_req->inExtWLen > MAILBOX_EXT_SIZE) ||
- (mbox_req->outExtWLen > MAILBOX_EXT_SIZE)) {
+ if ((mbox_req->inExtWLen > BSG_MBOX_SIZE/sizeof(uint32_t)) ||
+ (mbox_req->outExtWLen > BSG_MBOX_SIZE/sizeof(uint32_t))) {
rc = -ERANGE;
goto job_done;
}
@@ -2744,8 +2758,8 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
* use ours
*/
if (pmb->mbxCommand == MBX_RUN_BIU_DIAG64) {
- uint32_t transmit_length = pmb->un.varWords[1];
- uint32_t receive_length = pmb->un.varWords[4];
+ transmit_length = pmb->un.varWords[1];
+ receive_length = pmb->un.varWords[4];
/* transmit length cannot be greater than receive length or
* mailbox extension size
*/
@@ -2795,10 +2809,9 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
from += sizeof(MAILBOX_t);
memcpy((uint8_t *)dmp->dma.virt, from, transmit_length);
} else if (pmb->mbxCommand == MBX_READ_EVENT_LOG) {
- struct READ_EVENT_LOG_VAR *rdEventLog =
- &pmb->un.varRdEventLog ;
- uint32_t receive_length = rdEventLog->rcv_bde64.tus.f.bdeSize;
- uint32_t mode = bf_get(lpfc_event_log, rdEventLog);
+ rdEventLog = &pmb->un.varRdEventLog;
+ receive_length = rdEventLog->rcv_bde64.tus.f.bdeSize;
+ mode = bf_get(lpfc_event_log, rdEventLog);
/* receive length cannot be greater than mailbox
* extension size
@@ -2843,7 +2856,7 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
/* rebuild the command for sli4 using our own buffers
* like we do for biu diags
*/
- uint32_t receive_length = pmb->un.varWords[2];
+ receive_length = pmb->un.varWords[2];
/* receive length cannot be greater than mailbox
* extension size
*/
@@ -2879,8 +2892,7 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
pmb->un.varWords[4] = putPaddrHigh(dmp->dma.phys);
} else if ((pmb->mbxCommand == MBX_UPDATE_CFG) &&
pmb->un.varUpdateCfg.co) {
- struct ulp_bde64 *bde =
- (struct ulp_bde64 *)&pmb->un.varWords[4];
+ bde = (struct ulp_bde64 *)&pmb->un.varWords[4];
/* bde size cannot be greater than mailbox ext size */
if (bde->tus.f.bdeSize > MAILBOX_EXT_SIZE) {
@@ -2921,10 +2933,6 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
memcpy((uint8_t *)dmp->dma.virt, from,
bde->tus.f.bdeSize);
} else if (pmb->mbxCommand == MBX_SLI4_CONFIG) {
- struct lpfc_mbx_nembed_cmd *nembed_sge;
- struct mbox_header *header;
- uint32_t receive_length;
-
/* rebuild the command for sli4 using our own buffers
* like we do for biu diags
*/
@@ -3386,6 +3394,7 @@ no_dd_data:
job->dd_data = NULL;
return rc;
}
+
/**
* lpfc_bsg_hst_vendor - process a vendor-specific fc_bsg_job
* @job: fc_bsg_job to handle
diff --git a/drivers/scsi/lpfc/lpfc_bsg.h b/drivers/scsi/lpfc/lpfc_bsg.h
index a2c33e7c915..b542aca6f5a 100644
--- a/drivers/scsi/lpfc/lpfc_bsg.h
+++ b/drivers/scsi/lpfc/lpfc_bsg.h
@@ -109,3 +109,133 @@ struct menlo_response {
uint32_t xri; /* return the xri of the iocb exchange */
};
+/*
+ * macros and data structures for handling sli-config mailbox command
+ * pass-through support, this header file is shared between user and
+ * kernel spaces, note the set of macros are duplicates from lpfc_hw4.h,
+ * with macro names prefixed with bsg_, as the macros defined in
+ * lpfc_hw4.h are not accessible from user space.
+ */
+
+/* Macros to deal with bit fields. Each bit field must have 3 #defines
+ * associated with it (_SHIFT, _MASK, and _WORD).
+ * EG. For a bit field that is in the 7th bit of the "field4" field of a
+ * structure and is 2 bits in size the following #defines must exist:
+ * struct temp {
+ * uint32_t field1;
+ * uint32_t field2;
+ * uint32_t field3;
+ * uint32_t field4;
+ * #define example_bit_field_SHIFT 7
+ * #define example_bit_field_MASK 0x03
+ * #define example_bit_field_WORD field4
+ * uint32_t field5;
+ * };
+ * Then the macros below may be used to get or set the value of that field.
+ * EG. To get the value of the bit field from the above example:
+ * struct temp t1;
+ * value = bsg_bf_get(example_bit_field, &t1);
+ * And then to set that bit field:
+ * bsg_bf_set(example_bit_field, &t1, 2);
+ * Or clear that bit field:
+ * bsg_bf_set(example_bit_field, &t1, 0);
+ */
+#define bsg_bf_get_le32(name, ptr) \
+ ((le32_to_cpu((ptr)->name##_WORD) >> name##_SHIFT) & name##_MASK)
+#define bsg_bf_get(name, ptr) \
+ (((ptr)->name##_WORD >> name##_SHIFT) & name##_MASK)
+#define bsg_bf_set_le32(name, ptr, value) \
+ ((ptr)->name##_WORD = cpu_to_le32(((((value) & \
+ name##_MASK) << name##_SHIFT) | (le32_to_cpu((ptr)->name##_WORD) & \
+ ~(name##_MASK << name##_SHIFT)))))
+#define bsg_bf_set(name, ptr, value) \
+ ((ptr)->name##_WORD = ((((value) & name##_MASK) << name##_SHIFT) | \
+ ((ptr)->name##_WORD & ~(name##_MASK << name##_SHIFT))))
+
+/*
+ * The sli_config structure specified here is based on the following
+ * restriction:
+ *
+ * -- SLI_CONFIG EMB=0, carrying MSEs, will carry subcommands without
+ * carrying HBD.
+ * -- SLI_CONFIG EMB=1, not carrying MSE, will carry subcommands with or
+ * without carrying HBDs.
+ */
+
+struct lpfc_sli_config_mse {
+ uint32_t pa_lo;
+ uint32_t pa_hi;
+ uint32_t buf_len;
+#define lpfc_mbox_sli_config_mse_len_SHIFT 0
+#define lpfc_mbox_sli_config_mse_len_MASK 0xffffff
+#define lpfc_mbox_sli_config_mse_len_WORD buf_len
+};
+
+struct lpfc_sli_config_subcmd_hbd {
+ uint32_t buf_len;
+#define lpfc_mbox_sli_config_ecmn_hbd_len_SHIFT 0
+#define lpfc_mbox_sli_config_ecmn_hbd_len_MASK 0xffffff
+#define lpfc_mbox_sli_config_ecmn_hbd_len_WORD buf_len
+ uint32_t pa_lo;
+ uint32_t pa_hi;
+};
+
+struct lpfc_sli_config_hdr {
+ uint32_t word1;
+#define lpfc_mbox_hdr_emb_SHIFT 0
+#define lpfc_mbox_hdr_emb_MASK 0x00000001
+#define lpfc_mbox_hdr_emb_WORD word1
+#define lpfc_mbox_hdr_mse_cnt_SHIFT 3
+#define lpfc_mbox_hdr_mse_cnt_MASK 0x0000001f
+#define lpfc_mbox_hdr_mse_cnt_WORD word1
+ uint32_t payload_length;
+ uint32_t tag_lo;
+ uint32_t tag_hi;
+ uint32_t reserved5;
+};
+
+struct lpfc_sli_config_generic {
+ struct lpfc_sli_config_hdr sli_config_hdr;
+#define LPFC_MBX_SLI_CONFIG_MAX_MSE 19
+ struct lpfc_sli_config_mse mse[LPFC_MBX_SLI_CONFIG_MAX_MSE];
+};
+
+struct lpfc_sli_config_subcmnd {
+ struct lpfc_sli_config_hdr sli_config_hdr;
+ uint32_t word6;
+#define lpfc_subcmnd_opcode_SHIFT 0
+#define lpfc_subcmnd_opcode_MASK 0xff
+#define lpfc_subcmnd_opcode_WORD word6
+#define lpfc_subcmnd_subsys_SHIFT 8
+#define lpfc_subcmnd_subsys_MASK 0xff
+#define lpfc_subcmnd_subsys_WORD word6
+ uint32_t timeout;
+ uint32_t request_length;
+ uint32_t word9;
+#define lpfc_subcmnd_version_SHIFT 0
+#define lpfc_subcmnd_version_MASK 0xff
+#define lpfc_subcmnd_version_WORD word9
+ uint32_t word10;
+#define lpfc_subcmnd_ask_rd_len_SHIFT 0
+#define lpfc_subcmnd_ask_rd_len_MASK 0xffffff
+#define lpfc_subcmnd_ask_rd_len_WORD word10
+ uint32_t rd_offset;
+ uint32_t obj_name[26];
+ uint32_t hbd_count;
+#define LPFC_MBX_SLI_CONFIG_MAX_HBD 10
+ struct lpfc_sli_config_subcmd_hbd hbd[LPFC_MBX_SLI_CONFIG_MAX_HBD];
+};
+
+struct lpfc_sli_config_mbox {
+ uint32_t word0;
+#define lpfc_mqe_status_SHIFT 16
+#define lpfc_mqe_status_MASK 0x0000FFFF
+#define lpfc_mqe_status_WORD word0
+#define lpfc_mqe_command_SHIFT 8
+#define lpfc_mqe_command_MASK 0x000000FF
+#define lpfc_mqe_command_WORD word0
+ union {
+ struct lpfc_sli_config_generic sli_config_generic;
+ struct lpfc_sli_config_subcmnd sli_config_subcmnd;
+ } un;
+};
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c
index 3d967741c70..c93fca05860 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.c
+++ b/drivers/scsi/lpfc/lpfc_debugfs.c
@@ -1119,172 +1119,14 @@ lpfc_debugfs_dumpDataDif_release(struct inode *inode, struct file *file)
}
/*
+ * ---------------------------------
* iDiag debugfs file access methods
- */
-
-/*
- * iDiag PCI config space register access methods:
- *
- * The PCI config space register accessees of read, write, read-modify-write
- * for set bits, and read-modify-write for clear bits to SLI4 PCI functions
- * are provided. In the proper SLI4 PCI function's debugfs iDiag directory,
- *
- * /sys/kernel/debug/lpfc/fn<#>/iDiag
- *
- * the access is through the debugfs entry pciCfg:
- *
- * 1. For PCI config space register read access, there are two read methods:
- * A) read a single PCI config space register in the size of a byte
- * (8 bits), a word (16 bits), or a dword (32 bits); or B) browse through
- * the 4K extended PCI config space.
- *
- * A) Read a single PCI config space register consists of two steps:
- *
- * Step-1: Set up PCI config space register read command, the command
- * syntax is,
- *
- * echo 1 <where> <count> > pciCfg
- *
- * where, 1 is the iDiag command for PCI config space read, <where> is the
- * offset from the beginning of the device's PCI config space to read from,
- * and <count> is the size of PCI config space register data to read back,
- * it will be 1 for reading a byte (8 bits), 2 for reading a word (16 bits
- * or 2 bytes), or 4 for reading a dword (32 bits or 4 bytes).
- *
- * Setp-2: Perform the debugfs read operation to execute the idiag command
- * set up in Step-1,
- *
- * cat pciCfg
- *
- * Examples:
- * To read PCI device's vendor-id and device-id from PCI config space,
- *
- * echo 1 0 4 > pciCfg
- * cat pciCfg
- *
- * To read PCI device's currnt command from config space,
- *
- * echo 1 4 2 > pciCfg
- * cat pciCfg
- *
- * B) Browse through the entire 4K extended PCI config space also consists
- * of two steps:
- *
- * Step-1: Set up PCI config space register browsing command, the command
- * syntax is,
- *
- * echo 1 0 4096 > pciCfg
- *
- * where, 1 is the iDiag command for PCI config space read, 0 must be used
- * as the offset for PCI config space register browse, and 4096 must be
- * used as the count for PCI config space register browse.
- *
- * Step-2: Repeately issue the debugfs read operation to browse through
- * the entire PCI config space registers:
- *
- * cat pciCfg
- * cat pciCfg
- * cat pciCfg
- * ...
- *
- * When browsing to the end of the 4K PCI config space, the browse method
- * shall wrap around to start reading from beginning again, and again...
- *
- * 2. For PCI config space register write access, it supports a single PCI
- * config space register write in the size of a byte (8 bits), a word
- * (16 bits), or a dword (32 bits). The command syntax is,
- *
- * echo 2 <where> <count> <value> > pciCfg
- *
- * where, 2 is the iDiag command for PCI config space write, <where> is
- * the offset from the beginning of the device's PCI config space to write
- * into, <count> is the size of data to write into the PCI config space,
- * it will be 1 for writing a byte (8 bits), 2 for writing a word (16 bits
- * or 2 bytes), or 4 for writing a dword (32 bits or 4 bytes), and <value>
- * is the data to be written into the PCI config space register at the
- * offset.
- *
- * Examples:
- * To disable PCI device's interrupt assertion,
- *
- * 1) Read in device's PCI config space register command field <cmd>:
- *
- * echo 1 4 2 > pciCfg
- * cat pciCfg
- *
- * 2) Set bit 10 (Interrupt Disable bit) in the <cmd>:
- *
- * <cmd> = <cmd> | (1 < 10)
- *
- * 3) Write the modified command back:
- *
- * echo 2 4 2 <cmd> > pciCfg
- *
- * 3. For PCI config space register set bits access, it supports a single PCI
- * config space register set bits in the size of a byte (8 bits), a word
- * (16 bits), or a dword (32 bits). The command syntax is,
- *
- * echo 3 <where> <count> <bitmask> > pciCfg
- *
- * where, 3 is the iDiag command for PCI config space set bits, <where> is
- * the offset from the beginning of the device's PCI config space to set
- * bits into, <count> is the size of the bitmask to set into the PCI config
- * space, it will be 1 for setting a byte (8 bits), 2 for setting a word
- * (16 bits or 2 bytes), or 4 for setting a dword (32 bits or 4 bytes), and
- * <bitmask> is the bitmask, indicating the bits to be set into the PCI
- * config space register at the offset. The logic performed to the content
- * of the PCI config space register, regval, is,
- *
- * regval |= <bitmask>
- *
- * 4. For PCI config space register clear bits access, it supports a single
- * PCI config space register clear bits in the size of a byte (8 bits),
- * a word (16 bits), or a dword (32 bits). The command syntax is,
- *
- * echo 4 <where> <count> <bitmask> > pciCfg
- *
- * where, 4 is the iDiag command for PCI config space clear bits, <where>
- * is the offset from the beginning of the device's PCI config space to
- * clear bits from, <count> is the size of the bitmask to set into the PCI
- * config space, it will be 1 for setting a byte (8 bits), 2 for setting
- * a word(16 bits or 2 bytes), or 4 for setting a dword (32 bits or 4
- * bytes), and <bitmask> is the bitmask, indicating the bits to be cleared
- * from the PCI config space register at the offset. the logic performed
- * to the content of the PCI config space register, regval, is,
- *
- * regval &= ~<bitmask>
- *
- * Note, for all single register read, write, set bits, or clear bits access,
- * the offset (<where>) must be aligned with the size of the data:
- *
- * For data size of byte (8 bits), the offset must be aligned to the byte
- * boundary; for data size of word (16 bits), the offset must be aligned
- * to the word boundary; while for data size of dword (32 bits), the offset
- * must be aligned to the dword boundary. Otherwise, the interface will
- * return the error:
+ * ---------------------------------
*
- * "-bash: echo: write error: Invalid argument".
+ * All access methods are through the proper SLI4 PCI function's debugfs
+ * iDiag directory:
*
- * For example:
- *
- * echo 1 2 4 > pciCfg
- * -bash: echo: write error: Invalid argument
- *
- * Note also, all of the numbers in the command fields for all read, write,
- * set bits, and clear bits PCI config space register command fields can be
- * either decimal or hex.
- *
- * For example,
- * echo 1 0 4096 > pciCfg
- *
- * will be the same as
- * echo 1 0 0x1000 > pciCfg
- *
- * And,
- * echo 2 155 1 10 > pciCfg
- *
- * will be
- * echo 2 0x9b 1 0xa > pciCfg
+ * /sys/kernel/debug/lpfc/fn<#>/iDiag
*/
/**
@@ -1331,10 +1173,10 @@ static int lpfc_idiag_cmd_get(const char __user *buf, size_t nbytes,
for (i = 0; i < LPFC_IDIAG_CMD_DATA_SIZE; i++) {
step_str = strsep(&pbuf, "\t ");
if (!step_str)
- return 0;
+ return i;
idiag_cmd->data[i] = simple_strtol(step_str, NULL, 0);
}
- return 0;
+ return i;
}
/**
@@ -1403,7 +1245,7 @@ lpfc_idiag_release(struct inode *inode, struct file *file)
* Description:
* This routine frees the buffer that was allocated when the debugfs file
* was opened. It also reset the fields in the idiag command struct in the
- * case the command is not continuous browsing of the data structure.
+ * case of command for write operation.
*
* Returns:
* This function returns zero.
@@ -1413,18 +1255,20 @@ lpfc_idiag_cmd_release(struct inode *inode, struct file *file)
{
struct lpfc_debug *debug = file->private_data;
- /* Read PCI config register, if not read all, clear command fields */
- if ((debug->op == LPFC_IDIAG_OP_RD) &&
- (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_RD))
- if ((idiag.cmd.data[1] == sizeof(uint8_t)) ||
- (idiag.cmd.data[1] == sizeof(uint16_t)) ||
- (idiag.cmd.data[1] == sizeof(uint32_t)))
+ if (debug->op == LPFC_IDIAG_OP_WR) {
+ switch (idiag.cmd.opcode) {
+ case LPFC_IDIAG_CMD_PCICFG_WR:
+ case LPFC_IDIAG_CMD_PCICFG_ST:
+ case LPFC_IDIAG_CMD_PCICFG_CL:
+ case LPFC_IDIAG_CMD_QUEACC_WR:
+ case LPFC_IDIAG_CMD_QUEACC_ST:
+ case LPFC_IDIAG_CMD_QUEACC_CL:
memset(&idiag, 0, sizeof(idiag));
-
- /* Write PCI config register, clear command fields */
- if ((debug->op == LPFC_IDIAG_OP_WR) &&
- (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_WR))
- memset(&idiag, 0, sizeof(idiag));
+ break;
+ default:
+ break;
+ }
+ }
/* Free the buffers to the file operation */
kfree(debug->buffer);
@@ -1504,7 +1348,7 @@ lpfc_idiag_pcicfg_read(struct file *file, char __user *buf, size_t nbytes,
len += snprintf(pbuffer+len, LPFC_PCI_CFG_SIZE-len,
"%03x: %08x\n", where, u32val);
break;
- case LPFC_PCI_CFG_SIZE: /* browse all */
+ case LPFC_PCI_CFG_BROWSE: /* browse all */
goto pcicfg_browse;
break;
default:
@@ -1586,16 +1430,21 @@ lpfc_idiag_pcicfg_write(struct file *file, const char __user *buf,
debug->op = LPFC_IDIAG_OP_WR;
rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
- if (rc)
+ if (rc < 0)
return rc;
if (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_RD) {
+ /* Sanity check on PCI config read command line arguments */
+ if (rc != LPFC_PCI_CFG_RD_CMD_ARG)
+ goto error_out;
/* Read command from PCI config space, set up command fields */
where = idiag.cmd.data[0];
count = idiag.cmd.data[1];
- if (count == LPFC_PCI_CFG_SIZE) {
- if (where != 0)
+ if (count == LPFC_PCI_CFG_BROWSE) {
+ if (where % sizeof(uint32_t))
goto error_out;
+ /* Starting offset to browse */
+ idiag.offset.last_rd = where;
} else if ((count != sizeof(uint8_t)) &&
(count != sizeof(uint16_t)) &&
(count != sizeof(uint32_t)))
@@ -1621,6 +1470,9 @@ lpfc_idiag_pcicfg_write(struct file *file, const char __user *buf,
} else if (idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_WR ||
idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_ST ||
idiag.cmd.opcode == LPFC_IDIAG_CMD_PCICFG_CL) {
+ /* Sanity check on PCI config write command line arguments */
+ if (rc != LPFC_PCI_CFG_WR_CMD_ARG)
+ goto error_out;
/* Write command to PCI config space, read-modify-write */
where = idiag.cmd.data[0];
count = idiag.cmd.data[1];
@@ -1753,10 +1605,12 @@ lpfc_idiag_queinfo_read(struct file *file, char __user *buf, size_t nbytes,
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
"Slow-path EQ information:\n");
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tID [%02d], EQE-COUNT [%04d], "
- "HOST-INDEX [%04x], PORT-INDEX [%04x]\n\n",
+ "\tEQID[%02d], "
+ "QE-COUNT[%04d], QE-SIZE[%04d], "
+ "HOST-INDEX[%04d], PORT-INDEX[%04d]\n\n",
phba->sli4_hba.sp_eq->queue_id,
phba->sli4_hba.sp_eq->entry_count,
+ phba->sli4_hba.sp_eq->entry_size,
phba->sli4_hba.sp_eq->host_index,
phba->sli4_hba.sp_eq->hba_index);
@@ -1765,10 +1619,12 @@ lpfc_idiag_queinfo_read(struct file *file, char __user *buf, size_t nbytes,
"Fast-path EQ information:\n");
for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_eq_count; fcp_qidx++) {
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tID [%02d], EQE-COUNT [%04d], "
- "HOST-INDEX [%04x], PORT-INDEX [%04x]\n",
+ "\tEQID[%02d], "
+ "QE-COUNT[%04d], QE-SIZE[%04d], "
+ "HOST-INDEX[%04d], PORT-INDEX[%04d]\n",
phba->sli4_hba.fp_eq[fcp_qidx]->queue_id,
phba->sli4_hba.fp_eq[fcp_qidx]->entry_count,
+ phba->sli4_hba.fp_eq[fcp_qidx]->entry_size,
phba->sli4_hba.fp_eq[fcp_qidx]->host_index,
phba->sli4_hba.fp_eq[fcp_qidx]->hba_index);
}
@@ -1776,89 +1632,101 @@ lpfc_idiag_queinfo_read(struct file *file, char __user *buf, size_t nbytes,
/* Get mailbox complete queue information */
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Mailbox CQ information:\n");
+ "Slow-path MBX CQ information:\n");
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\t\tAssociated EQ-ID [%02d]:\n",
+ "Associated EQID[%02d]:\n",
phba->sli4_hba.mbx_cq->assoc_qid);
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tID [%02d], CQE-COUNT [%04d], "
- "HOST-INDEX [%04x], PORT-INDEX [%04x]\n\n",
+ "\tCQID[%02d], "
+ "QE-COUNT[%04d], QE-SIZE[%04d], "
+ "HOST-INDEX[%04d], PORT-INDEX[%04d]\n\n",
phba->sli4_hba.mbx_cq->queue_id,
phba->sli4_hba.mbx_cq->entry_count,
+ phba->sli4_hba.mbx_cq->entry_size,
phba->sli4_hba.mbx_cq->host_index,
phba->sli4_hba.mbx_cq->hba_index);
/* Get slow-path complete queue information */
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Slow-path CQ information:\n");
+ "Slow-path ELS CQ information:\n");
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\t\tAssociated EQ-ID [%02d]:\n",
+ "Associated EQID[%02d]:\n",
phba->sli4_hba.els_cq->assoc_qid);
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tID [%02d], CQE-COUNT [%04d], "
- "HOST-INDEX [%04x], PORT-INDEX [%04x]\n\n",
+ "\tCQID [%02d], "
+ "QE-COUNT[%04d], QE-SIZE[%04d], "
+ "HOST-INDEX[%04d], PORT-INDEX[%04d]\n\n",
phba->sli4_hba.els_cq->queue_id,
phba->sli4_hba.els_cq->entry_count,
+ phba->sli4_hba.els_cq->entry_size,
phba->sli4_hba.els_cq->host_index,
phba->sli4_hba.els_cq->hba_index);
/* Get fast-path complete queue information */
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Fast-path CQ information:\n");
+ "Fast-path FCP CQ information:\n");
for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_eq_count; fcp_qidx++) {
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\t\tAssociated EQ-ID [%02d]:\n",
+ "Associated EQID[%02d]:\n",
phba->sli4_hba.fcp_cq[fcp_qidx]->assoc_qid);
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tID [%02d], EQE-COUNT [%04d], "
- "HOST-INDEX [%04x], PORT-INDEX [%04x]\n",
- phba->sli4_hba.fcp_cq[fcp_qidx]->queue_id,
- phba->sli4_hba.fcp_cq[fcp_qidx]->entry_count,
- phba->sli4_hba.fcp_cq[fcp_qidx]->host_index,
- phba->sli4_hba.fcp_cq[fcp_qidx]->hba_index);
+ "\tCQID[%02d], "
+ "QE-COUNT[%04d], QE-SIZE[%04d], "
+ "HOST-INDEX[%04d], PORT-INDEX[%04d]\n",
+ phba->sli4_hba.fcp_cq[fcp_qidx]->queue_id,
+ phba->sli4_hba.fcp_cq[fcp_qidx]->entry_count,
+ phba->sli4_hba.fcp_cq[fcp_qidx]->entry_size,
+ phba->sli4_hba.fcp_cq[fcp_qidx]->host_index,
+ phba->sli4_hba.fcp_cq[fcp_qidx]->hba_index);
}
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len, "\n");
/* Get mailbox queue information */
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Mailbox MQ information:\n");
+ "Slow-path MBX MQ information:\n");
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\t\tAssociated CQ-ID [%02d]:\n",
+ "Associated CQID[%02d]:\n",
phba->sli4_hba.mbx_wq->assoc_qid);
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tID [%02d], MQE-COUNT [%04d], "
- "HOST-INDEX [%04x], PORT-INDEX [%04x]\n\n",
+ "\tWQID[%02d], "
+ "QE-COUNT[%04d], QE-SIZE[%04d], "
+ "HOST-INDEX[%04d], PORT-INDEX[%04d]\n\n",
phba->sli4_hba.mbx_wq->queue_id,
phba->sli4_hba.mbx_wq->entry_count,
+ phba->sli4_hba.mbx_wq->entry_size,
phba->sli4_hba.mbx_wq->host_index,
phba->sli4_hba.mbx_wq->hba_index);
/* Get slow-path work queue information */
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Slow-path WQ information:\n");
+ "Slow-path ELS WQ information:\n");
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\t\tAssociated CQ-ID [%02d]:\n",
+ "Associated CQID[%02d]:\n",
phba->sli4_hba.els_wq->assoc_qid);
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tID [%02d], WQE-COUNT [%04d], "
- "HOST-INDEX [%04x], PORT-INDEX [%04x]\n\n",
+ "\tWQID[%02d], "
+ "QE-COUNT[%04d], QE-SIZE[%04d], "
+ "HOST-INDEX[%04d], PORT-INDEX[%04d]\n\n",
phba->sli4_hba.els_wq->queue_id,
phba->sli4_hba.els_wq->entry_count,
+ phba->sli4_hba.els_wq->entry_size,
phba->sli4_hba.els_wq->host_index,
phba->sli4_hba.els_wq->hba_index);
/* Get fast-path work queue information */
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "Fast-path WQ information:\n");
+ "Fast-path FCP WQ information:\n");
for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_wq_count; fcp_qidx++) {
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\t\tAssociated CQ-ID [%02d]:\n",
+ "Associated CQID[%02d]:\n",
phba->sli4_hba.fcp_wq[fcp_qidx]->assoc_qid);
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tID [%02d], WQE-COUNT [%04d], "
- "HOST-INDEX [%04x], PORT-INDEX [%04x]\n",
+ "\tWQID[%02d], "
+ "QE-COUNT[%04d], WQE-SIZE[%04d], "
+ "HOST-INDEX[%04d], PORT-INDEX[%04d]\n",
phba->sli4_hba.fcp_wq[fcp_qidx]->queue_id,
phba->sli4_hba.fcp_wq[fcp_qidx]->entry_count,
+ phba->sli4_hba.fcp_wq[fcp_qidx]->entry_size,
phba->sli4_hba.fcp_wq[fcp_qidx]->host_index,
phba->sli4_hba.fcp_wq[fcp_qidx]->hba_index);
}
@@ -1868,26 +1736,597 @@ lpfc_idiag_queinfo_read(struct file *file, char __user *buf, size_t nbytes,
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
"Slow-path RQ information:\n");
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\t\tAssociated CQ-ID [%02d]:\n",
+ "Associated CQID[%02d]:\n",
phba->sli4_hba.hdr_rq->assoc_qid);
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tID [%02d], RHQE-COUNT [%04d], "
- "HOST-INDEX [%04x], PORT-INDEX [%04x]\n",
+ "\tHQID[%02d], "
+ "QE-COUNT[%04d], QE-SIZE[%04d], "
+ "HOST-INDEX[%04d], PORT-INDEX[%04d]\n",
phba->sli4_hba.hdr_rq->queue_id,
phba->sli4_hba.hdr_rq->entry_count,
+ phba->sli4_hba.hdr_rq->entry_size,
phba->sli4_hba.hdr_rq->host_index,
phba->sli4_hba.hdr_rq->hba_index);
len += snprintf(pbuffer+len, LPFC_QUE_INFO_GET_BUF_SIZE-len,
- "\tID [%02d], RDQE-COUNT [%04d], "
- "HOST-INDEX [%04x], PORT-INDEX [%04x]\n",
+ "\tDQID[%02d], "
+ "QE-COUNT[%04d], QE-SIZE[%04d], "
+ "HOST-INDEX[%04d], PORT-INDEX[%04d]\n",
phba->sli4_hba.dat_rq->queue_id,
phba->sli4_hba.dat_rq->entry_count,
+ phba->sli4_hba.dat_rq->entry_size,
phba->sli4_hba.dat_rq->host_index,
phba->sli4_hba.dat_rq->hba_index);
return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
}
+/**
+ * lpfc_idiag_que_param_check - queue access command parameter sanity check
+ * @q: The pointer to queue structure.
+ * @index: The index into a queue entry.
+ * @count: The number of queue entries to access.
+ *
+ * Description:
+ * The routine performs sanity check on device queue access method commands.
+ *
+ * Returns:
+ * This function returns -EINVAL when fails the sanity check, otherwise, it
+ * returns 0.
+ **/
+static int
+lpfc_idiag_que_param_check(struct lpfc_queue *q, int index, int count)
+{
+ /* Only support single entry read or browsing */
+ if ((count != 1) && (count != LPFC_QUE_ACC_BROWSE))
+ return -EINVAL;
+ if (index > q->entry_count - 1)
+ return -EINVAL;
+ return 0;
+}
+
+/**
+ * lpfc_idiag_queacc_read_qe - read a single entry from the given queue index
+ * @pbuffer: The pointer to buffer to copy the read data into.
+ * @pque: The pointer to the queue to be read.
+ * @index: The index into the queue entry.
+ *
+ * Description:
+ * This routine reads out a single entry from the given queue's index location
+ * and copies it into the buffer provided.
+ *
+ * Returns:
+ * This function returns 0 when it fails, otherwise, it returns the length of
+ * the data read into the buffer provided.
+ **/
+static int
+lpfc_idiag_queacc_read_qe(char *pbuffer, int len, struct lpfc_queue *pque,
+ uint32_t index)
+{
+ int offset, esize;
+ uint32_t *pentry;
+
+ if (!pbuffer || !pque)
+ return 0;
+
+ esize = pque->entry_size;
+ len += snprintf(pbuffer+len, LPFC_QUE_ACC_BUF_SIZE-len,
+ "QE-INDEX[%04d]:\n", index);
+
+ offset = 0;
+ pentry = pque->qe[index].address;
+ while (esize > 0) {
+ len += snprintf(pbuffer+len, LPFC_QUE_ACC_BUF_SIZE-len,
+ "%08x ", *pentry);
+ pentry++;
+ offset += sizeof(uint32_t);
+ esize -= sizeof(uint32_t);
+ if (esize > 0 && !(offset % (4 * sizeof(uint32_t))))
+ len += snprintf(pbuffer+len,
+ LPFC_QUE_ACC_BUF_SIZE-len, "\n");
+ }
+ len += snprintf(pbuffer+len, LPFC_QUE_ACC_BUF_SIZE-len, "\n");
+
+ return len;
+}
+
+/**
+ * lpfc_idiag_queacc_read - idiag debugfs read port queue
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the data to.
+ * @nbytes: The number of bytes to read.
+ * @ppos: The position in the file to start reading from.
+ *
+ * Description:
+ * This routine reads data from the @phba device queue memory according to the
+ * idiag command, and copies to user @buf. Depending on the queue dump read
+ * command setup, it does either a single queue entry read or browing through
+ * all entries of the queue.
+ *
+ * Returns:
+ * This function returns the amount of data that was read (this could be less
+ * than @nbytes if the end of the file was reached) or a negative error value.
+ **/
+static ssize_t
+lpfc_idiag_queacc_read(struct file *file, char __user *buf, size_t nbytes,
+ loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ uint32_t last_index, index, count;
+ struct lpfc_queue *pque = NULL;
+ char *pbuffer;
+ int len = 0;
+
+ /* This is a user read operation */
+ debug->op = LPFC_IDIAG_OP_RD;
+
+ if (!debug->buffer)
+ debug->buffer = kmalloc(LPFC_QUE_ACC_BUF_SIZE, GFP_KERNEL);
+ if (!debug->buffer)
+ return 0;
+ pbuffer = debug->buffer;
+
+ if (*ppos)
+ return 0;
+
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_RD) {
+ index = idiag.cmd.data[2];
+ count = idiag.cmd.data[3];
+ pque = (struct lpfc_queue *)idiag.ptr_private;
+ } else
+ return 0;
+
+ /* Browse the queue starting from index */
+ if (count == LPFC_QUE_ACC_BROWSE)
+ goto que_browse;
+
+ /* Read a single entry from the queue */
+ len = lpfc_idiag_queacc_read_qe(pbuffer, len, pque, index);
+
+ return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
+
+que_browse:
+
+ /* Browse all entries from the queue */
+ last_index = idiag.offset.last_rd;
+ index = last_index;
+
+ while (len < LPFC_QUE_ACC_SIZE - pque->entry_size) {
+ len = lpfc_idiag_queacc_read_qe(pbuffer, len, pque, index);
+ index++;
+ if (index > pque->entry_count - 1)
+ break;
+ }
+
+ /* Set up the offset for next portion of pci cfg read */
+ if (index > pque->entry_count - 1)
+ index = 0;
+ idiag.offset.last_rd = index;
+
+ return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
+}
+
+/**
+ * lpfc_idiag_queacc_write - Syntax check and set up idiag queacc commands
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the user data from.
+ * @nbytes: The number of bytes to get.
+ * @ppos: The position in the file to start reading from.
+ *
+ * This routine get the debugfs idiag command struct from user space and then
+ * perform the syntax check for port queue read (dump) or write (set) command
+ * accordingly. In the case of port queue read command, it sets up the command
+ * in the idiag command struct for the following debugfs read operation. In
+ * the case of port queue write operation, it executes the write operation
+ * into the port queue entry accordingly.
+ *
+ * It returns the @nbytges passing in from debugfs user space when successful.
+ * In case of error conditions, it returns proper error code back to the user
+ * space.
+ **/
+static ssize_t
+lpfc_idiag_queacc_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+ uint32_t qidx, quetp, queid, index, count, offset, value;
+ uint32_t *pentry;
+ struct lpfc_queue *pque;
+ int rc;
+
+ /* This is a user write operation */
+ debug->op = LPFC_IDIAG_OP_WR;
+
+ rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
+ if (rc < 0)
+ return rc;
+
+ /* Get and sanity check on command feilds */
+ quetp = idiag.cmd.data[0];
+ queid = idiag.cmd.data[1];
+ index = idiag.cmd.data[2];
+ count = idiag.cmd.data[3];
+ offset = idiag.cmd.data[4];
+ value = idiag.cmd.data[5];
+
+ /* Sanity check on command line arguments */
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_WR ||
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_ST ||
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_CL) {
+ if (rc != LPFC_QUE_ACC_WR_CMD_ARG)
+ goto error_out;
+ if (count != 1)
+ goto error_out;
+ } else if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_RD) {
+ if (rc != LPFC_QUE_ACC_RD_CMD_ARG)
+ goto error_out;
+ } else
+ goto error_out;
+
+ switch (quetp) {
+ case LPFC_IDIAG_EQ:
+ /* Slow-path event queue */
+ if (phba->sli4_hba.sp_eq->queue_id == queid) {
+ /* Sanity check */
+ rc = lpfc_idiag_que_param_check(
+ phba->sli4_hba.sp_eq, index, count);
+ if (rc)
+ goto error_out;
+ idiag.ptr_private = phba->sli4_hba.sp_eq;
+ goto pass_check;
+ }
+ /* Fast-path event queue */
+ for (qidx = 0; qidx < phba->cfg_fcp_eq_count; qidx++) {
+ if (phba->sli4_hba.fp_eq[qidx]->queue_id == queid) {
+ /* Sanity check */
+ rc = lpfc_idiag_que_param_check(
+ phba->sli4_hba.fp_eq[qidx],
+ index, count);
+ if (rc)
+ goto error_out;
+ idiag.ptr_private = phba->sli4_hba.fp_eq[qidx];
+ goto pass_check;
+ }
+ }
+ goto error_out;
+ break;
+ case LPFC_IDIAG_CQ:
+ /* MBX complete queue */
+ if (phba->sli4_hba.mbx_cq->queue_id == queid) {
+ /* Sanity check */
+ rc = lpfc_idiag_que_param_check(
+ phba->sli4_hba.mbx_cq, index, count);
+ if (rc)
+ goto error_out;
+ idiag.ptr_private = phba->sli4_hba.mbx_cq;
+ goto pass_check;
+ }
+ /* ELS complete queue */
+ if (phba->sli4_hba.els_cq->queue_id == queid) {
+ /* Sanity check */
+ rc = lpfc_idiag_que_param_check(
+ phba->sli4_hba.els_cq, index, count);
+ if (rc)
+ goto error_out;
+ idiag.ptr_private = phba->sli4_hba.els_cq;
+ goto pass_check;
+ }
+ /* FCP complete queue */
+ for (qidx = 0; qidx < phba->cfg_fcp_eq_count; qidx++) {
+ if (phba->sli4_hba.fcp_cq[qidx]->queue_id == queid) {
+ /* Sanity check */
+ rc = lpfc_idiag_que_param_check(
+ phba->sli4_hba.fcp_cq[qidx],
+ index, count);
+ if (rc)
+ goto error_out;
+ idiag.ptr_private =
+ phba->sli4_hba.fcp_cq[qidx];
+ goto pass_check;
+ }
+ }
+ goto error_out;
+ break;
+ case LPFC_IDIAG_MQ:
+ /* MBX work queue */
+ if (phba->sli4_hba.mbx_wq->queue_id == queid) {
+ /* Sanity check */
+ rc = lpfc_idiag_que_param_check(
+ phba->sli4_hba.mbx_wq, index, count);
+ if (rc)
+ goto error_out;
+ idiag.ptr_private = phba->sli4_hba.mbx_wq;
+ goto pass_check;
+ }
+ break;
+ case LPFC_IDIAG_WQ:
+ /* ELS work queue */
+ if (phba->sli4_hba.els_wq->queue_id == queid) {
+ /* Sanity check */
+ rc = lpfc_idiag_que_param_check(
+ phba->sli4_hba.els_wq, index, count);
+ if (rc)
+ goto error_out;
+ idiag.ptr_private = phba->sli4_hba.els_wq;
+ goto pass_check;
+ }
+ /* FCP work queue */
+ for (qidx = 0; qidx < phba->cfg_fcp_wq_count; qidx++) {
+ if (phba->sli4_hba.fcp_wq[qidx]->queue_id == queid) {
+ /* Sanity check */
+ rc = lpfc_idiag_que_param_check(
+ phba->sli4_hba.fcp_wq[qidx],
+ index, count);
+ if (rc)
+ goto error_out;
+ idiag.ptr_private =
+ phba->sli4_hba.fcp_wq[qidx];
+ goto pass_check;
+ }
+ }
+ goto error_out;
+ break;
+ case LPFC_IDIAG_RQ:
+ /* HDR queue */
+ if (phba->sli4_hba.hdr_rq->queue_id == queid) {
+ /* Sanity check */
+ rc = lpfc_idiag_que_param_check(
+ phba->sli4_hba.hdr_rq, index, count);
+ if (rc)
+ goto error_out;
+ idiag.ptr_private = phba->sli4_hba.hdr_rq;
+ goto pass_check;
+ }
+ /* DAT queue */
+ if (phba->sli4_hba.dat_rq->queue_id == queid) {
+ /* Sanity check */
+ rc = lpfc_idiag_que_param_check(
+ phba->sli4_hba.dat_rq, index, count);
+ if (rc)
+ goto error_out;
+ idiag.ptr_private = phba->sli4_hba.dat_rq;
+ goto pass_check;
+ }
+ goto error_out;
+ break;
+ default:
+ goto error_out;
+ break;
+ }
+
+pass_check:
+
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_RD) {
+ if (count == LPFC_QUE_ACC_BROWSE)
+ idiag.offset.last_rd = index;
+ }
+
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_WR ||
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_ST ||
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_CL) {
+ /* Additional sanity checks on write operation */
+ pque = (struct lpfc_queue *)idiag.ptr_private;
+ if (offset > pque->entry_size/sizeof(uint32_t) - 1)
+ goto error_out;
+ pentry = pque->qe[index].address;
+ pentry += offset;
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_WR)
+ *pentry = value;
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_ST)
+ *pentry |= value;
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_QUEACC_CL)
+ *pentry &= ~value;
+ }
+ return nbytes;
+
+error_out:
+ /* Clean out command structure on command error out */
+ memset(&idiag, 0, sizeof(idiag));
+ return -EINVAL;
+}
+
+/**
+ * lpfc_idiag_drbacc_read_reg - idiag debugfs read a doorbell register
+ * @phba: The pointer to hba structure.
+ * @pbuffer: The pointer to the buffer to copy the data to.
+ * @len: The lenght of bytes to copied.
+ * @drbregid: The id to doorbell registers.
+ *
+ * Description:
+ * This routine reads a doorbell register and copies its content to the
+ * user buffer pointed to by @pbuffer.
+ *
+ * Returns:
+ * This function returns the amount of data that was copied into @pbuffer.
+ **/
+static int
+lpfc_idiag_drbacc_read_reg(struct lpfc_hba *phba, char *pbuffer,
+ int len, uint32_t drbregid)
+{
+
+ if (!pbuffer)
+ return 0;
+
+ switch (drbregid) {
+ case LPFC_DRB_EQCQ:
+ len += snprintf(pbuffer+len, LPFC_DRB_ACC_BUF_SIZE-len,
+ "EQCQ-DRB-REG: 0x%08x\n",
+ readl(phba->sli4_hba.EQCQDBregaddr));
+ break;
+ case LPFC_DRB_MQ:
+ len += snprintf(pbuffer+len, LPFC_DRB_ACC_BUF_SIZE-len,
+ "MQ-DRB-REG: 0x%08x\n",
+ readl(phba->sli4_hba.MQDBregaddr));
+ break;
+ case LPFC_DRB_WQ:
+ len += snprintf(pbuffer+len, LPFC_DRB_ACC_BUF_SIZE-len,
+ "WQ-DRB-REG: 0x%08x\n",
+ readl(phba->sli4_hba.WQDBregaddr));
+ break;
+ case LPFC_DRB_RQ:
+ len += snprintf(pbuffer+len, LPFC_DRB_ACC_BUF_SIZE-len,
+ "RQ-DRB-REG: 0x%08x\n",
+ readl(phba->sli4_hba.RQDBregaddr));
+ break;
+ default:
+ break;
+ }
+
+ return len;
+}
+
+/**
+ * lpfc_idiag_drbacc_read - idiag debugfs read port doorbell
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the data to.
+ * @nbytes: The number of bytes to read.
+ * @ppos: The position in the file to start reading from.
+ *
+ * Description:
+ * This routine reads data from the @phba device doorbell register according
+ * to the idiag command, and copies to user @buf. Depending on the doorbell
+ * register read command setup, it does either a single doorbell register
+ * read or dump all doorbell registers.
+ *
+ * Returns:
+ * This function returns the amount of data that was read (this could be less
+ * than @nbytes if the end of the file was reached) or a negative error value.
+ **/
+static ssize_t
+lpfc_idiag_drbacc_read(struct file *file, char __user *buf, size_t nbytes,
+ loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+ uint32_t drb_reg_id, i;
+ char *pbuffer;
+ int len = 0;
+
+ /* This is a user read operation */
+ debug->op = LPFC_IDIAG_OP_RD;
+
+ if (!debug->buffer)
+ debug->buffer = kmalloc(LPFC_DRB_ACC_BUF_SIZE, GFP_KERNEL);
+ if (!debug->buffer)
+ return 0;
+ pbuffer = debug->buffer;
+
+ if (*ppos)
+ return 0;
+
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_RD)
+ drb_reg_id = idiag.cmd.data[0];
+ else
+ return 0;
+
+ if (drb_reg_id == LPFC_DRB_ACC_ALL)
+ for (i = 1; i <= LPFC_DRB_MAX; i++)
+ len = lpfc_idiag_drbacc_read_reg(phba,
+ pbuffer, len, i);
+ else
+ len = lpfc_idiag_drbacc_read_reg(phba,
+ pbuffer, len, drb_reg_id);
+
+ return simple_read_from_buffer(buf, nbytes, ppos, pbuffer, len);
+}
+
+/**
+ * lpfc_idiag_drbacc_write - Syntax check and set up idiag drbacc commands
+ * @file: The file pointer to read from.
+ * @buf: The buffer to copy the user data from.
+ * @nbytes: The number of bytes to get.
+ * @ppos: The position in the file to start reading from.
+ *
+ * This routine get the debugfs idiag command struct from user space and then
+ * perform the syntax check for port doorbell register read (dump) or write
+ * (set) command accordingly. In the case of port queue read command, it sets
+ * up the command in the idiag command struct for the following debugfs read
+ * operation. In the case of port doorbell register write operation, it
+ * executes the write operation into the port doorbell register accordingly.
+ *
+ * It returns the @nbytges passing in from debugfs user space when successful.
+ * In case of error conditions, it returns proper error code back to the user
+ * space.
+ **/
+static ssize_t
+lpfc_idiag_drbacc_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ struct lpfc_debug *debug = file->private_data;
+ struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
+ uint32_t drb_reg_id, value, reg_val;
+ void __iomem *drb_reg;
+ int rc;
+
+ /* This is a user write operation */
+ debug->op = LPFC_IDIAG_OP_WR;
+
+ rc = lpfc_idiag_cmd_get(buf, nbytes, &idiag.cmd);
+ if (rc < 0)
+ return rc;
+
+ /* Sanity check on command line arguments */
+ drb_reg_id = idiag.cmd.data[0];
+ value = idiag.cmd.data[1];
+
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_WR ||
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_ST ||
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_CL) {
+ if (rc != LPFC_DRB_ACC_WR_CMD_ARG)
+ goto error_out;
+ if (drb_reg_id > LPFC_DRB_MAX)
+ goto error_out;
+ } else if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_RD) {
+ if (rc != LPFC_DRB_ACC_RD_CMD_ARG)
+ goto error_out;
+ if ((drb_reg_id > LPFC_DRB_MAX) &&
+ (drb_reg_id != LPFC_DRB_ACC_ALL))
+ goto error_out;
+ } else
+ goto error_out;
+
+ /* Perform the write access operation */
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_WR ||
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_ST ||
+ idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_CL) {
+ switch (drb_reg_id) {
+ case LPFC_DRB_EQCQ:
+ drb_reg = phba->sli4_hba.EQCQDBregaddr;
+ break;
+ case LPFC_DRB_MQ:
+ drb_reg = phba->sli4_hba.MQDBregaddr;
+ break;
+ case LPFC_DRB_WQ:
+ drb_reg = phba->sli4_hba.WQDBregaddr;
+ break;
+ case LPFC_DRB_RQ:
+ drb_reg = phba->sli4_hba.RQDBregaddr;
+ break;
+ default:
+ goto error_out;
+ }
+
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_WR)
+ reg_val = value;
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_ST) {
+ reg_val = readl(drb_reg);
+ reg_val |= value;
+ }
+ if (idiag.cmd.opcode == LPFC_IDIAG_CMD_DRBACC_CL) {
+ reg_val = readl(drb_reg);
+ reg_val &= ~value;
+ }
+ writel(reg_val, drb_reg);
+ readl(drb_reg); /* flush */
+ }
+ return nbytes;
+
+error_out:
+ /* Clean out command structure on command error out */
+ memset(&idiag, 0, sizeof(idiag));
+ return -EINVAL;
+}
+
#undef lpfc_debugfs_op_disc_trc
static const struct file_operations lpfc_debugfs_op_disc_trc = {
.owner = THIS_MODULE,
@@ -1986,6 +2425,26 @@ static const struct file_operations lpfc_idiag_op_queInfo = {
.release = lpfc_idiag_release,
};
+#undef lpfc_idiag_op_queacc
+static const struct file_operations lpfc_idiag_op_queAcc = {
+ .owner = THIS_MODULE,
+ .open = lpfc_idiag_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_idiag_queacc_read,
+ .write = lpfc_idiag_queacc_write,
+ .release = lpfc_idiag_cmd_release,
+};
+
+#undef lpfc_idiag_op_drbacc
+static const struct file_operations lpfc_idiag_op_drbAcc = {
+ .owner = THIS_MODULE,
+ .open = lpfc_idiag_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_idiag_drbacc_read,
+ .write = lpfc_idiag_drbacc_write,
+ .release = lpfc_idiag_cmd_release,
+};
+
#endif
/**
@@ -2261,6 +2720,32 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
}
}
+ /* iDiag access PCI function queue */
+ snprintf(name, sizeof(name), "queAcc");
+ if (!phba->idiag_que_acc) {
+ phba->idiag_que_acc =
+ debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
+ phba->idiag_root, phba, &lpfc_idiag_op_queAcc);
+ if (!phba->idiag_que_acc) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+ "2926 Can't create idiag debugfs\n");
+ goto debug_failed;
+ }
+ }
+
+ /* iDiag access PCI function doorbell registers */
+ snprintf(name, sizeof(name), "drbAcc");
+ if (!phba->idiag_drb_acc) {
+ phba->idiag_drb_acc =
+ debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
+ phba->idiag_root, phba, &lpfc_idiag_op_drbAcc);
+ if (!phba->idiag_drb_acc) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+ "2927 Can't create idiag debugfs\n");
+ goto debug_failed;
+ }
+ }
+
debug_failed:
return;
#endif
@@ -2339,6 +2824,16 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport)
* iDiag release
*/
if (phba->sli_rev == LPFC_SLI_REV4) {
+ if (phba->idiag_drb_acc) {
+ /* iDiag drbAcc */
+ debugfs_remove(phba->idiag_drb_acc);
+ phba->idiag_drb_acc = NULL;
+ }
+ if (phba->idiag_que_acc) {
+ /* iDiag queAcc */
+ debugfs_remove(phba->idiag_que_acc);
+ phba->idiag_que_acc = NULL;
+ }
if (phba->idiag_que_info) {
/* iDiag queInfo */
debugfs_remove(phba->idiag_que_info);
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.h b/drivers/scsi/lpfc/lpfc_debugfs.h
index 91b9a9427cd..6525a5e62d2 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.h
+++ b/drivers/scsi/lpfc/lpfc_debugfs.h
@@ -39,13 +39,42 @@
/* hbqinfo output buffer size */
#define LPFC_HBQINFO_SIZE 8192
-/* rdPciConf output buffer size */
+/* pciConf */
+#define LPFC_PCI_CFG_BROWSE 0xffff
+#define LPFC_PCI_CFG_RD_CMD_ARG 2
+#define LPFC_PCI_CFG_WR_CMD_ARG 3
#define LPFC_PCI_CFG_SIZE 4096
#define LPFC_PCI_CFG_RD_BUF_SIZE (LPFC_PCI_CFG_SIZE/2)
#define LPFC_PCI_CFG_RD_SIZE (LPFC_PCI_CFG_SIZE/4)
-/* queue info output buffer size */
-#define LPFC_QUE_INFO_GET_BUF_SIZE 2048
+/* queue info */
+#define LPFC_QUE_INFO_GET_BUF_SIZE 4096
+
+/* queue acc */
+#define LPFC_QUE_ACC_BROWSE 0xffff
+#define LPFC_QUE_ACC_RD_CMD_ARG 4
+#define LPFC_QUE_ACC_WR_CMD_ARG 6
+#define LPFC_QUE_ACC_BUF_SIZE 4096
+#define LPFC_QUE_ACC_SIZE (LPFC_QUE_ACC_BUF_SIZE/2)
+
+#define LPFC_IDIAG_EQ 1
+#define LPFC_IDIAG_CQ 2
+#define LPFC_IDIAG_MQ 3
+#define LPFC_IDIAG_WQ 4
+#define LPFC_IDIAG_RQ 5
+
+/* doorbell acc */
+#define LPFC_DRB_ACC_ALL 0xffff
+#define LPFC_DRB_ACC_RD_CMD_ARG 1
+#define LPFC_DRB_ACC_WR_CMD_ARG 2
+#define LPFC_DRB_ACC_BUF_SIZE 256
+
+#define LPFC_DRB_EQCQ 1
+#define LPFC_DRB_MQ 2
+#define LPFC_DRB_WQ 3
+#define LPFC_DRB_RQ 4
+
+#define LPFC_DRB_MAX 4
#define SIZE_U8 sizeof(uint8_t)
#define SIZE_U16 sizeof(uint16_t)
@@ -73,13 +102,23 @@ struct lpfc_idiag_offset {
uint32_t last_rd;
};
-#define LPFC_IDIAG_CMD_DATA_SIZE 4
+#define LPFC_IDIAG_CMD_DATA_SIZE 8
struct lpfc_idiag_cmd {
uint32_t opcode;
#define LPFC_IDIAG_CMD_PCICFG_RD 0x00000001
#define LPFC_IDIAG_CMD_PCICFG_WR 0x00000002
#define LPFC_IDIAG_CMD_PCICFG_ST 0x00000003
#define LPFC_IDIAG_CMD_PCICFG_CL 0x00000004
+
+#define LPFC_IDIAG_CMD_QUEACC_RD 0x00000011
+#define LPFC_IDIAG_CMD_QUEACC_WR 0x00000012
+#define LPFC_IDIAG_CMD_QUEACC_ST 0x00000013
+#define LPFC_IDIAG_CMD_QUEACC_CL 0x00000014
+
+#define LPFC_IDIAG_CMD_DRBACC_RD 0x00000021
+#define LPFC_IDIAG_CMD_DRBACC_WR 0x00000022
+#define LPFC_IDIAG_CMD_DRBACC_ST 0x00000023
+#define LPFC_IDIAG_CMD_DRBACC_CL 0x00000024
uint32_t data[LPFC_IDIAG_CMD_DATA_SIZE];
};
@@ -87,6 +126,7 @@ struct lpfc_idiag {
uint32_t active;
struct lpfc_idiag_cmd cmd;
struct lpfc_idiag_offset offset;
+ void *ptr_private;
};
#endif
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index d34b69f9cdb..e2c452467c8 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -670,6 +670,7 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
* Driver needs to re-reg VPI in order for f/w
* to update the MAC address.
*/
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
lpfc_register_new_vport(phba, vport, ndlp);
return 0;
}
@@ -869,8 +870,8 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
*/
if ((phba->hba_flag & HBA_FIP_SUPPORT) &&
(phba->fcf.fcf_flag & FCF_DISCOVERY) &&
- (irsp->ulpStatus != IOSTAT_LOCAL_REJECT) &&
- (irsp->un.ulpWord[4] != IOERR_SLI_ABORTED)) {
+ !((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+ (irsp->un.ulpWord[4] == IOERR_SLI_ABORTED))) {
lpfc_printf_log(phba, KERN_WARNING, LOG_FIP | LOG_ELS,
"2611 FLOGI failed on FCF (x%x), "
"status:x%x/x%x, tmo:x%x, perform "
@@ -1085,14 +1086,15 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
if (sp->cmn.fcphHigh < FC_PH3)
sp->cmn.fcphHigh = FC_PH3;
- if ((phba->sli_rev == LPFC_SLI_REV4) &&
- (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
- LPFC_SLI_INTF_IF_TYPE_0)) {
- elsiocb->iocb.ulpCt_h = ((SLI4_CT_FCFI >> 1) & 1);
- elsiocb->iocb.ulpCt_l = (SLI4_CT_FCFI & 1);
- /* FLOGI needs to be 3 for WQE FCFI */
- /* Set the fcfi to the fcfi we registered with */
- elsiocb->iocb.ulpContext = phba->fcf.fcfi;
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
+ LPFC_SLI_INTF_IF_TYPE_0) {
+ elsiocb->iocb.ulpCt_h = ((SLI4_CT_FCFI >> 1) & 1);
+ elsiocb->iocb.ulpCt_l = (SLI4_CT_FCFI & 1);
+ /* FLOGI needs to be 3 for WQE FCFI */
+ /* Set the fcfi to the fcfi we registered with */
+ elsiocb->iocb.ulpContext = phba->fcf.fcfi;
+ }
} else if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) {
sp->cmn.request_multiple_Nport = 1;
/* For FLOGI, Let FLOGI rsp set the NPortID for VPI 0 */
@@ -4107,13 +4109,13 @@ lpfc_els_clear_rrq(struct lpfc_vport *vport,
pcmd += sizeof(uint32_t);
rrq = (struct RRQ *)pcmd;
rrq->rrq_exchg = be32_to_cpu(rrq->rrq_exchg);
- rxid = be16_to_cpu(bf_get(rrq_rxid, rrq));
+ rxid = bf_get(rrq_rxid, rrq);
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
"2883 Clear RRQ for SID:x%x OXID:x%x RXID:x%x"
" x%x x%x\n",
be32_to_cpu(bf_get(rrq_did, rrq)),
- be16_to_cpu(bf_get(rrq_oxid, rrq)),
+ bf_get(rrq_oxid, rrq),
rxid,
iocb->iotag, iocb->iocb.ulpContext);
@@ -4121,7 +4123,7 @@ lpfc_els_clear_rrq(struct lpfc_vport *vport,
"Clear RRQ: did:x%x flg:x%x exchg:x%.08x",
ndlp->nlp_DID, ndlp->nlp_flag, rrq->rrq_exchg);
if (vport->fc_myDID == be32_to_cpu(bf_get(rrq_did, rrq)))
- xri = be16_to_cpu(bf_get(rrq_oxid, rrq));
+ xri = bf_get(rrq_oxid, rrq);
else
xri = rxid;
prrq = lpfc_get_active_rrq(vport, xri, ndlp->nlp_DID);
@@ -7290,8 +7292,9 @@ lpfc_cmpl_els_npiv_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
struct lpfc_vport *vport = cmdiocb->vport;
IOCB_t *irsp;
struct lpfc_nodelist *ndlp;
- ndlp = (struct lpfc_nodelist *)cmdiocb->context1;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ ndlp = (struct lpfc_nodelist *)cmdiocb->context1;
irsp = &rspiocb->iocb;
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_CMD,
"LOGO npiv cmpl: status:x%x/x%x did:x%x",
@@ -7302,6 +7305,19 @@ lpfc_cmpl_els_npiv_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
/* Trigger the release of the ndlp after logo */
lpfc_nlp_put(ndlp);
+
+ /* NPIV LOGO completes to NPort <nlp_DID> */
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "2928 NPIV LOGO completes to NPort x%x "
+ "Data: x%x x%x x%x x%x\n",
+ ndlp->nlp_DID, irsp->ulpStatus, irsp->un.ulpWord[4],
+ irsp->ulpTimeout, vport->num_disc_nodes);
+
+ if (irsp->ulpStatus == IOSTAT_SUCCESS) {
+ spin_lock_irq(shost->host_lock);
+ vport->fc_flag &= ~FC_FABRIC;
+ spin_unlock_irq(shost->host_lock);
+ }
}
/**
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index 301498301a8..7a35df5e203 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2009 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2011 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* Portions Copyright (C) 2004-2005 Christoph Hellwig *
@@ -3569,6 +3569,10 @@ lpfc_register_remote_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
"rport add: did:x%x flg:x%x type x%x",
ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_type);
+ /* Don't add the remote port if unloading. */
+ if (vport->load_flag & FC_UNLOADING)
+ return;
+
ndlp->rport = rport = fc_remote_port_add(shost, 0, &rport_ids);
if (!rport || !get_device(&rport->dev)) {
dev_printk(KERN_WARNING, &phba->pcidev->dev,
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
index 8433ac0d9fb..4dff668ebda 100644
--- a/drivers/scsi/lpfc/lpfc_hw4.h
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -1059,6 +1059,11 @@ struct rq_context {
#define lpfc_rq_context_rqe_size_SHIFT 8 /* Version 1 Only */
#define lpfc_rq_context_rqe_size_MASK 0x0000000F
#define lpfc_rq_context_rqe_size_WORD word0
+#define LPFC_RQE_SIZE_8 2
+#define LPFC_RQE_SIZE_16 3
+#define LPFC_RQE_SIZE_32 4
+#define LPFC_RQE_SIZE_64 5
+#define LPFC_RQE_SIZE_128 6
#define lpfc_rq_context_page_size_SHIFT 0 /* Version 1 Only */
#define lpfc_rq_context_page_size_MASK 0x000000FF
#define lpfc_rq_context_page_size_WORD word0
@@ -2108,6 +2113,8 @@ struct lpfc_mbx_pc_sli4_params {
#define sgl_pp_align_WORD word12
uint32_t rsvd_13_63[51];
};
+#define SLI4_PAGE_ALIGN(addr) (((addr)+((SLI4_PAGE_SIZE)-1)) \
+ &(~((SLI4_PAGE_SIZE)-1)))
struct lpfc_sli4_parameters {
uint32_t word0;
@@ -2491,6 +2498,9 @@ struct wqe_common {
#define wqe_reqtag_SHIFT 0
#define wqe_reqtag_MASK 0x0000FFFF
#define wqe_reqtag_WORD word9
+#define wqe_temp_rpi_SHIFT 16
+#define wqe_temp_rpi_MASK 0x0000FFFF
+#define wqe_temp_rpi_WORD word9
#define wqe_rcvoxid_SHIFT 16
#define wqe_rcvoxid_MASK 0x0000FFFF
#define wqe_rcvoxid_WORD word9
@@ -2524,7 +2534,7 @@ struct wqe_common {
#define wqe_wqes_WORD word10
/* Note that this field overlaps above fields */
#define wqe_wqid_SHIFT 1
-#define wqe_wqid_MASK 0x0000007f
+#define wqe_wqid_MASK 0x00007fff
#define wqe_wqid_WORD word10
#define wqe_pri_SHIFT 16
#define wqe_pri_MASK 0x00000007
@@ -2621,7 +2631,11 @@ struct xmit_els_rsp64_wqe {
uint32_t rsvd4;
struct wqe_did wqe_dest;
struct wqe_common wqe_com; /* words 6-11 */
- uint32_t rsvd_12_15[4];
+ uint32_t word12;
+#define wqe_rsp_temp_rpi_SHIFT 0
+#define wqe_rsp_temp_rpi_MASK 0x0000FFFF
+#define wqe_rsp_temp_rpi_WORD word12
+ uint32_t rsvd_13_15[3];
};
struct xmit_bls_rsp64_wqe {
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 505f88443b5..7dda036a1af 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -3209,9 +3209,9 @@ lpfc_sli4_async_link_evt(struct lpfc_hba *phba,
phba->sli4_hba.link_state.logical_speed =
bf_get(lpfc_acqe_logical_link_speed, acqe_link);
lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
- "2900 Async FCoE Link event - Speed:%dGBit duplex:x%x "
- "LA Type:x%x Port Type:%d Port Number:%d Logical "
- "speed:%dMbps Fault:%d\n",
+ "2900 Async FC/FCoE Link event - Speed:%dGBit "
+ "duplex:x%x LA Type:x%x Port Type:%d Port Number:%d "
+ "Logical speed:%dMbps Fault:%d\n",
phba->sli4_hba.link_state.speed,
phba->sli4_hba.link_state.topology,
phba->sli4_hba.link_state.status,
@@ -4906,6 +4906,7 @@ lpfc_sli4_create_rpi_hdr(struct lpfc_hba *phba)
uint16_t rpi_limit, curr_rpi_range;
struct lpfc_dmabuf *dmabuf;
struct lpfc_rpi_hdr *rpi_hdr;
+ uint32_t rpi_count;
rpi_limit = phba->sli4_hba.max_cfg_param.rpi_base +
phba->sli4_hba.max_cfg_param.max_rpi - 1;
@@ -4920,7 +4921,9 @@ lpfc_sli4_create_rpi_hdr(struct lpfc_hba *phba)
* and to allow the full max_rpi range per port.
*/
if ((curr_rpi_range + (LPFC_RPI_HDR_COUNT - 1)) > rpi_limit)
- return NULL;
+ rpi_count = rpi_limit - curr_rpi_range;
+ else
+ rpi_count = LPFC_RPI_HDR_COUNT;
/*
* First allocate the protocol header region for the port. The
@@ -4961,7 +4964,7 @@ lpfc_sli4_create_rpi_hdr(struct lpfc_hba *phba)
* The next_rpi stores the next module-64 rpi value to post
* in any subsequent rpi memory region postings.
*/
- phba->sli4_hba.next_rpi += LPFC_RPI_HDR_COUNT;
+ phba->sli4_hba.next_rpi += rpi_count;
spin_unlock_irq(&phba->hbalock);
return rpi_hdr;
@@ -7004,7 +7007,8 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
lpfc_sli4_bar0_register_memmap(phba, if_type);
}
- if (pci_resource_start(pdev, 2)) {
+ if ((if_type == LPFC_SLI_INTF_IF_TYPE_0) &&
+ (pci_resource_start(pdev, 2))) {
/*
* Map SLI4 if type 0 HBA Control Register base to a kernel
* virtual address and setup the registers.
@@ -7021,7 +7025,8 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
lpfc_sli4_bar1_register_memmap(phba);
}
- if (pci_resource_start(pdev, 4)) {
+ if ((if_type == LPFC_SLI_INTF_IF_TYPE_0) &&
+ (pci_resource_start(pdev, 4))) {
/*
* Map SLI4 if type 0 HBA Doorbell Register base to a kernel
* virtual address and setup the registers.
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
index fbab9734e9b..e6ce9033f85 100644
--- a/drivers/scsi/lpfc/lpfc_mbox.c
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -1736,7 +1736,7 @@ lpfc_sli4_config(struct lpfc_hba *phba, struct lpfcMboxq *mbox,
}
/* Setup for the none-embedded mbox command */
- pcount = (PAGE_ALIGN(length))/SLI4_PAGE_SIZE;
+ pcount = (SLI4_PAGE_ALIGN(length))/SLI4_PAGE_SIZE;
pcount = (pcount > LPFC_SLI4_MBX_SGE_MAX_PAGES) ?
LPFC_SLI4_MBX_SGE_MAX_PAGES : pcount;
/* Allocate record for keeping SGE virtual addresses */
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index fe7cc84e773..84e4481b240 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -3238,9 +3238,8 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
if (!lpfc_cmd) {
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
"2873 SCSI Layer I/O Abort Request IO CMPL Status "
- "x%x ID %d "
- "LUN %d snum %#lx\n", ret, cmnd->device->id,
- cmnd->device->lun, cmnd->serial_number);
+ "x%x ID %d LUN %d\n",
+ ret, cmnd->device->id, cmnd->device->lun);
return SUCCESS;
}
@@ -3318,16 +3317,15 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
"0748 abort handler timed out waiting "
"for abort to complete: ret %#x, ID %d, "
- "LUN %d, snum %#lx\n",
- ret, cmnd->device->id, cmnd->device->lun,
- cmnd->serial_number);
+ "LUN %d\n",
+ ret, cmnd->device->id, cmnd->device->lun);
}
out:
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
"0749 SCSI Layer I/O Abort Request Status x%x ID %d "
- "LUN %d snum %#lx\n", ret, cmnd->device->id,
- cmnd->device->lun, cmnd->serial_number);
+ "LUN %d\n", ret, cmnd->device->id,
+ cmnd->device->lun);
return ret;
}
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index dacabbe0a58..837d272cb2d 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -4769,8 +4769,7 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
else
phba->hba_flag &= ~HBA_FIP_SUPPORT;
- if (phba->sli_rev != LPFC_SLI_REV4 ||
- !(phba->hba_flag & HBA_FCOE_MODE)) {
+ if (phba->sli_rev != LPFC_SLI_REV4) {
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
"0376 READ_REV Error. SLI Level %d "
"FCoE enabled %d\n",
@@ -5018,10 +5017,11 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
lpfc_reg_fcfi(phba, mboxq);
mboxq->vport = phba->pport;
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
- if (rc == MBX_SUCCESS)
- rc = 0;
- else
+ if (rc != MBX_SUCCESS)
goto out_unset_queue;
+ rc = 0;
+ phba->fcf.fcfi = bf_get(lpfc_reg_fcfi_fcfi,
+ &mboxq->u.mqe.un.reg_fcfi);
}
/*
* The port is ready, set the host's link state to LINK_DOWN
@@ -6402,6 +6402,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
uint32_t els_id = LPFC_ELS_ID_DEFAULT;
int numBdes, i;
struct ulp_bde64 bde;
+ struct lpfc_nodelist *ndlp;
fip = phba->hba_flag & HBA_FIP_SUPPORT;
/* The fcp commands will set command type */
@@ -6447,6 +6448,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
switch (iocbq->iocb.ulpCommand) {
case CMD_ELS_REQUEST64_CR:
+ ndlp = (struct lpfc_nodelist *)iocbq->context1;
if (!iocbq->iocb.ulpLe) {
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"2007 Only Limited Edition cmd Format"
@@ -6472,6 +6474,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
els_id = ((iocbq->iocb_flag & LPFC_FIP_ELS_ID_MASK)
>> LPFC_FIP_ELS_ID_SHIFT);
}
+ bf_set(wqe_temp_rpi, &wqe->els_req.wqe_com, ndlp->nlp_rpi);
bf_set(wqe_els_id, &wqe->els_req.wqe_com, els_id);
bf_set(wqe_dbde, &wqe->els_req.wqe_com, 1);
bf_set(wqe_iod, &wqe->els_req.wqe_com, LPFC_WQE_IOD_READ);
@@ -6604,6 +6607,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
command_type = OTHER_COMMAND;
break;
case CMD_XMIT_ELS_RSP64_CX:
+ ndlp = (struct lpfc_nodelist *)iocbq->context1;
/* words0-2 BDE memcpy */
/* word3 iocb=iotag32 wqe=response_payload_len */
wqe->xmit_els_rsp.response_payload_len = xmit_len;
@@ -6626,6 +6630,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
bf_set(wqe_lenloc, &wqe->xmit_els_rsp.wqe_com,
LPFC_WQE_LENLOC_WORD3);
bf_set(wqe_ebde_cnt, &wqe->xmit_els_rsp.wqe_com, 0);
+ bf_set(wqe_rsp_temp_rpi, &wqe->xmit_els_rsp, ndlp->nlp_rpi);
command_type = OTHER_COMMAND;
break;
case CMD_CLOSE_XRI_CN:
@@ -10522,8 +10527,8 @@ lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq,
bf_set(lpfc_mbox_hdr_version, &shdr->request,
phba->sli4_hba.pc_sli4_params.cqv);
if (phba->sli4_hba.pc_sli4_params.cqv == LPFC_Q_CREATE_VERSION_2) {
- bf_set(lpfc_mbx_cq_create_page_size, &cq_create->u.request,
- (PAGE_SIZE/SLI4_PAGE_SIZE));
+ /* FW only supports 1. Should be PAGE_SIZE/SLI4_PAGE_SIZE */
+ bf_set(lpfc_mbx_cq_create_page_size, &cq_create->u.request, 1);
bf_set(lpfc_cq_eq_id_2, &cq_create->u.request.context,
eq->queue_id);
} else {
@@ -10967,6 +10972,12 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
&rq_create->u.request.context,
hrq->entry_count);
rq_create->u.request.context.buffer_size = LPFC_HDR_BUF_SIZE;
+ bf_set(lpfc_rq_context_rqe_size,
+ &rq_create->u.request.context,
+ LPFC_RQE_SIZE_8);
+ bf_set(lpfc_rq_context_page_size,
+ &rq_create->u.request.context,
+ (PAGE_SIZE/SLI4_PAGE_SIZE));
} else {
switch (hrq->entry_count) {
default:
@@ -11042,9 +11053,12 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
phba->sli4_hba.pc_sli4_params.rqv);
if (phba->sli4_hba.pc_sli4_params.rqv == LPFC_Q_CREATE_VERSION_1) {
bf_set(lpfc_rq_context_rqe_count_1,
- &rq_create->u.request.context,
- hrq->entry_count);
+ &rq_create->u.request.context, hrq->entry_count);
rq_create->u.request.context.buffer_size = LPFC_DATA_BUF_SIZE;
+ bf_set(lpfc_rq_context_rqe_size, &rq_create->u.request.context,
+ LPFC_RQE_SIZE_8);
+ bf_set(lpfc_rq_context_page_size, &rq_create->u.request.context,
+ (PAGE_SIZE/SLI4_PAGE_SIZE));
} else {
switch (drq->entry_count) {
default:
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index 2404d1d6556..c03921b1232 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -18,7 +18,7 @@
* included with this package. *
*******************************************************************/
-#define LPFC_DRIVER_VERSION "8.3.22"
+#define LPFC_DRIVER_VERSION "8.3.23"
#define LPFC_DRIVER_NAME "lpfc"
#define LPFC_SP_DRIVER_HANDLER_NAME "lpfc:sp"
#define LPFC_FP_DRIVER_HANDLER_NAME "lpfc:fp"
diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c
index f2684dd09ed..5c1776406c9 100644
--- a/drivers/scsi/megaraid.c
+++ b/drivers/scsi/megaraid.c
@@ -1469,8 +1469,8 @@ mega_cmd_done(adapter_t *adapter, u8 completed[], int nstatus, int status)
if( scb->state & SCB_ABORT ) {
printk(KERN_WARNING
- "megaraid: aborted cmd %lx[%x] complete.\n",
- scb->cmd->serial_number, scb->idx);
+ "megaraid: aborted cmd [%x] complete.\n",
+ scb->idx);
scb->cmd->result = (DID_ABORT << 16);
@@ -1488,8 +1488,8 @@ mega_cmd_done(adapter_t *adapter, u8 completed[], int nstatus, int status)
if( scb->state & SCB_RESET ) {
printk(KERN_WARNING
- "megaraid: reset cmd %lx[%x] complete.\n",
- scb->cmd->serial_number, scb->idx);
+ "megaraid: reset cmd [%x] complete.\n",
+ scb->idx);
scb->cmd->result = (DID_RESET << 16);
@@ -1958,8 +1958,8 @@ megaraid_abort_and_reset(adapter_t *adapter, Scsi_Cmnd *cmd, int aor)
struct list_head *pos, *next;
scb_t *scb;
- printk(KERN_WARNING "megaraid: %s-%lx cmd=%x <c=%d t=%d l=%d>\n",
- (aor == SCB_ABORT)? "ABORTING":"RESET", cmd->serial_number,
+ printk(KERN_WARNING "megaraid: %s cmd=%x <c=%d t=%d l=%d>\n",
+ (aor == SCB_ABORT)? "ABORTING":"RESET",
cmd->cmnd[0], cmd->device->channel,
cmd->device->id, cmd->device->lun);
@@ -1983,9 +1983,9 @@ megaraid_abort_and_reset(adapter_t *adapter, Scsi_Cmnd *cmd, int aor)
if( scb->state & SCB_ISSUED ) {
printk(KERN_WARNING
- "megaraid: %s-%lx[%x], fw owner.\n",
+ "megaraid: %s[%x], fw owner.\n",
(aor==SCB_ABORT) ? "ABORTING":"RESET",
- cmd->serial_number, scb->idx);
+ scb->idx);
return FALSE;
}
@@ -1996,9 +1996,9 @@ megaraid_abort_and_reset(adapter_t *adapter, Scsi_Cmnd *cmd, int aor)
* list
*/
printk(KERN_WARNING
- "megaraid: %s-%lx[%x], driver owner.\n",
+ "megaraid: %s-[%x], driver owner.\n",
(aor==SCB_ABORT) ? "ABORTING":"RESET",
- cmd->serial_number, scb->idx);
+ scb->idx);
mega_free_scb(adapter, scb);
diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c
index 1dba32870b4..2e6619eff3e 100644
--- a/drivers/scsi/megaraid/megaraid_mbox.c
+++ b/drivers/scsi/megaraid/megaraid_mbox.c
@@ -2315,8 +2315,8 @@ megaraid_mbox_dpc(unsigned long devp)
// Was an abort issued for this command earlier
if (scb->state & SCB_ABORT) {
con_log(CL_ANN, (KERN_NOTICE
- "megaraid: aborted cmd %lx[%x] completed\n",
- scp->serial_number, scb->sno));
+ "megaraid: aborted cmd [%x] completed\n",
+ scb->sno));
}
/*
@@ -2472,8 +2472,8 @@ megaraid_abort_handler(struct scsi_cmnd *scp)
raid_dev = ADAP2RAIDDEV(adapter);
con_log(CL_ANN, (KERN_WARNING
- "megaraid: aborting-%ld cmd=%x <c=%d t=%d l=%d>\n",
- scp->serial_number, scp->cmnd[0], SCP2CHANNEL(scp),
+ "megaraid: aborting cmd=%x <c=%d t=%d l=%d>\n",
+ scp->cmnd[0], SCP2CHANNEL(scp),
SCP2TARGET(scp), SCP2LUN(scp)));
// If FW has stopped responding, simply return failure
@@ -2496,9 +2496,8 @@ megaraid_abort_handler(struct scsi_cmnd *scp)
list_del_init(&scb->list); // from completed list
con_log(CL_ANN, (KERN_WARNING
- "megaraid: %ld:%d[%d:%d], abort from completed list\n",
- scp->serial_number, scb->sno,
- scb->dev_channel, scb->dev_target));
+ "megaraid: %d[%d:%d], abort from completed list\n",
+ scb->sno, scb->dev_channel, scb->dev_target));
scp->result = (DID_ABORT << 16);
scp->scsi_done(scp);
@@ -2527,9 +2526,8 @@ megaraid_abort_handler(struct scsi_cmnd *scp)
ASSERT(!(scb->state & SCB_ISSUED));
con_log(CL_ANN, (KERN_WARNING
- "megaraid abort: %ld[%d:%d], driver owner\n",
- scp->serial_number, scb->dev_channel,
- scb->dev_target));
+ "megaraid abort: [%d:%d], driver owner\n",
+ scb->dev_channel, scb->dev_target));
scp->result = (DID_ABORT << 16);
scp->scsi_done(scp);
@@ -2560,25 +2558,21 @@ megaraid_abort_handler(struct scsi_cmnd *scp)
if (!(scb->state & SCB_ISSUED)) {
con_log(CL_ANN, (KERN_WARNING
- "megaraid abort: %ld%d[%d:%d], invalid state\n",
- scp->serial_number, scb->sno, scb->dev_channel,
- scb->dev_target));
+ "megaraid abort: %d[%d:%d], invalid state\n",
+ scb->sno, scb->dev_channel, scb->dev_target));
BUG();
}
else {
con_log(CL_ANN, (KERN_WARNING
- "megaraid abort: %ld:%d[%d:%d], fw owner\n",
- scp->serial_number, scb->sno, scb->dev_channel,
- scb->dev_target));
+ "megaraid abort: %d[%d:%d], fw owner\n",
+ scb->sno, scb->dev_channel, scb->dev_target));
}
}
}
spin_unlock_irq(&adapter->lock);
if (!found) {
- con_log(CL_ANN, (KERN_WARNING
- "megaraid abort: scsi cmd:%ld, do now own\n",
- scp->serial_number));
+ con_log(CL_ANN, (KERN_WARNING "megaraid abort: do now own\n"));
// FIXME: Should there be a callback for this command?
return SUCCESS;
@@ -2649,9 +2643,8 @@ megaraid_reset_handler(struct scsi_cmnd *scp)
} else {
if (scb->scp == scp) { // Found command
con_log(CL_ANN, (KERN_WARNING
- "megaraid: %ld:%d[%d:%d], reset from pending list\n",
- scp->serial_number, scb->sno,
- scb->dev_channel, scb->dev_target));
+ "megaraid: %d[%d:%d], reset from pending list\n",
+ scb->sno, scb->dev_channel, scb->dev_target));
} else {
con_log(CL_ANN, (KERN_WARNING
"megaraid: IO packet with %d[%d:%d] being reset\n",
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index 66d4cea4df9..89c623ebadb 100644
--- a/drivers/scsi/megaraid/megaraid_sas_base.c
+++ b/drivers/scsi/megaraid/megaraid_sas_base.c
@@ -1751,10 +1751,9 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance)
list_del_init(&reset_cmd->list);
if (reset_cmd->scmd) {
reset_cmd->scmd->result = DID_RESET << 16;
- printk(KERN_NOTICE "%d:%p reset [%02x], %#lx\n",
+ printk(KERN_NOTICE "%d:%p reset [%02x]\n",
reset_index, reset_cmd,
- reset_cmd->scmd->cmnd[0],
- reset_cmd->scmd->serial_number);
+ reset_cmd->scmd->cmnd[0]);
reset_cmd->scmd->scsi_done(reset_cmd->scmd);
megasas_return_cmd(instance, reset_cmd);
@@ -1879,8 +1878,8 @@ static int megasas_generic_reset(struct scsi_cmnd *scmd)
instance = (struct megasas_instance *)scmd->device->host->hostdata;
- scmd_printk(KERN_NOTICE, scmd, "megasas: RESET -%ld cmd=%x retries=%x\n",
- scmd->serial_number, scmd->cmnd[0], scmd->retries);
+ scmd_printk(KERN_NOTICE, scmd, "megasas: RESET cmd=%x retries=%x\n",
+ scmd->cmnd[0], scmd->retries);
if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) {
printk(KERN_ERR "megasas: cannot recover from previous reset "
@@ -2349,9 +2348,9 @@ megasas_issue_pending_cmds_again(struct megasas_instance *instance)
cmd->frame_phys_addr ,
0, instance->reg_set);
} else if (cmd->scmd) {
- printk(KERN_NOTICE "megasas: %p scsi cmd [%02x],%#lx"
+ printk(KERN_NOTICE "megasas: %p scsi cmd [%02x]"
"detected on the internal queue, issue again.\n",
- cmd, cmd->scmd->cmnd[0], cmd->scmd->serial_number);
+ cmd, cmd->scmd->cmnd[0]);
atomic_inc(&instance->fw_outstanding);
instance->instancet->fire_cmd(instance,
diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c
index 197aa1b3f0f..49447477953 100644
--- a/drivers/scsi/mesh.c
+++ b/drivers/scsi/mesh.c
@@ -415,8 +415,7 @@ static void mesh_start_cmd(struct mesh_state *ms, struct scsi_cmnd *cmd)
#if 1
if (DEBUG_TARGET(cmd)) {
int i;
- printk(KERN_DEBUG "mesh_start: %p ser=%lu tgt=%d cmd=",
- cmd, cmd->serial_number, id);
+ printk(KERN_DEBUG "mesh_start: %p tgt=%d cmd=", cmd, id);
for (i = 0; i < cmd->cmd_len; ++i)
printk(" %x", cmd->cmnd[i]);
printk(" use_sg=%d buffer=%p bufflen=%u\n",
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c
index 3346357031e..efa0255491c 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.c
@@ -522,7 +522,8 @@ _base_display_event_data(struct MPT2SAS_ADAPTER *ioc,
desc = "Device Status Change";
break;
case MPI2_EVENT_IR_OPERATION_STATUS:
- desc = "IR Operation Status";
+ if (!ioc->hide_ir_msg)
+ desc = "IR Operation Status";
break;
case MPI2_EVENT_SAS_DISCOVERY:
{
@@ -553,16 +554,20 @@ _base_display_event_data(struct MPT2SAS_ADAPTER *ioc,
desc = "SAS Enclosure Device Status Change";
break;
case MPI2_EVENT_IR_VOLUME:
- desc = "IR Volume";
+ if (!ioc->hide_ir_msg)
+ desc = "IR Volume";
break;
case MPI2_EVENT_IR_PHYSICAL_DISK:
- desc = "IR Physical Disk";
+ if (!ioc->hide_ir_msg)
+ desc = "IR Physical Disk";
break;
case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST:
- desc = "IR Configuration Change List";
+ if (!ioc->hide_ir_msg)
+ desc = "IR Configuration Change List";
break;
case MPI2_EVENT_LOG_ENTRY_ADDED:
- desc = "Log Entry Added";
+ if (!ioc->hide_ir_msg)
+ desc = "Log Entry Added";
break;
}
@@ -616,7 +621,10 @@ _base_sas_log_info(struct MPT2SAS_ADAPTER *ioc , u32 log_info)
originator_str = "PL";
break;
case 2:
- originator_str = "IR";
+ if (!ioc->hide_ir_msg)
+ originator_str = "IR";
+ else
+ originator_str = "WarpDrive";
break;
}
@@ -1508,6 +1516,7 @@ mpt2sas_base_free_smid(struct MPT2SAS_ADAPTER *ioc, u16 smid)
}
ioc->scsi_lookup[i].cb_idx = 0xFF;
ioc->scsi_lookup[i].scmd = NULL;
+ ioc->scsi_lookup[i].direct_io = 0;
list_add_tail(&ioc->scsi_lookup[i].tracker_list,
&ioc->free_list);
spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
@@ -1844,10 +1853,12 @@ _base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc)
printk("), ");
printk("Capabilities=(");
- if (ioc->facts.IOCCapabilities &
- MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID) {
- printk("Raid");
- i++;
+ if (!ioc->hide_ir_msg) {
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID) {
+ printk("Raid");
+ i++;
+ }
}
if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR) {
@@ -3680,6 +3691,7 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
u32 reply_address;
u16 smid;
struct _tr_list *delayed_tr, *delayed_tr_next;
+ u8 hide_flag;
dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
__func__));
@@ -3706,6 +3718,7 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
ioc->scsi_lookup[i].cb_idx = 0xFF;
ioc->scsi_lookup[i].smid = smid;
ioc->scsi_lookup[i].scmd = NULL;
+ ioc->scsi_lookup[i].direct_io = 0;
list_add_tail(&ioc->scsi_lookup[i].tracker_list,
&ioc->free_list);
}
@@ -3766,6 +3779,15 @@ _base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
if (sleep_flag == CAN_SLEEP)
_base_static_config_pages(ioc);
+ if (ioc->wait_for_port_enable_to_complete && ioc->is_warpdrive) {
+ if (ioc->manu_pg10.OEMIdentifier == 0x80) {
+ hide_flag = (u8) (ioc->manu_pg10.OEMSpecificFlags0 &
+ MFG_PAGE10_HIDE_SSDS_MASK);
+ if (hide_flag != MFG_PAGE10_HIDE_SSDS_MASK)
+ ioc->mfg_pg10_hide_flag = hide_flag;
+ }
+ }
+
if (ioc->wait_for_port_enable_to_complete) {
if (diag_buffer_enable != 0)
mpt2sas_enable_diag_buffer(ioc, diag_buffer_enable);
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h
index 500328245f6..2a3c05f6db8 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.h
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.h
@@ -69,11 +69,11 @@
#define MPT2SAS_DRIVER_NAME "mpt2sas"
#define MPT2SAS_AUTHOR "LSI Corporation <DL-MPTFusionLinux@lsi.com>"
#define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver"
-#define MPT2SAS_DRIVER_VERSION "08.100.00.00"
+#define MPT2SAS_DRIVER_VERSION "08.100.00.01"
#define MPT2SAS_MAJOR_VERSION 08
#define MPT2SAS_MINOR_VERSION 100
#define MPT2SAS_BUILD_VERSION 00
-#define MPT2SAS_RELEASE_VERSION 00
+#define MPT2SAS_RELEASE_VERSION 01
/*
* Set MPT2SAS_SG_DEPTH value based on user input.
@@ -189,6 +189,16 @@
#define MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_SSDID 0x0046
/*
+ * WarpDrive Specific Log codes
+ */
+
+#define MPT2_WARPDRIVE_LOGENTRY (0x8002)
+#define MPT2_WARPDRIVE_LC_SSDT (0x41)
+#define MPT2_WARPDRIVE_LC_SSDLW (0x43)
+#define MPT2_WARPDRIVE_LC_SSDLF (0x44)
+#define MPT2_WARPDRIVE_LC_BRMF (0x4D)
+
+/*
* per target private data
*/
#define MPT_TARGET_FLAGS_RAID_COMPONENT 0x01
@@ -199,6 +209,7 @@
* struct MPT2SAS_TARGET - starget private hostdata
* @starget: starget object
* @sas_address: target sas address
+ * @raid_device: raid_device pointer to access volume data
* @handle: device handle
* @num_luns: number luns
* @flags: MPT_TARGET_FLAGS_XXX flags
@@ -208,6 +219,7 @@
struct MPT2SAS_TARGET {
struct scsi_target *starget;
u64 sas_address;
+ struct _raid_device *raid_device;
u16 handle;
int num_luns;
u32 flags;
@@ -215,6 +227,7 @@ struct MPT2SAS_TARGET {
u8 tm_busy;
};
+
/*
* per device private data
*/
@@ -262,6 +275,12 @@ typedef struct _MPI2_CONFIG_PAGE_MAN_10 {
MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_10,
Mpi2ManufacturingPage10_t, MPI2_POINTER pMpi2ManufacturingPage10_t;
+#define MFG_PAGE10_HIDE_SSDS_MASK (0x00000003)
+#define MFG_PAGE10_HIDE_ALL_DISKS (0x00)
+#define MFG_PAGE10_EXPOSE_ALL_DISKS (0x01)
+#define MFG_PAGE10_HIDE_IF_VOL_PRESENT (0x02)
+
+
struct MPT2SAS_DEVICE {
struct MPT2SAS_TARGET *sas_target;
unsigned int lun;
@@ -341,6 +360,7 @@ struct _sas_device {
* @sdev: scsi device struct (volumes are single lun)
* @wwid: unique identifier for the volume
* @handle: device handle
+ * @block_size: Block size of the volume
* @id: target id
* @channel: target channel
* @volume_type: the raid level
@@ -348,20 +368,33 @@ struct _sas_device {
* @num_pds: number of hidden raid components
* @responding: used in _scsih_raid_device_mark_responding
* @percent_complete: resync percent complete
+ * @direct_io_enabled: Whether direct io to PDs are allowed or not
+ * @stripe_exponent: X where 2powX is the stripe sz in blocks
+ * @max_lba: Maximum number of LBA in the volume
+ * @stripe_sz: Stripe Size of the volume
+ * @device_info: Device info of the volume member disk
+ * @pd_handle: Array of handles of the physical drives for direct I/O in le16
*/
+#define MPT_MAX_WARPDRIVE_PDS 8
struct _raid_device {
struct list_head list;
struct scsi_target *starget;
struct scsi_device *sdev;
u64 wwid;
u16 handle;
+ u16 block_sz;
int id;
int channel;
u8 volume_type;
- u32 device_info;
u8 num_pds;
u8 responding;
u8 percent_complete;
+ u8 direct_io_enabled;
+ u8 stripe_exponent;
+ u64 max_lba;
+ u32 stripe_sz;
+ u32 device_info;
+ u16 pd_handle[MPT_MAX_WARPDRIVE_PDS];
};
/**
@@ -470,6 +503,7 @@ struct chain_tracker {
* @smid: system message id
* @scmd: scsi request pointer
* @cb_idx: callback index
+ * @direct_io: To indicate whether I/O is direct (WARPDRIVE)
* @chain_list: list of chains associated to this IO
* @tracker_list: list of free request (ioc->free_list)
*/
@@ -477,14 +511,14 @@ struct scsiio_tracker {
u16 smid;
struct scsi_cmnd *scmd;
u8 cb_idx;
+ u8 direct_io;
struct list_head chain_list;
struct list_head tracker_list;
};
/**
- * struct request_tracker - misc mf request tracker
+ * struct request_tracker - firmware request tracker
* @smid: system message id
- * @scmd: scsi request pointer
* @cb_idx: callback index
* @tracker_list: list of free request (ioc->free_list)
*/
@@ -832,6 +866,11 @@ struct MPT2SAS_ADAPTER {
u32 diagnostic_flags[MPI2_DIAG_BUF_TYPE_COUNT];
u32 ring_buffer_offset;
u32 ring_buffer_sz;
+ u8 is_warpdrive;
+ u8 hide_ir_msg;
+ u8 mfg_pg10_hide_flag;
+ u8 hide_drives;
+
};
typedef u8 (*MPT_CALLBACK)(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c
index 1c6d2b405ee..437c2d94c45 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c
@@ -688,6 +688,13 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc,
goto out;
}
+ /* Check for overflow and wraparound */
+ if (karg.data_sge_offset * 4 > ioc->request_sz ||
+ karg.data_sge_offset > (UINT_MAX / 4)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
/* copy in request message frame from user */
if (copy_from_user(mpi_request, mf, karg.data_sge_offset*4)) {
printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__,
@@ -1034,7 +1041,10 @@ _ctl_getiocinfo(void __user *arg)
__func__));
memset(&karg, 0 , sizeof(karg));
- karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2;
+ if (ioc->is_warpdrive)
+ karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2_SSS6200;
+ else
+ karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2;
if (ioc->pfacts)
karg.port_number = ioc->pfacts[0].PortNumber;
pci_read_config_byte(ioc->pdev, PCI_CLASS_REVISION, &revision);
@@ -1963,7 +1973,7 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state)
Mpi2DiagBufferPostReply_t *mpi_reply;
int rc, i;
u8 buffer_type;
- unsigned long timeleft;
+ unsigned long timeleft, request_size, copy_size;
u16 smid;
u16 ioc_status;
u8 issue_reset = 0;
@@ -1999,6 +2009,8 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state)
return -ENOMEM;
}
+ request_size = ioc->diag_buffer_sz[buffer_type];
+
if ((karg.starting_offset % 4) || (karg.bytes_to_read % 4)) {
printk(MPT2SAS_ERR_FMT "%s: either the starting_offset "
"or bytes_to_read are not 4 byte aligned\n", ioc->name,
@@ -2006,13 +2018,23 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state)
return -EINVAL;
}
+ if (karg.starting_offset > request_size)
+ return -EINVAL;
+
diag_data = (void *)(request_data + karg.starting_offset);
dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: diag_buffer(%p), "
"offset(%d), sz(%d)\n", ioc->name, __func__,
diag_data, karg.starting_offset, karg.bytes_to_read));
+ /* Truncate data on requests that are too large */
+ if ((diag_data + karg.bytes_to_read < diag_data) ||
+ (diag_data + karg.bytes_to_read > request_data + request_size))
+ copy_size = request_size - karg.starting_offset;
+ else
+ copy_size = karg.bytes_to_read;
+
if (copy_to_user((void __user *)uarg->diagnostic_data,
- diag_data, karg.bytes_to_read)) {
+ diag_data, copy_size)) {
printk(MPT2SAS_ERR_FMT "%s: Unable to write "
"mpt_diag_read_buffer_t data @ %p\n", ioc->name,
__func__, diag_data);
diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.h b/drivers/scsi/mpt2sas/mpt2sas_ctl.h
index 69916e46e04..11ff1d5fb8f 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_ctl.h
+++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.h
@@ -133,6 +133,7 @@ struct mpt2_ioctl_pci_info {
#define MPT2_IOCTL_INTERFACE_FC_IP (0x02)
#define MPT2_IOCTL_INTERFACE_SAS (0x03)
#define MPT2_IOCTL_INTERFACE_SAS2 (0x04)
+#define MPT2_IOCTL_INTERFACE_SAS2_SSS6200 (0x05)
#define MPT2_IOCTL_VERSION_LENGTH (32)
/**
diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
index d2064a0533a..f12e02358d6 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
@@ -233,6 +233,9 @@ static struct pci_device_id scsih_pci_table[] = {
PCI_ANY_ID, PCI_ANY_ID },
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_3,
PCI_ANY_ID, PCI_ANY_ID },
+ /* SSS6200 */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SSS6200,
+ PCI_ANY_ID, PCI_ANY_ID },
{0} /* Terminating entry */
};
MODULE_DEVICE_TABLE(pci, scsih_pci_table);
@@ -1256,6 +1259,7 @@ _scsih_target_alloc(struct scsi_target *starget)
sas_target_priv_data->handle = raid_device->handle;
sas_target_priv_data->sas_address = raid_device->wwid;
sas_target_priv_data->flags |= MPT_TARGET_FLAGS_VOLUME;
+ sas_target_priv_data->raid_device = raid_device;
raid_device->starget = starget;
}
spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
@@ -1455,7 +1459,10 @@ static int
_scsih_is_raid(struct device *dev)
{
struct scsi_device *sdev = to_scsi_device(dev);
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(sdev->host);
+ if (ioc->is_warpdrive)
+ return 0;
return (sdev->channel == RAID_CHANNEL) ? 1 : 0;
}
@@ -1480,7 +1487,7 @@ _scsih_get_resync(struct device *dev)
sdev->channel);
spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
- if (!raid_device)
+ if (!raid_device || ioc->is_warpdrive)
goto out;
if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0,
@@ -1640,6 +1647,212 @@ _scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc,
kfree(vol_pg0);
}
+/**
+ * _scsih_disable_ddio - Disable direct I/O for all the volumes
+ * @ioc: per adapter object
+ */
+static void
+_scsih_disable_ddio(struct MPT2SAS_ADAPTER *ioc)
+{
+ Mpi2RaidVolPage1_t vol_pg1;
+ Mpi2ConfigReply_t mpi_reply;
+ struct _raid_device *raid_device;
+ u16 handle;
+ u16 ioc_status;
+
+ handle = 0xFFFF;
+ while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply,
+ &vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ break;
+ handle = le16_to_cpu(vol_pg1.DevHandle);
+ raid_device = _scsih_raid_device_find_by_handle(ioc, handle);
+ if (raid_device)
+ raid_device->direct_io_enabled = 0;
+ }
+ return;
+}
+
+
+/**
+ * _scsih_get_num_volumes - Get number of volumes in the ioc
+ * @ioc: per adapter object
+ */
+static u8
+_scsih_get_num_volumes(struct MPT2SAS_ADAPTER *ioc)
+{
+ Mpi2RaidVolPage1_t vol_pg1;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 handle;
+ u8 vol_cnt = 0;
+ u16 ioc_status;
+
+ handle = 0xFFFF;
+ while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply,
+ &vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ break;
+ vol_cnt++;
+ handle = le16_to_cpu(vol_pg1.DevHandle);
+ }
+ return vol_cnt;
+}
+
+
+/**
+ * _scsih_init_warpdrive_properties - Set properties for warpdrive direct I/O.
+ * @ioc: per adapter object
+ * @raid_device: the raid_device object
+ */
+static void
+_scsih_init_warpdrive_properties(struct MPT2SAS_ADAPTER *ioc,
+ struct _raid_device *raid_device)
+{
+ Mpi2RaidVolPage0_t *vol_pg0;
+ Mpi2RaidPhysDiskPage0_t pd_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 sz;
+ u8 num_pds, count;
+ u64 mb = 1024 * 1024;
+ u64 tb_2 = 2 * mb * mb;
+ u64 capacity;
+ u32 stripe_sz;
+ u8 i, stripe_exp;
+
+ if (!ioc->is_warpdrive)
+ return;
+
+ if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_EXPOSE_ALL_DISKS) {
+ printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled "
+ "globally as drives are exposed\n", ioc->name);
+ return;
+ }
+ if (_scsih_get_num_volumes(ioc) > 1) {
+ _scsih_disable_ddio(ioc);
+ printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled "
+ "globally as number of drives > 1\n", ioc->name);
+ return;
+ }
+ if ((mpt2sas_config_get_number_pds(ioc, raid_device->handle,
+ &num_pds)) || !num_pds) {
+ printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled "
+ "Failure in computing number of drives\n", ioc->name);
+ return;
+ }
+
+ sz = offsetof(Mpi2RaidVolPage0_t, PhysDisk) + (num_pds *
+ sizeof(Mpi2RaidVol0PhysDisk_t));
+ vol_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!vol_pg0) {
+ printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled "
+ "Memory allocation failure for RVPG0\n", ioc->name);
+ return;
+ }
+
+ if ((mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0,
+ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) {
+ printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled "
+ "Failure in retrieving RVPG0\n", ioc->name);
+ kfree(vol_pg0);
+ return;
+ }
+
+ /*
+ * WARPDRIVE:If number of physical disks in a volume exceeds the max pds
+ * assumed for WARPDRIVE, disable direct I/O
+ */
+ if (num_pds > MPT_MAX_WARPDRIVE_PDS) {
+ printk(MPT2SAS_WARN_FMT "WarpDrive : Direct IO is disabled "
+ "for the drive with handle(0x%04x): num_mem=%d, "
+ "max_mem_allowed=%d\n", ioc->name, raid_device->handle,
+ num_pds, MPT_MAX_WARPDRIVE_PDS);
+ kfree(vol_pg0);
+ return;
+ }
+ for (count = 0; count < num_pds; count++) {
+ if (mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply,
+ &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM,
+ vol_pg0->PhysDisk[count].PhysDiskNum) ||
+ pd_pg0.DevHandle == MPT2SAS_INVALID_DEVICE_HANDLE) {
+ printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is "
+ "disabled for the drive with handle(0x%04x) member"
+ "handle retrieval failed for member number=%d\n",
+ ioc->name, raid_device->handle,
+ vol_pg0->PhysDisk[count].PhysDiskNum);
+ goto out_error;
+ }
+ raid_device->pd_handle[count] = le16_to_cpu(pd_pg0.DevHandle);
+ }
+
+ /*
+ * Assumption for WD: Direct I/O is not supported if the volume is
+ * not RAID0, if the stripe size is not 64KB, if the block size is
+ * not 512 and if the volume size is >2TB
+ */
+ if (raid_device->volume_type != MPI2_RAID_VOL_TYPE_RAID0 ||
+ le16_to_cpu(vol_pg0->BlockSize) != 512) {
+ printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled "
+ "for the drive with handle(0x%04x): type=%d, "
+ "s_sz=%uK, blk_size=%u\n", ioc->name,
+ raid_device->handle, raid_device->volume_type,
+ le32_to_cpu(vol_pg0->StripeSize)/2,
+ le16_to_cpu(vol_pg0->BlockSize));
+ goto out_error;
+ }
+
+ capacity = (u64) le16_to_cpu(vol_pg0->BlockSize) *
+ (le64_to_cpu(vol_pg0->MaxLBA) + 1);
+
+ if (capacity > tb_2) {
+ printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled "
+ "for the drive with handle(0x%04x) since drive sz > 2TB\n",
+ ioc->name, raid_device->handle);
+ goto out_error;
+ }
+
+ stripe_sz = le32_to_cpu(vol_pg0->StripeSize);
+ stripe_exp = 0;
+ for (i = 0; i < 32; i++) {
+ if (stripe_sz & 1)
+ break;
+ stripe_exp++;
+ stripe_sz >>= 1;
+ }
+ if (i == 32) {
+ printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled "
+ "for the drive with handle(0x%04x) invalid stripe sz %uK\n",
+ ioc->name, raid_device->handle,
+ le32_to_cpu(vol_pg0->StripeSize)/2);
+ goto out_error;
+ }
+ raid_device->stripe_exponent = stripe_exp;
+ raid_device->direct_io_enabled = 1;
+
+ printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is Enabled for the drive"
+ " with handle(0x%04x)\n", ioc->name, raid_device->handle);
+ /*
+ * WARPDRIVE: Though the following fields are not used for direct IO,
+ * stored for future purpose:
+ */
+ raid_device->max_lba = le64_to_cpu(vol_pg0->MaxLBA);
+ raid_device->stripe_sz = le32_to_cpu(vol_pg0->StripeSize);
+ raid_device->block_sz = le16_to_cpu(vol_pg0->BlockSize);
+
+
+ kfree(vol_pg0);
+ return;
+
+out_error:
+ raid_device->direct_io_enabled = 0;
+ for (count = 0; count < num_pds; count++)
+ raid_device->pd_handle[count] = 0;
+ kfree(vol_pg0);
+ return;
+}
/**
* _scsih_enable_tlr - setting TLR flags
@@ -1710,6 +1923,11 @@ _scsih_slave_configure(struct scsi_device *sdev)
_scsih_get_volume_capabilities(ioc, raid_device);
+ /*
+ * WARPDRIVE: Initialize the required data for Direct IO
+ */
+ _scsih_init_warpdrive_properties(ioc, raid_device);
+
/* RAID Queue Depth Support
* IS volume = underlying qdepth of drive type, either
* MPT2SAS_SAS_QUEUE_DEPTH or MPT2SAS_SATA_QUEUE_DEPTH
@@ -1757,14 +1975,16 @@ _scsih_slave_configure(struct scsi_device *sdev)
break;
}
- sdev_printk(KERN_INFO, sdev, "%s: "
- "handle(0x%04x), wwid(0x%016llx), pd_count(%d), type(%s)\n",
- r_level, raid_device->handle,
- (unsigned long long)raid_device->wwid,
- raid_device->num_pds, ds);
+ if (!ioc->hide_ir_msg)
+ sdev_printk(KERN_INFO, sdev, "%s: handle(0x%04x), "
+ "wwid(0x%016llx), pd_count(%d), type(%s)\n",
+ r_level, raid_device->handle,
+ (unsigned long long)raid_device->wwid,
+ raid_device->num_pds, ds);
_scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT);
/* raid transport support */
- _scsih_set_level(sdev, raid_device);
+ if (!ioc->is_warpdrive)
+ _scsih_set_level(sdev, raid_device);
return 0;
}
@@ -2133,8 +2353,7 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint channel,
switch (type) {
case MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK:
scmd_lookup = _scsih_scsi_lookup_get(ioc, smid_task);
- if (scmd_lookup && (scmd_lookup->serial_number ==
- scmd->serial_number))
+ if (scmd_lookup)
rc = FAILED;
else
rc = SUCCESS;
@@ -2182,16 +2401,20 @@ _scsih_tm_display_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd)
struct MPT2SAS_TARGET *priv_target = starget->hostdata;
struct _sas_device *sas_device = NULL;
unsigned long flags;
+ char *device_str = NULL;
if (!priv_target)
return;
+ if (ioc->hide_ir_msg)
+ device_str = "WarpDrive";
+ else
+ device_str = "volume";
scsi_print_command(scmd);
if (priv_target->flags & MPT_TARGET_FLAGS_VOLUME) {
- starget_printk(KERN_INFO, starget, "volume handle(0x%04x), "
- "volume wwid(0x%016llx)\n",
- priv_target->handle,
- (unsigned long long)priv_target->sas_address);
+ starget_printk(KERN_INFO, starget, "%s handle(0x%04x), "
+ "%s wwid(0x%016llx)\n", device_str, priv_target->handle,
+ device_str, (unsigned long long)priv_target->sas_address);
} else {
spin_lock_irqsave(&ioc->sas_device_lock, flags);
sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
@@ -3130,6 +3353,9 @@ _scsih_check_ir_config_unhide_events(struct MPT2SAS_ADAPTER *ioc,
a = 0;
b = 0;
+ if (ioc->is_warpdrive)
+ return;
+
/* Volume Resets for Deleted or Removed */
element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
for (i = 0; i < event_data->NumElements; i++, element++) {
@@ -3347,6 +3573,105 @@ _scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status)
}
/**
+ * _scsih_scsi_direct_io_get - returns direct io flag
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Returns the smid stored scmd pointer.
+ */
+static inline u8
+_scsih_scsi_direct_io_get(struct MPT2SAS_ADAPTER *ioc, u16 smid)
+{
+ return ioc->scsi_lookup[smid - 1].direct_io;
+}
+
+/**
+ * _scsih_scsi_direct_io_set - sets direct io flag
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @direct_io: Zero or non-zero value to set in the direct_io flag
+ *
+ * Returns Nothing.
+ */
+static inline void
+_scsih_scsi_direct_io_set(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 direct_io)
+{
+ ioc->scsi_lookup[smid - 1].direct_io = direct_io;
+}
+
+
+/**
+ * _scsih_setup_direct_io - setup MPI request for WARPDRIVE Direct I/O
+ * @ioc: per adapter object
+ * @scmd: pointer to scsi command object
+ * @raid_device: pointer to raid device data structure
+ * @mpi_request: pointer to the SCSI_IO reqest message frame
+ * @smid: system request message index
+ *
+ * Returns nothing
+ */
+static void
+_scsih_setup_direct_io(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
+ struct _raid_device *raid_device, Mpi2SCSIIORequest_t *mpi_request,
+ u16 smid)
+{
+ u32 v_lba, p_lba, stripe_off, stripe_unit, column, io_size;
+ u32 stripe_sz, stripe_exp;
+ u8 num_pds, *cdb_ptr, *tmp_ptr, *lba_ptr1, *lba_ptr2;
+ u8 cdb0 = scmd->cmnd[0];
+
+ /*
+ * Try Direct I/O to RAID memeber disks
+ */
+ if (cdb0 == READ_16 || cdb0 == READ_10 ||
+ cdb0 == WRITE_16 || cdb0 == WRITE_10) {
+ cdb_ptr = mpi_request->CDB.CDB32;
+
+ if ((cdb0 < READ_16) || !(cdb_ptr[2] | cdb_ptr[3] | cdb_ptr[4]
+ | cdb_ptr[5])) {
+ io_size = scsi_bufflen(scmd) >> 9;
+ /* get virtual lba */
+ lba_ptr1 = lba_ptr2 = (cdb0 < READ_16) ? &cdb_ptr[2] :
+ &cdb_ptr[6];
+ tmp_ptr = (u8 *)&v_lba + 3;
+ *tmp_ptr-- = *lba_ptr1++;
+ *tmp_ptr-- = *lba_ptr1++;
+ *tmp_ptr-- = *lba_ptr1++;
+ *tmp_ptr = *lba_ptr1;
+
+ if (((u64)v_lba + (u64)io_size - 1) <=
+ (u32)raid_device->max_lba) {
+ stripe_sz = raid_device->stripe_sz;
+ stripe_exp = raid_device->stripe_exponent;
+ stripe_off = v_lba & (stripe_sz - 1);
+
+ /* Check whether IO falls within a stripe */
+ if ((stripe_off + io_size) <= stripe_sz) {
+ num_pds = raid_device->num_pds;
+ p_lba = v_lba >> stripe_exp;
+ stripe_unit = p_lba / num_pds;
+ column = p_lba % num_pds;
+ p_lba = (stripe_unit << stripe_exp) +
+ stripe_off;
+ mpi_request->DevHandle =
+ cpu_to_le16(raid_device->
+ pd_handle[column]);
+ tmp_ptr = (u8 *)&p_lba + 3;
+ *lba_ptr2++ = *tmp_ptr--;
+ *lba_ptr2++ = *tmp_ptr--;
+ *lba_ptr2++ = *tmp_ptr--;
+ *lba_ptr2 = *tmp_ptr;
+ /*
+ * WD: To indicate this I/O is directI/O
+ */
+ _scsih_scsi_direct_io_set(ioc, smid, 1);
+ }
+ }
+ }
+ }
+}
+
+/**
* _scsih_qcmd - main scsi request entry point
* @scmd: pointer to scsi command object
* @done: function pointer to be invoked on completion
@@ -3363,6 +3688,7 @@ _scsih_qcmd_lck(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
struct MPT2SAS_DEVICE *sas_device_priv_data;
struct MPT2SAS_TARGET *sas_target_priv_data;
+ struct _raid_device *raid_device;
Mpi2SCSIIORequest_t *mpi_request;
u32 mpi_control;
u16 smid;
@@ -3424,8 +3750,10 @@ _scsih_qcmd_lck(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
} else
mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
- /* Make sure Device is not raid volume */
- if (!_scsih_is_raid(&scmd->device->sdev_gendev) &&
+ /* Make sure Device is not raid volume.
+ * We do not expose raid functionality to upper layer for warpdrive.
+ */
+ if (!ioc->is_warpdrive && !_scsih_is_raid(&scmd->device->sdev_gendev) &&
sas_is_tlr_enabled(scmd->device) && scmd->cmd_len != 32)
mpi_control |= MPI2_SCSIIO_CONTROL_TLR_ON;
@@ -3473,9 +3801,14 @@ _scsih_qcmd_lck(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
}
}
+ raid_device = sas_target_priv_data->raid_device;
+ if (raid_device && raid_device->direct_io_enabled)
+ _scsih_setup_direct_io(ioc, scmd, raid_device, mpi_request,
+ smid);
+
if (likely(mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST))
mpt2sas_base_put_smid_scsi_io(ioc, smid,
- sas_device_priv_data->sas_target->handle);
+ le16_to_cpu(mpi_request->DevHandle));
else
mpt2sas_base_put_smid_default(ioc, smid);
return 0;
@@ -3540,10 +3873,16 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
unsigned long flags;
struct scsi_target *starget = scmd->device->sdev_target;
struct MPT2SAS_TARGET *priv_target = starget->hostdata;
+ char *device_str = NULL;
if (!priv_target)
return;
+ if (ioc->hide_ir_msg)
+ device_str = "WarpDrive";
+ else
+ device_str = "volume";
+
if (log_info == 0x31170000)
return;
@@ -3660,8 +3999,8 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
scsi_print_command(scmd);
if (priv_target->flags & MPT_TARGET_FLAGS_VOLUME) {
- printk(MPT2SAS_WARN_FMT "\tvolume wwid(0x%016llx)\n", ioc->name,
- (unsigned long long)priv_target->sas_address);
+ printk(MPT2SAS_WARN_FMT "\t%s wwid(0x%016llx)\n", ioc->name,
+ device_str, (unsigned long long)priv_target->sas_address);
} else {
spin_lock_irqsave(&ioc->sas_device_lock, flags);
sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
@@ -3840,6 +4179,20 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
scmd->result = DID_NO_CONNECT << 16;
goto out;
}
+ /*
+ * WARPDRIVE: If direct_io is set then it is directIO,
+ * the failed direct I/O should be redirected to volume
+ */
+ if (_scsih_scsi_direct_io_get(ioc, smid)) {
+ _scsih_scsi_direct_io_set(ioc, smid, 0);
+ memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len);
+ mpi_request->DevHandle =
+ cpu_to_le16(sas_device_priv_data->sas_target->handle);
+ mpt2sas_base_put_smid_scsi_io(ioc, smid,
+ sas_device_priv_data->sas_target->handle);
+ return 0;
+ }
+
/* turning off TLR */
scsi_state = mpi_reply->SCSIState;
@@ -3848,7 +4201,10 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
le32_to_cpu(mpi_reply->ResponseInfo) & 0xFF;
if (!sas_device_priv_data->tlr_snoop_check) {
sas_device_priv_data->tlr_snoop_check++;
- if (!_scsih_is_raid(&scmd->device->sdev_gendev) &&
+ /* Make sure Device is not raid volume.
+ * We do not expose raid functionality to upper layer for warpdrive.
+ */
+ if (!ioc->is_warpdrive && !_scsih_is_raid(&scmd->device->sdev_gendev) &&
sas_is_tlr_enabled(scmd->device) &&
response_code == MPI2_SCSITASKMGMT_RSP_INVALID_FRAME) {
sas_disable_tlr(scmd->device);
@@ -4681,8 +5037,10 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc,
_scsih_ublock_io_device(ioc, sas_device_backup.handle);
- mpt2sas_transport_port_remove(ioc, sas_device_backup.sas_address,
- sas_device_backup.sas_address_parent);
+ if (!ioc->hide_drives)
+ mpt2sas_transport_port_remove(ioc,
+ sas_device_backup.sas_address,
+ sas_device_backup.sas_address_parent);
printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), sas_addr"
"(0x%016llx)\n", ioc->name, sas_device_backup.handle,
@@ -5413,6 +5771,7 @@ _scsih_sas_pd_hide(struct MPT2SAS_ADAPTER *ioc,
&sas_device->volume_wwid);
set_bit(handle, ioc->pd_handles);
_scsih_reprobe_target(sas_device->starget, 1);
+
}
/**
@@ -5591,7 +5950,8 @@ _scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc,
Mpi2EventDataIrConfigChangeList_t *event_data = fw_event->event_data;
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
- if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ if ((ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ && !ioc->hide_ir_msg)
_scsih_sas_ir_config_change_event_debug(ioc, event_data);
#endif
@@ -5614,16 +5974,20 @@ _scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc,
le16_to_cpu(element->VolDevHandle));
break;
case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED:
- _scsih_sas_pd_hide(ioc, element);
+ if (!ioc->is_warpdrive)
+ _scsih_sas_pd_hide(ioc, element);
break;
case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED:
- _scsih_sas_pd_expose(ioc, element);
+ if (!ioc->is_warpdrive)
+ _scsih_sas_pd_expose(ioc, element);
break;
case MPI2_EVENT_IR_CHANGE_RC_HIDE:
- _scsih_sas_pd_add(ioc, element);
+ if (!ioc->is_warpdrive)
+ _scsih_sas_pd_add(ioc, element);
break;
case MPI2_EVENT_IR_CHANGE_RC_UNHIDE:
- _scsih_sas_pd_delete(ioc, element);
+ if (!ioc->is_warpdrive)
+ _scsih_sas_pd_delete(ioc, element);
break;
}
}
@@ -5654,9 +6018,10 @@ _scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc,
handle = le16_to_cpu(event_data->VolDevHandle);
state = le32_to_cpu(event_data->NewValue);
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: handle(0x%04x), "
- "old(0x%08x), new(0x%08x)\n", ioc->name, __func__, handle,
- le32_to_cpu(event_data->PreviousValue), state));
+ if (!ioc->hide_ir_msg)
+ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: handle(0x%04x), "
+ "old(0x%08x), new(0x%08x)\n", ioc->name, __func__, handle,
+ le32_to_cpu(event_data->PreviousValue), state));
switch (state) {
case MPI2_RAID_VOL_STATE_MISSING:
@@ -5736,9 +6101,10 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc,
handle = le16_to_cpu(event_data->PhysDiskDevHandle);
state = le32_to_cpu(event_data->NewValue);
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: handle(0x%04x), "
- "old(0x%08x), new(0x%08x)\n", ioc->name, __func__, handle,
- le32_to_cpu(event_data->PreviousValue), state));
+ if (!ioc->hide_ir_msg)
+ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: handle(0x%04x), "
+ "old(0x%08x), new(0x%08x)\n", ioc->name, __func__, handle,
+ le32_to_cpu(event_data->PreviousValue), state));
switch (state) {
case MPI2_RAID_PD_STATE_ONLINE:
@@ -5747,7 +6113,8 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc,
case MPI2_RAID_PD_STATE_OPTIMAL:
case MPI2_RAID_PD_STATE_HOT_SPARE:
- set_bit(handle, ioc->pd_handles);
+ if (!ioc->is_warpdrive)
+ set_bit(handle, ioc->pd_handles);
spin_lock_irqsave(&ioc->sas_device_lock, flags);
sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
@@ -5851,7 +6218,8 @@ _scsih_sas_ir_operation_status_event(struct MPT2SAS_ADAPTER *ioc,
u16 handle;
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
- if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ if ((ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ && !ioc->hide_ir_msg)
_scsih_sas_ir_operation_status_event_debug(ioc,
event_data);
#endif
@@ -5910,7 +6278,7 @@ static void
_scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
u16 slot, u16 handle)
{
- struct MPT2SAS_TARGET *sas_target_priv_data;
+ struct MPT2SAS_TARGET *sas_target_priv_data = NULL;
struct scsi_target *starget;
struct _sas_device *sas_device;
unsigned long flags;
@@ -5918,7 +6286,7 @@ _scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
spin_lock_irqsave(&ioc->sas_device_lock, flags);
list_for_each_entry(sas_device, &ioc->sas_device_list, list) {
if (sas_device->sas_address == sas_address &&
- sas_device->slot == slot && sas_device->starget) {
+ sas_device->slot == slot) {
sas_device->responding = 1;
starget = sas_device->starget;
if (starget && starget->hostdata) {
@@ -5927,13 +6295,15 @@ _scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
sas_target_priv_data->deleted = 0;
} else
sas_target_priv_data = NULL;
- starget_printk(KERN_INFO, sas_device->starget,
- "handle(0x%04x), sas_addr(0x%016llx), enclosure "
- "logical id(0x%016llx), slot(%d)\n", handle,
- (unsigned long long)sas_device->sas_address,
- (unsigned long long)
- sas_device->enclosure_logical_id,
- sas_device->slot);
+ if (starget)
+ starget_printk(KERN_INFO, starget,
+ "handle(0x%04x), sas_addr(0x%016llx), "
+ "enclosure logical id(0x%016llx), "
+ "slot(%d)\n", handle,
+ (unsigned long long)sas_device->sas_address,
+ (unsigned long long)
+ sas_device->enclosure_logical_id,
+ sas_device->slot);
if (sas_device->handle == handle)
goto out;
printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n",
@@ -6025,6 +6395,12 @@ _scsih_mark_responding_raid_device(struct MPT2SAS_ADAPTER *ioc, u64 wwid,
starget_printk(KERN_INFO, raid_device->starget,
"handle(0x%04x), wwid(0x%016llx)\n", handle,
(unsigned long long)raid_device->wwid);
+ /*
+ * WARPDRIVE: The handles of the PDs might have changed
+ * across the host reset so re-initialize the
+ * required data for Direct IO
+ */
+ _scsih_init_warpdrive_properties(ioc, raid_device);
if (raid_device->handle == handle)
goto out;
printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n",
@@ -6086,18 +6462,20 @@ _scsih_search_responding_raid_devices(struct MPT2SAS_ADAPTER *ioc)
}
/* refresh the pd_handles */
- phys_disk_num = 0xFF;
- memset(ioc->pd_handles, 0, ioc->pd_handles_sz);
- while (!(mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply,
- &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM,
- phys_disk_num))) {
- ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
- MPI2_IOCSTATUS_MASK;
- if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
- break;
- phys_disk_num = pd_pg0.PhysDiskNum;
- handle = le16_to_cpu(pd_pg0.DevHandle);
- set_bit(handle, ioc->pd_handles);
+ if (!ioc->is_warpdrive) {
+ phys_disk_num = 0xFF;
+ memset(ioc->pd_handles, 0, ioc->pd_handles_sz);
+ while (!(mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply,
+ &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM,
+ phys_disk_num))) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ break;
+ phys_disk_num = pd_pg0.PhysDiskNum;
+ handle = le16_to_cpu(pd_pg0.DevHandle);
+ set_bit(handle, ioc->pd_handles);
+ }
}
}
@@ -6243,6 +6621,50 @@ _scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc)
}
/**
+ * _scsih_hide_unhide_sas_devices - add/remove device to/from OS
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+static void
+_scsih_hide_unhide_sas_devices(struct MPT2SAS_ADAPTER *ioc)
+{
+ struct _sas_device *sas_device, *sas_device_next;
+
+ if (!ioc->is_warpdrive || ioc->mfg_pg10_hide_flag !=
+ MFG_PAGE10_HIDE_IF_VOL_PRESENT)
+ return;
+
+ if (ioc->hide_drives) {
+ if (_scsih_get_num_volumes(ioc))
+ return;
+ ioc->hide_drives = 0;
+ list_for_each_entry_safe(sas_device, sas_device_next,
+ &ioc->sas_device_list, list) {
+ if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
+ sas_device->sas_address_parent)) {
+ _scsih_sas_device_remove(ioc, sas_device);
+ } else if (!sas_device->starget) {
+ mpt2sas_transport_port_remove(ioc,
+ sas_device->sas_address,
+ sas_device->sas_address_parent);
+ _scsih_sas_device_remove(ioc, sas_device);
+ }
+ }
+ } else {
+ if (!_scsih_get_num_volumes(ioc))
+ return;
+ ioc->hide_drives = 1;
+ list_for_each_entry_safe(sas_device, sas_device_next,
+ &ioc->sas_device_list, list) {
+ mpt2sas_transport_port_remove(ioc,
+ sas_device->sas_address,
+ sas_device->sas_address_parent);
+ }
+ }
+}
+
+/**
* mpt2sas_scsih_reset_handler - reset callback handler (for scsih)
* @ioc: per adapter object
* @reset_phase: phase
@@ -6326,6 +6748,7 @@ _firmware_event_work(struct work_struct *work)
spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock,
flags);
_scsih_remove_unresponding_sas_devices(ioc);
+ _scsih_hide_unhide_sas_devices(ioc);
return;
}
@@ -6425,6 +6848,53 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index,
(Mpi2EventDataIrVolume_t *)
mpi_reply->EventData);
break;
+ case MPI2_EVENT_LOG_ENTRY_ADDED:
+ {
+ Mpi2EventDataLogEntryAdded_t *log_entry;
+ u32 *log_code;
+
+ if (!ioc->is_warpdrive)
+ break;
+
+ log_entry = (Mpi2EventDataLogEntryAdded_t *)
+ mpi_reply->EventData;
+ log_code = (u32 *)log_entry->LogData;
+
+ if (le16_to_cpu(log_entry->LogEntryQualifier)
+ != MPT2_WARPDRIVE_LOGENTRY)
+ break;
+
+ switch (le32_to_cpu(*log_code)) {
+ case MPT2_WARPDRIVE_LC_SSDT:
+ printk(MPT2SAS_WARN_FMT "WarpDrive Warning: "
+ "IO Throttling has occurred in the WarpDrive "
+ "subsystem. Check WarpDrive documentation for "
+ "additional details.\n", ioc->name);
+ break;
+ case MPT2_WARPDRIVE_LC_SSDLW:
+ printk(MPT2SAS_WARN_FMT "WarpDrive Warning: "
+ "Program/Erase Cycles for the WarpDrive subsystem "
+ "in degraded range. Check WarpDrive documentation "
+ "for additional details.\n", ioc->name);
+ break;
+ case MPT2_WARPDRIVE_LC_SSDLF:
+ printk(MPT2SAS_ERR_FMT "WarpDrive Fatal Error: "
+ "There are no Program/Erase Cycles for the "
+ "WarpDrive subsystem. The storage device will be "
+ "in read-only mode. Check WarpDrive documentation "
+ "for additional details.\n", ioc->name);
+ break;
+ case MPT2_WARPDRIVE_LC_BRMF:
+ printk(MPT2SAS_ERR_FMT "WarpDrive Fatal Error: "
+ "The Backup Rail Monitor has failed on the "
+ "WarpDrive subsystem. Check WarpDrive "
+ "documentation for additional details.\n",
+ ioc->name);
+ break;
+ }
+
+ break;
+ }
case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE:
case MPI2_EVENT_IR_OPERATION_STATUS:
case MPI2_EVENT_SAS_DISCOVERY:
@@ -6583,7 +7053,8 @@ _scsih_ir_shutdown(struct MPT2SAS_ADAPTER *ioc)
mpi_request->Function = MPI2_FUNCTION_RAID_ACTION;
mpi_request->Action = MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED;
- printk(MPT2SAS_INFO_FMT "IR shutdown (sending)\n", ioc->name);
+ if (!ioc->hide_ir_msg)
+ printk(MPT2SAS_INFO_FMT "IR shutdown (sending)\n", ioc->name);
init_completion(&ioc->scsih_cmds.done);
mpt2sas_base_put_smid_default(ioc, smid);
wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ);
@@ -6597,10 +7068,11 @@ _scsih_ir_shutdown(struct MPT2SAS_ADAPTER *ioc)
if (ioc->scsih_cmds.status & MPT2_CMD_REPLY_VALID) {
mpi_reply = ioc->scsih_cmds.reply;
- printk(MPT2SAS_INFO_FMT "IR shutdown (complete): "
- "ioc_status(0x%04x), loginfo(0x%08x)\n",
- ioc->name, le16_to_cpu(mpi_reply->IOCStatus),
- le32_to_cpu(mpi_reply->IOCLogInfo));
+ if (!ioc->hide_ir_msg)
+ printk(MPT2SAS_INFO_FMT "IR shutdown (complete): "
+ "ioc_status(0x%04x), loginfo(0x%08x)\n",
+ ioc->name, le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo));
}
out:
@@ -6759,6 +7231,9 @@ _scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc)
spin_lock_irqsave(&ioc->sas_device_lock, flags);
list_move_tail(&sas_device->list, &ioc->sas_device_list);
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ if (ioc->hide_drives)
+ return;
if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
sas_device->sas_address_parent)) {
_scsih_sas_device_remove(ioc, sas_device);
@@ -6812,6 +7287,9 @@ _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc)
list_move_tail(&sas_device->list, &ioc->sas_device_list);
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (ioc->hide_drives)
+ continue;
+
if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
sas_device->sas_address_parent)) {
_scsih_sas_device_remove(ioc, sas_device);
@@ -6882,6 +7360,11 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ioc->id = mpt_ids++;
sprintf(ioc->name, "%s%d", MPT2SAS_DRIVER_NAME, ioc->id);
ioc->pdev = pdev;
+ if (id->device == MPI2_MFGPAGE_DEVID_SSS6200) {
+ ioc->is_warpdrive = 1;
+ ioc->hide_ir_msg = 1;
+ } else
+ ioc->mfg_pg10_hide_flag = MFG_PAGE10_EXPOSE_ALL_DISKS;
ioc->scsi_io_cb_idx = scsi_io_cb_idx;
ioc->tm_cb_idx = tm_cb_idx;
ioc->ctl_cb_idx = ctl_cb_idx;
@@ -6947,6 +7430,20 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
}
ioc->wait_for_port_enable_to_complete = 0;
+ if (ioc->is_warpdrive) {
+ if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_EXPOSE_ALL_DISKS)
+ ioc->hide_drives = 0;
+ else if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_HIDE_ALL_DISKS)
+ ioc->hide_drives = 1;
+ else {
+ if (_scsih_get_num_volumes(ioc))
+ ioc->hide_drives = 1;
+ else
+ ioc->hide_drives = 0;
+ }
+ } else
+ ioc->hide_drives = 0;
+
_scsih_probe_devices(ioc);
return 0;
diff --git a/drivers/scsi/mvsas/Kconfig b/drivers/scsi/mvsas/Kconfig
index 6de7af27e50..c82b012aba3 100644
--- a/drivers/scsi/mvsas/Kconfig
+++ b/drivers/scsi/mvsas/Kconfig
@@ -3,6 +3,7 @@
#
# Copyright 2007 Red Hat, Inc.
# Copyright 2008 Marvell. <kewei@marvell.com>
+# Copyright 2009-20011 Marvell. <yuxiangl@marvell.com>
#
# This file is licensed under GPLv2.
#
diff --git a/drivers/scsi/mvsas/Makefile b/drivers/scsi/mvsas/Makefile
index ffbf759e46f..87b231a5bd5 100644
--- a/drivers/scsi/mvsas/Makefile
+++ b/drivers/scsi/mvsas/Makefile
@@ -3,6 +3,7 @@
#
# Copyright 2007 Red Hat, Inc.
# Copyright 2008 Marvell. <kewei@marvell.com>
+# Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
#
# This file is licensed under GPLv2.
#
diff --git a/drivers/scsi/mvsas/mv_64xx.c b/drivers/scsi/mvsas/mv_64xx.c
index afc7f6f3a13..13c96048139 100644
--- a/drivers/scsi/mvsas/mv_64xx.c
+++ b/drivers/scsi/mvsas/mv_64xx.c
@@ -3,6 +3,7 @@
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
+ * Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
*
* This file is licensed under GPLv2.
*
diff --git a/drivers/scsi/mvsas/mv_64xx.h b/drivers/scsi/mvsas/mv_64xx.h
index 42e947d9795..545889bd975 100644
--- a/drivers/scsi/mvsas/mv_64xx.h
+++ b/drivers/scsi/mvsas/mv_64xx.h
@@ -3,6 +3,7 @@
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
+ * Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
*
* This file is licensed under GPLv2.
*
diff --git a/drivers/scsi/mvsas/mv_94xx.c b/drivers/scsi/mvsas/mv_94xx.c
index eed4c5c7201..78162c3c36e 100644
--- a/drivers/scsi/mvsas/mv_94xx.c
+++ b/drivers/scsi/mvsas/mv_94xx.c
@@ -3,6 +3,7 @@
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
+ * Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
*
* This file is licensed under GPLv2.
*
diff --git a/drivers/scsi/mvsas/mv_94xx.h b/drivers/scsi/mvsas/mv_94xx.h
index 23ed9b16466..8835befe2c0 100644
--- a/drivers/scsi/mvsas/mv_94xx.h
+++ b/drivers/scsi/mvsas/mv_94xx.h
@@ -3,6 +3,7 @@
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
+ * Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
*
* This file is licensed under GPLv2.
*
diff --git a/drivers/scsi/mvsas/mv_chips.h b/drivers/scsi/mvsas/mv_chips.h
index a67e1c4172f..1753a6fc42d 100644
--- a/drivers/scsi/mvsas/mv_chips.h
+++ b/drivers/scsi/mvsas/mv_chips.h
@@ -3,6 +3,7 @@
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
+ * Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
*
* This file is licensed under GPLv2.
*
diff --git a/drivers/scsi/mvsas/mv_defs.h b/drivers/scsi/mvsas/mv_defs.h
index 1849da1f030..bc00c940743 100644
--- a/drivers/scsi/mvsas/mv_defs.h
+++ b/drivers/scsi/mvsas/mv_defs.h
@@ -3,6 +3,7 @@
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
+ * Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
*
* This file is licensed under GPLv2.
*
@@ -34,6 +35,8 @@ enum chip_flavors {
chip_6485,
chip_9480,
chip_9180,
+ chip_9445,
+ chip_9485,
chip_1300,
chip_1320
};
diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c
index 938d045e418..90b636611cd 100644
--- a/drivers/scsi/mvsas/mv_init.c
+++ b/drivers/scsi/mvsas/mv_init.c
@@ -3,6 +3,7 @@
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
+ * Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
*
* This file is licensed under GPLv2.
*
@@ -25,13 +26,24 @@
#include "mv_sas.h"
+static int lldd_max_execute_num = 1;
+module_param_named(collector, lldd_max_execute_num, int, S_IRUGO);
+MODULE_PARM_DESC(collector, "\n"
+ "\tIf greater than one, tells the SAS Layer to run in Task Collector\n"
+ "\tMode. If 1 or 0, tells the SAS Layer to run in Direct Mode.\n"
+ "\tThe mvsas SAS LLDD supports both modes.\n"
+ "\tDefault: 1 (Direct Mode).\n");
+
static struct scsi_transport_template *mvs_stt;
+struct kmem_cache *mvs_task_list_cache;
static const struct mvs_chip_info mvs_chips[] = {
[chip_6320] = { 1, 2, 0x400, 17, 16, 9, &mvs_64xx_dispatch, },
[chip_6440] = { 1, 4, 0x400, 17, 16, 9, &mvs_64xx_dispatch, },
[chip_6485] = { 1, 8, 0x800, 33, 32, 10, &mvs_64xx_dispatch, },
[chip_9180] = { 2, 4, 0x800, 17, 64, 9, &mvs_94xx_dispatch, },
[chip_9480] = { 2, 4, 0x800, 17, 64, 9, &mvs_94xx_dispatch, },
+ [chip_9445] = { 1, 4, 0x800, 17, 64, 11, &mvs_94xx_dispatch, },
+ [chip_9485] = { 2, 4, 0x800, 17, 64, 11, &mvs_94xx_dispatch, },
[chip_1300] = { 1, 4, 0x400, 17, 16, 9, &mvs_64xx_dispatch, },
[chip_1320] = { 2, 4, 0x800, 17, 64, 9, &mvs_94xx_dispatch, },
};
@@ -107,7 +119,6 @@ static void __devinit mvs_phy_init(struct mvs_info *mvi, int phy_id)
static void mvs_free(struct mvs_info *mvi)
{
- int i;
struct mvs_wq *mwq;
int slot_nr;
@@ -119,12 +130,8 @@ static void mvs_free(struct mvs_info *mvi)
else
slot_nr = MVS_SLOTS;
- for (i = 0; i < mvi->tags_num; i++) {
- struct mvs_slot_info *slot = &mvi->slot_info[i];
- if (slot->buf)
- dma_free_coherent(mvi->dev, MVS_SLOT_BUF_SZ,
- slot->buf, slot->buf_dma);
- }
+ if (mvi->dma_pool)
+ pci_pool_destroy(mvi->dma_pool);
if (mvi->tx)
dma_free_coherent(mvi->dev,
@@ -213,6 +220,7 @@ static irqreturn_t mvs_interrupt(int irq, void *opaque)
static int __devinit mvs_alloc(struct mvs_info *mvi, struct Scsi_Host *shost)
{
int i = 0, slot_nr;
+ char pool_name[32];
if (mvi->flags & MVF_FLAG_SOC)
slot_nr = MVS_SOC_SLOTS;
@@ -272,18 +280,14 @@ static int __devinit mvs_alloc(struct mvs_info *mvi, struct Scsi_Host *shost)
if (!mvi->bulk_buffer)
goto err_out;
#endif
- for (i = 0; i < slot_nr; i++) {
- struct mvs_slot_info *slot = &mvi->slot_info[i];
-
- slot->buf = dma_alloc_coherent(mvi->dev, MVS_SLOT_BUF_SZ,
- &slot->buf_dma, GFP_KERNEL);
- if (!slot->buf) {
- printk(KERN_DEBUG"failed to allocate slot->buf.\n");
+ sprintf(pool_name, "%s%d", "mvs_dma_pool", mvi->id);
+ mvi->dma_pool = pci_pool_create(pool_name, mvi->pdev, MVS_SLOT_BUF_SZ, 16, 0);
+ if (!mvi->dma_pool) {
+ printk(KERN_DEBUG "failed to create dma pool %s.\n", pool_name);
goto err_out;
- }
- memset(slot->buf, 0, MVS_SLOT_BUF_SZ);
- ++mvi->tags_num;
}
+ mvi->tags_num = slot_nr;
+
/* Initialize tags */
mvs_tag_init(mvi);
return 0;
@@ -484,7 +488,7 @@ static void __devinit mvs_post_sas_ha_init(struct Scsi_Host *shost,
sha->num_phys = nr_core * chip_info->n_phy;
- sha->lldd_max_execute_num = 1;
+ sha->lldd_max_execute_num = lldd_max_execute_num;
if (mvi->flags & MVF_FLAG_SOC)
can_queue = MVS_SOC_CAN_QUEUE;
@@ -670,6 +674,24 @@ static struct pci_device_id __devinitdata mvs_pci_table[] = {
{ PCI_VDEVICE(TTI, 0x2740), chip_9480 },
{ PCI_VDEVICE(TTI, 0x2744), chip_9480 },
{ PCI_VDEVICE(TTI, 0x2760), chip_9480 },
+ {
+ .vendor = 0x1b4b,
+ .device = 0x9445,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = 0x9480,
+ .class = 0,
+ .class_mask = 0,
+ .driver_data = chip_9445,
+ },
+ {
+ .vendor = 0x1b4b,
+ .device = 0x9485,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = 0x9480,
+ .class = 0,
+ .class_mask = 0,
+ .driver_data = chip_9485,
+ },
{ } /* terminate list */
};
@@ -690,6 +712,14 @@ static int __init mvs_init(void)
if (!mvs_stt)
return -ENOMEM;
+ mvs_task_list_cache = kmem_cache_create("mvs_task_list", sizeof(struct mvs_task_list),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!mvs_task_list_cache) {
+ rc = -ENOMEM;
+ mv_printk("%s: mvs_task_list_cache alloc failed! \n", __func__);
+ goto err_out;
+ }
+
rc = pci_register_driver(&mvs_pci_driver);
if (rc)
@@ -706,6 +736,7 @@ static void __exit mvs_exit(void)
{
pci_unregister_driver(&mvs_pci_driver);
sas_release_transport(mvs_stt);
+ kmem_cache_destroy(mvs_task_list_cache);
}
module_init(mvs_init);
diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c
index adedaa916ec..0ef27425c44 100644
--- a/drivers/scsi/mvsas/mv_sas.c
+++ b/drivers/scsi/mvsas/mv_sas.c
@@ -3,6 +3,7 @@
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
+ * Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
*
* This file is licensed under GPLv2.
*
@@ -862,178 +863,286 @@ static int mvs_task_prep_ssp(struct mvs_info *mvi,
}
#define DEV_IS_GONE(mvi_dev) ((!mvi_dev || (mvi_dev->dev_type == NO_DEVICE)))
-static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags,
- struct completion *completion,int is_tmf,
- struct mvs_tmf_task *tmf)
+static int mvs_task_prep(struct sas_task *task, struct mvs_info *mvi, int is_tmf,
+ struct mvs_tmf_task *tmf, int *pass)
{
struct domain_device *dev = task->dev;
- struct mvs_device *mvi_dev = (struct mvs_device *)dev->lldd_dev;
- struct mvs_info *mvi = mvi_dev->mvi_info;
+ struct mvs_device *mvi_dev = dev->lldd_dev;
struct mvs_task_exec_info tei;
- struct sas_task *t = task;
struct mvs_slot_info *slot;
- u32 tag = 0xdeadbeef, rc, n_elem = 0;
- u32 n = num, pass = 0;
- unsigned long flags = 0, flags_libsas = 0;
+ u32 tag = 0xdeadbeef, n_elem = 0;
+ int rc = 0;
if (!dev->port) {
- struct task_status_struct *tsm = &t->task_status;
+ struct task_status_struct *tsm = &task->task_status;
tsm->resp = SAS_TASK_UNDELIVERED;
tsm->stat = SAS_PHY_DOWN;
+ /*
+ * libsas will use dev->port, should
+ * not call task_done for sata
+ */
if (dev->dev_type != SATA_DEV)
- t->task_done(t);
- return 0;
+ task->task_done(task);
+ return rc;
}
- spin_lock_irqsave(&mvi->lock, flags);
- do {
- dev = t->dev;
- mvi_dev = dev->lldd_dev;
- if (DEV_IS_GONE(mvi_dev)) {
- if (mvi_dev)
- mv_dprintk("device %d not ready.\n",
- mvi_dev->device_id);
- else
- mv_dprintk("device %016llx not ready.\n",
- SAS_ADDR(dev->sas_addr));
+ if (DEV_IS_GONE(mvi_dev)) {
+ if (mvi_dev)
+ mv_dprintk("device %d not ready.\n",
+ mvi_dev->device_id);
+ else
+ mv_dprintk("device %016llx not ready.\n",
+ SAS_ADDR(dev->sas_addr));
rc = SAS_PHY_DOWN;
- goto out_done;
- }
+ return rc;
+ }
+ tei.port = dev->port->lldd_port;
+ if (tei.port && !tei.port->port_attached && !tmf) {
+ if (sas_protocol_ata(task->task_proto)) {
+ struct task_status_struct *ts = &task->task_status;
+ mv_dprintk("SATA/STP port %d does not attach"
+ "device.\n", dev->port->id);
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_PHY_DOWN;
- if (dev->port->id >= mvi->chip->n_phy)
- tei.port = &mvi->port[dev->port->id - mvi->chip->n_phy];
- else
- tei.port = &mvi->port[dev->port->id];
-
- if (tei.port && !tei.port->port_attached) {
- if (sas_protocol_ata(t->task_proto)) {
- struct task_status_struct *ts = &t->task_status;
-
- mv_dprintk("port %d does not"
- "attached device.\n", dev->port->id);
- ts->stat = SAS_PROTO_RESPONSE;
- ts->stat = SAS_PHY_DOWN;
- spin_unlock_irqrestore(dev->sata_dev.ap->lock,
- flags_libsas);
- spin_unlock_irqrestore(&mvi->lock, flags);
- t->task_done(t);
- spin_lock_irqsave(&mvi->lock, flags);
- spin_lock_irqsave(dev->sata_dev.ap->lock,
- flags_libsas);
- if (n > 1)
- t = list_entry(t->list.next,
- struct sas_task, list);
- continue;
- } else {
- struct task_status_struct *ts = &t->task_status;
- ts->resp = SAS_TASK_UNDELIVERED;
- ts->stat = SAS_PHY_DOWN;
- t->task_done(t);
- if (n > 1)
- t = list_entry(t->list.next,
- struct sas_task, list);
- continue;
- }
- }
+ task->task_done(task);
- if (!sas_protocol_ata(t->task_proto)) {
- if (t->num_scatter) {
- n_elem = dma_map_sg(mvi->dev,
- t->scatter,
- t->num_scatter,
- t->data_dir);
- if (!n_elem) {
- rc = -ENOMEM;
- goto err_out;
- }
- }
} else {
- n_elem = t->num_scatter;
+ struct task_status_struct *ts = &task->task_status;
+ mv_dprintk("SAS port %d does not attach"
+ "device.\n", dev->port->id);
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_PHY_DOWN;
+ task->task_done(task);
}
+ return rc;
+ }
- rc = mvs_tag_alloc(mvi, &tag);
- if (rc)
- goto err_out;
+ if (!sas_protocol_ata(task->task_proto)) {
+ if (task->num_scatter) {
+ n_elem = dma_map_sg(mvi->dev,
+ task->scatter,
+ task->num_scatter,
+ task->data_dir);
+ if (!n_elem) {
+ rc = -ENOMEM;
+ goto prep_out;
+ }
+ }
+ } else {
+ n_elem = task->num_scatter;
+ }
- slot = &mvi->slot_info[tag];
+ rc = mvs_tag_alloc(mvi, &tag);
+ if (rc)
+ goto err_out;
+ slot = &mvi->slot_info[tag];
- t->lldd_task = NULL;
- slot->n_elem = n_elem;
- slot->slot_tag = tag;
- memset(slot->buf, 0, MVS_SLOT_BUF_SZ);
+ task->lldd_task = NULL;
+ slot->n_elem = n_elem;
+ slot->slot_tag = tag;
+
+ slot->buf = pci_pool_alloc(mvi->dma_pool, GFP_ATOMIC, &slot->buf_dma);
+ if (!slot->buf)
+ goto err_out_tag;
+ memset(slot->buf, 0, MVS_SLOT_BUF_SZ);
+
+ tei.task = task;
+ tei.hdr = &mvi->slot[tag];
+ tei.tag = tag;
+ tei.n_elem = n_elem;
+ switch (task->task_proto) {
+ case SAS_PROTOCOL_SMP:
+ rc = mvs_task_prep_smp(mvi, &tei);
+ break;
+ case SAS_PROTOCOL_SSP:
+ rc = mvs_task_prep_ssp(mvi, &tei, is_tmf, tmf);
+ break;
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+ rc = mvs_task_prep_ata(mvi, &tei);
+ break;
+ default:
+ dev_printk(KERN_ERR, mvi->dev,
+ "unknown sas_task proto: 0x%x\n",
+ task->task_proto);
+ rc = -EINVAL;
+ break;
+ }
- tei.task = t;
- tei.hdr = &mvi->slot[tag];
- tei.tag = tag;
- tei.n_elem = n_elem;
- switch (t->task_proto) {
- case SAS_PROTOCOL_SMP:
- rc = mvs_task_prep_smp(mvi, &tei);
- break;
- case SAS_PROTOCOL_SSP:
- rc = mvs_task_prep_ssp(mvi, &tei, is_tmf, tmf);
- break;
- case SAS_PROTOCOL_SATA:
- case SAS_PROTOCOL_STP:
- case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
- rc = mvs_task_prep_ata(mvi, &tei);
- break;
- default:
- dev_printk(KERN_ERR, mvi->dev,
- "unknown sas_task proto: 0x%x\n",
- t->task_proto);
- rc = -EINVAL;
- break;
- }
+ if (rc) {
+ mv_dprintk("rc is %x\n", rc);
+ goto err_out_slot_buf;
+ }
+ slot->task = task;
+ slot->port = tei.port;
+ task->lldd_task = slot;
+ list_add_tail(&slot->entry, &tei.port->list);
+ spin_lock(&task->task_state_lock);
+ task->task_state_flags |= SAS_TASK_AT_INITIATOR;
+ spin_unlock(&task->task_state_lock);
- if (rc) {
- mv_dprintk("rc is %x\n", rc);
- goto err_out_tag;
- }
- slot->task = t;
- slot->port = tei.port;
- t->lldd_task = slot;
- list_add_tail(&slot->entry, &tei.port->list);
- /* TODO: select normal or high priority */
- spin_lock(&t->task_state_lock);
- t->task_state_flags |= SAS_TASK_AT_INITIATOR;
- spin_unlock(&t->task_state_lock);
-
- mvs_hba_memory_dump(mvi, tag, t->task_proto);
- mvi_dev->running_req++;
- ++pass;
- mvi->tx_prod = (mvi->tx_prod + 1) & (MVS_CHIP_SLOT_SZ - 1);
- if (n > 1)
- t = list_entry(t->list.next, struct sas_task, list);
- if (likely(pass))
- MVS_CHIP_DISP->start_delivery(mvi, (mvi->tx_prod - 1) &
- (MVS_CHIP_SLOT_SZ - 1));
+ mvs_hba_memory_dump(mvi, tag, task->task_proto);
+ mvi_dev->running_req++;
+ ++(*pass);
+ mvi->tx_prod = (mvi->tx_prod + 1) & (MVS_CHIP_SLOT_SZ - 1);
- } while (--n);
- rc = 0;
- goto out_done;
+ return rc;
+err_out_slot_buf:
+ pci_pool_free(mvi->dma_pool, slot->buf, slot->buf_dma);
err_out_tag:
mvs_tag_free(mvi, tag);
err_out:
- dev_printk(KERN_ERR, mvi->dev, "mvsas exec failed[%d]!\n", rc);
- if (!sas_protocol_ata(t->task_proto))
+ dev_printk(KERN_ERR, mvi->dev, "mvsas prep failed[%d]!\n", rc);
+ if (!sas_protocol_ata(task->task_proto))
if (n_elem)
- dma_unmap_sg(mvi->dev, t->scatter, n_elem,
- t->data_dir);
-out_done:
+ dma_unmap_sg(mvi->dev, task->scatter, n_elem,
+ task->data_dir);
+prep_out:
+ return rc;
+}
+
+static struct mvs_task_list *mvs_task_alloc_list(int *num, gfp_t gfp_flags)
+{
+ struct mvs_task_list *first = NULL;
+
+ for (; *num > 0; --*num) {
+ struct mvs_task_list *mvs_list = kmem_cache_zalloc(mvs_task_list_cache, gfp_flags);
+
+ if (!mvs_list)
+ break;
+
+ INIT_LIST_HEAD(&mvs_list->list);
+ if (!first)
+ first = mvs_list;
+ else
+ list_add_tail(&mvs_list->list, &first->list);
+
+ }
+
+ return first;
+}
+
+static inline void mvs_task_free_list(struct mvs_task_list *mvs_list)
+{
+ LIST_HEAD(list);
+ struct list_head *pos, *a;
+ struct mvs_task_list *mlist = NULL;
+
+ __list_add(&list, mvs_list->list.prev, &mvs_list->list);
+
+ list_for_each_safe(pos, a, &list) {
+ list_del_init(pos);
+ mlist = list_entry(pos, struct mvs_task_list, list);
+ kmem_cache_free(mvs_task_list_cache, mlist);
+ }
+}
+
+static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags,
+ struct completion *completion, int is_tmf,
+ struct mvs_tmf_task *tmf)
+{
+ struct domain_device *dev = task->dev;
+ struct mvs_info *mvi = NULL;
+ u32 rc = 0;
+ u32 pass = 0;
+ unsigned long flags = 0;
+
+ mvi = ((struct mvs_device *)task->dev->lldd_dev)->mvi_info;
+
+ if ((dev->dev_type == SATA_DEV) && (dev->sata_dev.ap != NULL))
+ spin_unlock_irq(dev->sata_dev.ap->lock);
+
+ spin_lock_irqsave(&mvi->lock, flags);
+ rc = mvs_task_prep(task, mvi, is_tmf, tmf, &pass);
+ if (rc)
+ dev_printk(KERN_ERR, mvi->dev, "mvsas exec failed[%d]!\n", rc);
+
+ if (likely(pass))
+ MVS_CHIP_DISP->start_delivery(mvi, (mvi->tx_prod - 1) &
+ (MVS_CHIP_SLOT_SZ - 1));
spin_unlock_irqrestore(&mvi->lock, flags);
+
+ if ((dev->dev_type == SATA_DEV) && (dev->sata_dev.ap != NULL))
+ spin_lock_irq(dev->sata_dev.ap->lock);
+
+ return rc;
+}
+
+static int mvs_collector_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags,
+ struct completion *completion, int is_tmf,
+ struct mvs_tmf_task *tmf)
+{
+ struct domain_device *dev = task->dev;
+ struct mvs_prv_info *mpi = dev->port->ha->lldd_ha;
+ struct mvs_info *mvi = NULL;
+ struct sas_task *t = task;
+ struct mvs_task_list *mvs_list = NULL, *a;
+ LIST_HEAD(q);
+ int pass[2] = {0};
+ u32 rc = 0;
+ u32 n = num;
+ unsigned long flags = 0;
+
+ mvs_list = mvs_task_alloc_list(&n, gfp_flags);
+ if (n) {
+ printk(KERN_ERR "%s: mvs alloc list failed.\n", __func__);
+ rc = -ENOMEM;
+ goto free_list;
+ }
+
+ __list_add(&q, mvs_list->list.prev, &mvs_list->list);
+
+ list_for_each_entry(a, &q, list) {
+ a->task = t;
+ t = list_entry(t->list.next, struct sas_task, list);
+ }
+
+ list_for_each_entry(a, &q , list) {
+
+ t = a->task;
+ mvi = ((struct mvs_device *)t->dev->lldd_dev)->mvi_info;
+
+ spin_lock_irqsave(&mvi->lock, flags);
+ rc = mvs_task_prep(t, mvi, is_tmf, tmf, &pass[mvi->id]);
+ if (rc)
+ dev_printk(KERN_ERR, mvi->dev, "mvsas exec failed[%d]!\n", rc);
+ spin_unlock_irqrestore(&mvi->lock, flags);
+ }
+
+ if (likely(pass[0]))
+ MVS_CHIP_DISP->start_delivery(mpi->mvi[0],
+ (mpi->mvi[0]->tx_prod - 1) & (MVS_CHIP_SLOT_SZ - 1));
+
+ if (likely(pass[1]))
+ MVS_CHIP_DISP->start_delivery(mpi->mvi[1],
+ (mpi->mvi[1]->tx_prod - 1) & (MVS_CHIP_SLOT_SZ - 1));
+
+ list_del_init(&q);
+
+free_list:
+ if (mvs_list)
+ mvs_task_free_list(mvs_list);
+
return rc;
}
int mvs_queue_command(struct sas_task *task, const int num,
gfp_t gfp_flags)
{
- return mvs_task_exec(task, num, gfp_flags, NULL, 0, NULL);
+ struct mvs_device *mvi_dev = task->dev->lldd_dev;
+ struct sas_ha_struct *sas = mvi_dev->mvi_info->sas;
+
+ if (sas->lldd_max_execute_num < 2)
+ return mvs_task_exec(task, num, gfp_flags, NULL, 0, NULL);
+ else
+ return mvs_collector_task_exec(task, num, gfp_flags, NULL, 0, NULL);
}
static void mvs_slot_free(struct mvs_info *mvi, u32 rx_desc)
@@ -1067,6 +1176,11 @@ static void mvs_slot_task_free(struct mvs_info *mvi, struct sas_task *task,
/* do nothing */
break;
}
+
+ if (slot->buf) {
+ pci_pool_free(mvi->dma_pool, slot->buf, slot->buf_dma);
+ slot->buf = NULL;
+ }
list_del_init(&slot->entry);
task->lldd_task = NULL;
slot->task = NULL;
@@ -1255,6 +1369,7 @@ static void mvs_port_notify_formed(struct asd_sas_phy *sas_phy, int lock)
spin_lock_irqsave(&mvi->lock, flags);
port->port_attached = 1;
phy->port = port;
+ sas_port->lldd_port = port;
if (phy->phy_type & PORT_TYPE_SAS) {
port->wide_port_phymap = sas_port->phy_mask;
mv_printk("set wide port phy map %x\n", sas_port->phy_mask);
diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h
index 77ddc7c1e5f..1367d8b9350 100644
--- a/drivers/scsi/mvsas/mv_sas.h
+++ b/drivers/scsi/mvsas/mv_sas.h
@@ -3,6 +3,7 @@
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
+ * Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
*
* This file is licensed under GPLv2.
*
@@ -67,6 +68,7 @@ extern struct mvs_tgt_initiator mvs_tgt;
extern struct mvs_info *tgt_mvi;
extern const struct mvs_dispatch mvs_64xx_dispatch;
extern const struct mvs_dispatch mvs_94xx_dispatch;
+extern struct kmem_cache *mvs_task_list_cache;
#define DEV_IS_EXPANDER(type) \
((type == EDGE_DEV) || (type == FANOUT_DEV))
@@ -341,6 +343,7 @@ struct mvs_info {
dma_addr_t bulk_buffer_dma;
#define TRASH_BUCKET_SIZE 0x20000
#endif
+ void *dma_pool;
struct mvs_slot_info slot_info[0];
};
@@ -367,6 +370,11 @@ struct mvs_task_exec_info {
int n_elem;
};
+struct mvs_task_list {
+ struct sas_task *task;
+ struct list_head list;
+};
+
/******************** function prototype *********************/
void mvs_get_sas_addr(void *buf, u32 buflen);
diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c
index 835d8d66e69..4b3b4755945 100644
--- a/drivers/scsi/ncr53c8xx.c
+++ b/drivers/scsi/ncr53c8xx.c
@@ -8147,7 +8147,7 @@ static int ncr53c8xx_abort(struct scsi_cmnd *cmd)
unsigned long flags;
struct scsi_cmnd *done_list;
- printk("ncr53c8xx_abort: command pid %lu\n", cmd->serial_number);
+ printk("ncr53c8xx_abort\n");
NCR_LOCK_NCB(np, flags);
diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c
index 96d5ad0c1e4..7f636b11828 100644
--- a/drivers/scsi/pmcraid.c
+++ b/drivers/scsi/pmcraid.c
@@ -3814,6 +3814,9 @@ static long pmcraid_ioctl_passthrough(
rc = -EFAULT;
goto out_free_buffer;
}
+ } else if (request_size < 0) {
+ rc = -EINVAL;
+ goto out_free_buffer;
}
/* check if we have any additional command parameters */
diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c
index 8ba5744c267..d838205ab16 100644
--- a/drivers/scsi/qla1280.c
+++ b/drivers/scsi/qla1280.c
@@ -4066,7 +4066,7 @@ __qla1280_print_scsi_cmd(struct scsi_cmnd *cmd)
} */
printk(" tag=%d, transfersize=0x%x \n",
cmd->tag, cmd->transfersize);
- printk(" Pid=%li, SP=0x%p\n", cmd->serial_number, CMD_SP(cmd));
+ printk(" SP=0x%p\n", CMD_SP(cmd));
printk(" underflow size = 0x%x, direction=0x%x\n",
cmd->underflow, cmd->sc_data_direction);
}
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index d3e58d763b4..532313e0725 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
@@ -496,8 +496,8 @@ do_read:
offset = 0;
}
- rval = qla2x00_read_sfp(vha, ha->sfp_data_dma, addr, offset,
- SFP_BLOCK_SIZE);
+ rval = qla2x00_read_sfp(vha, ha->sfp_data_dma, ha->sfp_data,
+ addr, offset, SFP_BLOCK_SIZE, 0);
if (rval != QLA_SUCCESS) {
qla_printk(KERN_WARNING, ha,
"Unable to read SFP data (%x/%x/%x).\n", rval,
@@ -628,12 +628,12 @@ qla2x00_sysfs_write_edc(struct file *filp, struct kobject *kobj,
memcpy(ha->edc_data, &buf[8], len);
- rval = qla2x00_write_edc(vha, dev, adr, ha->edc_data_dma,
- ha->edc_data, len, opt);
+ rval = qla2x00_write_sfp(vha, ha->edc_data_dma, ha->edc_data,
+ dev, adr, len, opt);
if (rval != QLA_SUCCESS) {
DEBUG2(qla_printk(KERN_INFO, ha,
"Unable to write EDC (%x) %02x:%02x:%04x:%02x:%02x.\n",
- rval, dev, adr, opt, len, *buf));
+ rval, dev, adr, opt, len, buf[8]));
return 0;
}
@@ -685,8 +685,8 @@ qla2x00_sysfs_write_edc_status(struct file *filp, struct kobject *kobj,
return -EINVAL;
memset(ha->edc_data, 0, len);
- rval = qla2x00_read_edc(vha, dev, adr, ha->edc_data_dma,
- ha->edc_data, len, opt);
+ rval = qla2x00_read_sfp(vha, ha->edc_data_dma, ha->edc_data,
+ dev, adr, len, opt);
if (rval != QLA_SUCCESS) {
DEBUG2(qla_printk(KERN_INFO, ha,
"Unable to write EDC status (%x) %02x:%02x:%04x:%02x.\n",
@@ -1568,7 +1568,7 @@ qla2x00_dev_loss_tmo_callbk(struct fc_rport *rport)
/* Now that the rport has been deleted, set the fcport state to
FCS_DEVICE_DEAD */
- atomic_set(&fcport->state, FCS_DEVICE_DEAD);
+ qla2x00_set_fcport_state(fcport, FCS_DEVICE_DEAD);
/*
* Transport has effectively 'deleted' the rport, clear
@@ -1877,14 +1877,15 @@ qla24xx_vport_delete(struct fc_vport *fc_vport)
scsi_remove_host(vha->host);
+ /* Allow timer to run to drain queued items, when removing vp */
+ qla24xx_deallocate_vp_id(vha);
+
if (vha->timer_active) {
qla2x00_vp_stop_timer(vha);
DEBUG15(printk(KERN_INFO "scsi(%ld): timer for the vport[%d]"
" = %p has stopped\n", vha->host_no, vha->vp_idx, vha));
}
- qla24xx_deallocate_vp_id(vha);
-
/* No pending activities shall be there on the vha now */
DEBUG(msleep(random32()%10)); /* Just to see if something falls on
* the net we have placed below */
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index 903b0586ded..8c10e2c4928 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
diff --git a/drivers/scsi/qla2xxx/qla_bsg.h b/drivers/scsi/qla2xxx/qla_bsg.h
index 074a999c701..0f0f54e35f0 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.h
+++ b/drivers/scsi/qla2xxx/qla_bsg.h
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
index 09614114825..c53719a9a74 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.c
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
diff --git a/drivers/scsi/qla2xxx/qla_dbg.h b/drivers/scsi/qla2xxx/qla_dbg.h
index b74e6b5743d..930414541ec 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.h
+++ b/drivers/scsi/qla2xxx/qla_dbg.h
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index ee20353c855..cc5a79259d3 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
@@ -1717,6 +1717,14 @@ typedef struct fc_port {
#define FCS_DEVICE_LOST 3
#define FCS_ONLINE 4
+static const char * const port_state_str[] = {
+ "Unknown",
+ "UNCONFIGURED",
+ "DEAD",
+ "LOST",
+ "ONLINE"
+};
+
/*
* FC port flags.
*/
diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c
index 6271353e8c5..a5a4e1275bf 100644
--- a/drivers/scsi/qla2xxx/qla_dfs.c
+++ b/drivers/scsi/qla2xxx/qla_dfs.c
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
index f5ba09c8a66..691783abfb6 100644
--- a/drivers/scsi/qla2xxx/qla_fw.h
+++ b/drivers/scsi/qla2xxx/qla_fw.h
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
@@ -416,8 +416,7 @@ struct cmd_type_6 {
uint8_t vp_index;
uint32_t fcp_data_dseg_address[2]; /* Data segment address. */
- uint16_t fcp_data_dseg_len; /* Data segment length. */
- uint16_t reserved_1; /* MUST be set to 0. */
+ uint32_t fcp_data_dseg_len; /* Data segment length. */
};
#define COMMAND_TYPE_7 0x18 /* Command Type 7 entry */
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index d48326ee3f6..0b381224ae4 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
@@ -39,6 +39,8 @@ extern int qla81xx_load_risc(scsi_qla_host_t *, uint32_t *);
extern int qla2x00_perform_loop_resync(scsi_qla_host_t *);
extern int qla2x00_loop_resync(scsi_qla_host_t *);
+extern int qla2x00_find_new_loop_id(scsi_qla_host_t *, fc_port_t *);
+
extern int qla2x00_fabric_login(scsi_qla_host_t *, fc_port_t *, uint16_t *);
extern int qla2x00_local_device_login(scsi_qla_host_t *, fc_port_t *);
@@ -100,6 +102,8 @@ extern int ql2xgffidenable;
extern int ql2xenabledif;
extern int ql2xenablehba_err_chk;
extern int ql2xtargetreset;
+extern int ql2xdontresethba;
+extern unsigned int ql2xmaxlun;
extern int qla2x00_loop_reset(scsi_qla_host_t *);
extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int);
@@ -319,15 +323,12 @@ extern int
qla2x00_disable_fce_trace(scsi_qla_host_t *, uint64_t *, uint64_t *);
extern int
-qla2x00_read_sfp(scsi_qla_host_t *, dma_addr_t, uint16_t, uint16_t, uint16_t);
-
-extern int
-qla2x00_read_edc(scsi_qla_host_t *, uint16_t, uint16_t, dma_addr_t,
- uint8_t *, uint16_t, uint16_t);
+qla2x00_read_sfp(scsi_qla_host_t *, dma_addr_t, uint8_t *,
+ uint16_t, uint16_t, uint16_t, uint16_t);
extern int
-qla2x00_write_edc(scsi_qla_host_t *, uint16_t, uint16_t, dma_addr_t,
- uint8_t *, uint16_t, uint16_t);
+qla2x00_write_sfp(scsi_qla_host_t *, dma_addr_t, uint8_t *,
+ uint16_t, uint16_t, uint16_t, uint16_t);
extern int
qla2x00_set_idma_speed(scsi_qla_host_t *, uint16_t, uint16_t, uint16_t *);
@@ -549,7 +550,6 @@ extern int qla82xx_wr_32(struct qla_hw_data *, ulong, u32);
extern int qla82xx_rd_32(struct qla_hw_data *, ulong);
extern int qla82xx_rdmem(struct qla_hw_data *, u64, void *, int);
extern int qla82xx_wrmem(struct qla_hw_data *, u64, void *, int);
-extern void qla82xx_rom_unlock(struct qla_hw_data *);
/* ISP 8021 IDC */
extern void qla82xx_clear_drv_active(struct qla_hw_data *);
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index 74a91b6dfc6..8cd9066ad90 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 8575808dbae..920b76bfbb9 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
@@ -35,8 +35,6 @@ static int qla2x00_fabric_dev_login(scsi_qla_host_t *, fc_port_t *,
static int qla2x00_restart_isp(scsi_qla_host_t *);
-static int qla2x00_find_new_loop_id(scsi_qla_host_t *, fc_port_t *);
-
static struct qla_chip_state_84xx *qla84xx_get_chip(struct scsi_qla_host *);
static int qla84xx_init_chip(scsi_qla_host_t *);
static int qla25xx_init_queues(struct qla_hw_data *);
@@ -385,8 +383,18 @@ qla2x00_async_login_done(struct scsi_qla_host *vha, fc_port_t *fcport,
switch (data[0]) {
case MBS_COMMAND_COMPLETE:
+ /*
+ * Driver must validate login state - If PRLI not complete,
+ * force a relogin attempt via implicit LOGO, PLOGI, and PRLI
+ * requests.
+ */
+ rval = qla2x00_get_port_database(vha, fcport, 0);
+ if (rval != QLA_SUCCESS) {
+ qla2x00_post_async_logout_work(vha, fcport, NULL);
+ qla2x00_post_async_login_work(vha, fcport, NULL);
+ break;
+ }
if (fcport->flags & FCF_FCP2_DEVICE) {
- fcport->flags |= FCF_ASYNC_SENT;
qla2x00_post_async_adisc_work(vha, fcport, data);
break;
}
@@ -397,7 +405,7 @@ qla2x00_async_login_done(struct scsi_qla_host *vha, fc_port_t *fcport,
if (data[1] & QLA_LOGIO_LOGIN_RETRIED)
set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
else
- qla2x00_mark_device_lost(vha, fcport, 1, 1);
+ qla2x00_mark_device_lost(vha, fcport, 1, 0);
break;
case MBS_PORT_ID_USED:
fcport->loop_id = data[1];
@@ -409,7 +417,7 @@ qla2x00_async_login_done(struct scsi_qla_host *vha, fc_port_t *fcport,
rval = qla2x00_find_new_loop_id(vha, fcport);
if (rval != QLA_SUCCESS) {
fcport->flags &= ~FCF_ASYNC_SENT;
- qla2x00_mark_device_lost(vha, fcport, 1, 1);
+ qla2x00_mark_device_lost(vha, fcport, 1, 0);
break;
}
qla2x00_post_async_login_work(vha, fcport, NULL);
@@ -441,7 +449,7 @@ qla2x00_async_adisc_done(struct scsi_qla_host *vha, fc_port_t *fcport,
if (data[1] & QLA_LOGIO_LOGIN_RETRIED)
set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
else
- qla2x00_mark_device_lost(vha, fcport, 1, 1);
+ qla2x00_mark_device_lost(vha, fcport, 1, 0);
return;
}
@@ -2536,7 +2544,7 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags)
fcport->vp_idx = vha->vp_idx;
fcport->port_type = FCT_UNKNOWN;
fcport->loop_id = FC_NO_LOOP_ID;
- atomic_set(&fcport->state, FCS_UNCONFIGURED);
+ qla2x00_set_fcport_state(fcport, FCS_UNCONFIGURED);
fcport->supported_classes = FC_COS_UNSPECIFIED;
return fcport;
@@ -2722,7 +2730,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha)
"loop_id=0x%04x\n",
vha->host_no, fcport->loop_id));
- atomic_set(&fcport->state, FCS_DEVICE_LOST);
+ qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST);
}
}
@@ -2934,7 +2942,7 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
qla2x00_iidma_fcport(vha, fcport);
qla24xx_update_fcport_fcp_prio(vha, fcport);
qla2x00_reg_remote_port(vha, fcport);
- atomic_set(&fcport->state, FCS_ONLINE);
+ qla2x00_set_fcport_state(fcport, FCS_ONLINE);
}
/*
@@ -3391,7 +3399,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
* Context:
* Kernel context.
*/
-static int
+int
qla2x00_find_new_loop_id(scsi_qla_host_t *vha, fc_port_t *dev)
{
int rval;
@@ -5202,7 +5210,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
}
/* Reset Initialization control block */
- memset(icb, 0, sizeof(struct init_cb_81xx));
+ memset(icb, 0, ha->init_cb_size);
/* Copy 1st segment. */
dptr1 = (uint8_t *)icb;
@@ -5427,6 +5435,13 @@ qla82xx_restart_isp(scsi_qla_host_t *vha)
ha->isp_abort_cnt = 0;
clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
+ /* Update the firmware version */
+ qla2x00_get_fw_version(vha, &ha->fw_major_version,
+ &ha->fw_minor_version, &ha->fw_subminor_version,
+ &ha->fw_attributes, &ha->fw_memory_size,
+ ha->mpi_version, &ha->mpi_capabilities,
+ ha->phy_version);
+
if (ha->fce) {
ha->flags.fce_enabled = 1;
memset(ha->fce, 0,
@@ -5508,26 +5523,26 @@ qla81xx_update_fw_options(scsi_qla_host_t *vha)
*
* Return:
* non-zero (if found)
- * 0 (if not found)
+ * -1 (if not found)
*
* Context:
* Kernel context
*/
-uint8_t
+static int
qla24xx_get_fcp_prio(scsi_qla_host_t *vha, fc_port_t *fcport)
{
int i, entries;
uint8_t pid_match, wwn_match;
- uint8_t priority;
+ int priority;
uint32_t pid1, pid2;
uint64_t wwn1, wwn2;
struct qla_fcp_prio_entry *pri_entry;
struct qla_hw_data *ha = vha->hw;
if (!ha->fcp_prio_cfg || !ha->flags.fcp_prio_enabled)
- return 0;
+ return -1;
- priority = 0;
+ priority = -1;
entries = ha->fcp_prio_cfg->num_entries;
pri_entry = &ha->fcp_prio_cfg->entry[0];
@@ -5610,7 +5625,7 @@ int
qla24xx_update_fcport_fcp_prio(scsi_qla_host_t *vha, fc_port_t *fcport)
{
int ret;
- uint8_t priority;
+ int priority;
uint16_t mb[5];
if (fcport->port_type != FCT_TARGET ||
@@ -5618,6 +5633,9 @@ qla24xx_update_fcport_fcp_prio(scsi_qla_host_t *vha, fc_port_t *fcport)
return QLA_FUNCTION_FAILED;
priority = qla24xx_get_fcp_prio(vha, fcport);
+ if (priority < 0)
+ return QLA_FUNCTION_FAILED;
+
ret = qla24xx_set_fcp_prio(vha, fcport->loop_id, priority, mb);
if (ret == QLA_SUCCESS)
fcport->fcp_prio = priority;
diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
index 48f97a92e33..4c8167e11f6 100644
--- a/drivers/scsi/qla2xxx/qla_inline.h
+++ b/drivers/scsi/qla2xxx/qla_inline.h
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
@@ -83,3 +83,22 @@ qla2x00_clean_dsd_pool(struct qla_hw_data *ha, srb_t *sp)
}
INIT_LIST_HEAD(&((struct crc_context *)sp->ctx)->dsd_list);
}
+
+static inline void
+qla2x00_set_fcport_state(fc_port_t *fcport, int state)
+{
+ int old_state;
+
+ old_state = atomic_read(&fcport->state);
+ atomic_set(&fcport->state, state);
+
+ /* Don't print state transitions during initial allocation of fcport */
+ if (old_state && old_state != state) {
+ DEBUG(qla_printk(KERN_WARNING, fcport->vha->hw,
+ "scsi(%ld): FCPort state transitioned from %s to %s - "
+ "portid=%02x%02x%02x.\n", fcport->vha->host_no,
+ port_state_str[old_state], port_state_str[state],
+ fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa));
+ }
+}
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index d78d5896fc3..7bac3cd109d 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 712518d0512..9c0f0e3389e 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
@@ -843,7 +843,10 @@ qla2x00_process_completed_request(struct scsi_qla_host *vha,
qla_printk(KERN_WARNING, ha,
"Invalid SCSI completion handle %d.\n", index);
- set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ if (IS_QLA82XX(ha))
+ set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
+ else
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
return;
}
@@ -861,7 +864,10 @@ qla2x00_process_completed_request(struct scsi_qla_host *vha,
qla_printk(KERN_WARNING, ha,
"Invalid ISP SCSI completion handle\n");
- set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ if (IS_QLA82XX(ha))
+ set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
+ else
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
}
}
@@ -878,7 +884,10 @@ qla2x00_get_sp_from_handle(scsi_qla_host_t *vha, const char *func,
if (index >= MAX_OUTSTANDING_COMMANDS) {
qla_printk(KERN_WARNING, ha,
"%s: Invalid completion handle (%x).\n", func, index);
- set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ if (IS_QLA82XX(ha))
+ set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
+ else
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
goto done;
}
sp = req->outstanding_cmds[index];
@@ -1564,7 +1573,10 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
"scsi(%ld): Invalid status handle (0x%x).\n", vha->host_no,
sts->handle);
- set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ if (IS_QLA82XX(ha))
+ set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
+ else
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
qla2xxx_wake_dpc(vha);
return;
}
@@ -1794,12 +1806,13 @@ out:
if (logit)
DEBUG2(qla_printk(KERN_INFO, ha,
"scsi(%ld:%d:%d) FCP command status: 0x%x-0x%x (0x%x) "
- "oxid=0x%x cdb=%02x%02x%02x len=0x%x "
+ "portid=%02x%02x%02x oxid=0x%x cdb=%02x%02x%02x len=0x%x "
"rsp_info=0x%x resid=0x%x fw_resid=0x%x\n", vha->host_no,
cp->device->id, cp->device->lun, comp_status, scsi_status,
- cp->result, ox_id, cp->cmnd[0],
- cp->cmnd[1], cp->cmnd[2], scsi_bufflen(cp), rsp_info_len,
- resid_len, fw_resid_len));
+ cp->result, fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa, ox_id, cp->cmnd[0], cp->cmnd[1],
+ cp->cmnd[2], scsi_bufflen(cp), rsp_info_len, resid_len,
+ fw_resid_len));
if (rsp->status_srb == NULL)
qla2x00_sp_compl(ha, sp);
@@ -1908,13 +1921,17 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt)
qla2x00_sp_compl(ha, sp);
} else if (pkt->entry_type == COMMAND_A64_TYPE || pkt->entry_type ==
- COMMAND_TYPE || pkt->entry_type == COMMAND_TYPE_7) {
+ COMMAND_TYPE || pkt->entry_type == COMMAND_TYPE_7
+ || pkt->entry_type == COMMAND_TYPE_6) {
DEBUG2(printk("scsi(%ld): Error entry - invalid handle\n",
- vha->host_no));
+ vha->host_no));
qla_printk(KERN_WARNING, ha,
- "Error entry - invalid handle\n");
+ "Error entry - invalid handle\n");
- set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ if (IS_QLA82XX(ha))
+ set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
+ else
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
qla2xxx_wake_dpc(vha);
}
}
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 34893397ac8..c26f0acdfec 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
@@ -1261,11 +1261,12 @@ qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt)
/* Check for logged in state. */
if (pd24->current_login_state != PDS_PRLI_COMPLETE &&
pd24->last_login_state != PDS_PRLI_COMPLETE) {
- DEBUG2(printk("%s(%ld): Unable to verify "
- "login-state (%x/%x) for loop_id %x\n",
- __func__, vha->host_no,
- pd24->current_login_state,
- pd24->last_login_state, fcport->loop_id));
+ DEBUG2(qla_printk(KERN_WARNING, ha,
+ "scsi(%ld): Unable to verify login-state (%x/%x) "
+ " - portid=%02x%02x%02x.\n", vha->host_no,
+ pd24->current_login_state, pd24->last_login_state,
+ fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa));
rval = QLA_FUNCTION_FAILED;
goto gpd_error_out;
}
@@ -1289,6 +1290,12 @@ qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt)
/* Check for logged in state. */
if (pd->master_state != PD_STATE_PORT_LOGGED_IN &&
pd->slave_state != PD_STATE_PORT_LOGGED_IN) {
+ DEBUG2(qla_printk(KERN_WARNING, ha,
+ "scsi(%ld): Unable to verify login-state (%x/%x) "
+ " - portid=%02x%02x%02x.\n", vha->host_no,
+ pd->master_state, pd->slave_state,
+ fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa));
rval = QLA_FUNCTION_FAILED;
goto gpd_error_out;
}
@@ -1883,7 +1890,8 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
lg->handle = MAKE_HANDLE(req->id, lg->handle);
lg->nport_handle = cpu_to_le16(loop_id);
lg->control_flags =
- __constant_cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO);
+ __constant_cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO|
+ LCF_FREE_NPORT);
lg->port_id[0] = al_pa;
lg->port_id[1] = area;
lg->port_id[2] = domain;
@@ -2362,7 +2370,7 @@ qla24xx_abort_command(srb_t *sp)
abt->entry_count = 1;
abt->handle = MAKE_HANDLE(req->id, abt->handle);
abt->nport_handle = cpu_to_le16(fcport->loop_id);
- abt->handle_to_abort = handle;
+ abt->handle_to_abort = MAKE_HANDLE(req->id, handle);
abt->port_id[0] = fcport->d_id.b.al_pa;
abt->port_id[1] = fcport->d_id.b.area;
abt->port_id[2] = fcport->d_id.b.domain;
@@ -2779,44 +2787,6 @@ qla2x00_disable_fce_trace(scsi_qla_host_t *vha, uint64_t *wr, uint64_t *rd)
}
int
-qla2x00_read_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint16_t addr,
- uint16_t off, uint16_t count)
-{
- int rval;
- mbx_cmd_t mc;
- mbx_cmd_t *mcp = &mc;
-
- if (!IS_FWI2_CAPABLE(vha->hw))
- return QLA_FUNCTION_FAILED;
-
- DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
-
- mcp->mb[0] = MBC_READ_SFP;
- mcp->mb[1] = addr;
- mcp->mb[2] = MSW(sfp_dma);
- mcp->mb[3] = LSW(sfp_dma);
- mcp->mb[6] = MSW(MSD(sfp_dma));
- mcp->mb[7] = LSW(MSD(sfp_dma));
- mcp->mb[8] = count;
- mcp->mb[9] = off;
- mcp->mb[10] = 0;
- mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
- mcp->in_mb = MBX_0;
- mcp->tov = MBX_TOV_SECONDS;
- mcp->flags = 0;
- rval = qla2x00_mailbox_command(vha, mcp);
-
- if (rval != QLA_SUCCESS) {
- DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__,
- vha->host_no, rval, mcp->mb[0]));
- } else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
- }
-
- return rval;
-}
-
-int
qla2x00_get_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id,
uint16_t *port_speed, uint16_t *mb)
{
@@ -3581,15 +3551,22 @@ qla81xx_restart_mpi_firmware(scsi_qla_host_t *vha)
}
int
-qla2x00_read_edc(scsi_qla_host_t *vha, uint16_t dev, uint16_t adr,
- dma_addr_t sfp_dma, uint8_t *sfp, uint16_t len, uint16_t opt)
+qla2x00_read_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint8_t *sfp,
+ uint16_t dev, uint16_t off, uint16_t len, uint16_t opt)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!IS_FWI2_CAPABLE(ha))
+ return QLA_FUNCTION_FAILED;
DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+ if (len == 1)
+ opt |= BIT_0;
+
mcp->mb[0] = MBC_READ_SFP;
mcp->mb[1] = dev;
mcp->mb[2] = MSW(sfp_dma);
@@ -3597,17 +3574,16 @@ qla2x00_read_edc(scsi_qla_host_t *vha, uint16_t dev, uint16_t adr,
mcp->mb[6] = MSW(MSD(sfp_dma));
mcp->mb[7] = LSW(MSD(sfp_dma));
mcp->mb[8] = len;
- mcp->mb[9] = adr;
+ mcp->mb[9] = off;
mcp->mb[10] = opt;
mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
- mcp->in_mb = MBX_0;
+ mcp->in_mb = MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
rval = qla2x00_mailbox_command(vha, mcp);
if (opt & BIT_0)
- if (sfp)
- *sfp = mcp->mb[8];
+ *sfp = mcp->mb[1];
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__,
@@ -3620,18 +3596,24 @@ qla2x00_read_edc(scsi_qla_host_t *vha, uint16_t dev, uint16_t adr,
}
int
-qla2x00_write_edc(scsi_qla_host_t *vha, uint16_t dev, uint16_t adr,
- dma_addr_t sfp_dma, uint8_t *sfp, uint16_t len, uint16_t opt)
+qla2x00_write_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint8_t *sfp,
+ uint16_t dev, uint16_t off, uint16_t len, uint16_t opt)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!IS_FWI2_CAPABLE(ha))
+ return QLA_FUNCTION_FAILED;
DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
+ if (len == 1)
+ opt |= BIT_0;
+
if (opt & BIT_0)
- if (sfp)
- len = *sfp;
+ len = *sfp;
mcp->mb[0] = MBC_WRITE_SFP;
mcp->mb[1] = dev;
@@ -3640,10 +3622,10 @@ qla2x00_write_edc(scsi_qla_host_t *vha, uint16_t dev, uint16_t adr,
mcp->mb[6] = MSW(MSD(sfp_dma));
mcp->mb[7] = LSW(MSD(sfp_dma));
mcp->mb[8] = len;
- mcp->mb[9] = adr;
+ mcp->mb[9] = off;
mcp->mb[10] = opt;
mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
- mcp->in_mb = MBX_0;
+ mcp->in_mb = MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
rval = qla2x00_mailbox_command(vha, mcp);
@@ -4160,63 +4142,32 @@ int
qla2x00_get_thermal_temp(scsi_qla_host_t *vha, uint16_t *temp, uint16_t *frac)
{
int rval;
- mbx_cmd_t mc;
- mbx_cmd_t *mcp = &mc;
+ uint8_t byte;
struct qla_hw_data *ha = vha->hw;
- DEBUG11(printk(KERN_INFO "%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk(KERN_INFO "%s(%ld): entered.\n", __func__, vha->host_no));
- /* High bits. */
- mcp->mb[0] = MBC_READ_SFP;
- mcp->mb[1] = 0x98;
- mcp->mb[2] = 0;
- mcp->mb[3] = 0;
- mcp->mb[6] = 0;
- mcp->mb[7] = 0;
- mcp->mb[8] = 1;
- mcp->mb[9] = 0x01;
- mcp->mb[10] = BIT_13|BIT_0;
- mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
- mcp->in_mb = MBX_1|MBX_0;
- mcp->tov = MBX_TOV_SECONDS;
- mcp->flags = 0;
- rval = qla2x00_mailbox_command(vha, mcp);
+ /* Integer part */
+ rval = qla2x00_read_sfp(vha, 0, &byte, 0x98, 0x01, 1, BIT_13|BIT_0);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk(KERN_WARNING
- "%s(%ld): failed=%x (%x).\n", __func__,
- vha->host_no, rval, mcp->mb[0]));
+ "%s(%ld): failed=%x.\n", __func__, vha->host_no, rval));
ha->flags.thermal_supported = 0;
goto fail;
}
- *temp = mcp->mb[1] & 0xFF;
+ *temp = byte;
- /* Low bits. */
- mcp->mb[0] = MBC_READ_SFP;
- mcp->mb[1] = 0x98;
- mcp->mb[2] = 0;
- mcp->mb[3] = 0;
- mcp->mb[6] = 0;
- mcp->mb[7] = 0;
- mcp->mb[8] = 1;
- mcp->mb[9] = 0x10;
- mcp->mb[10] = BIT_13|BIT_0;
- mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
- mcp->in_mb = MBX_1|MBX_0;
- mcp->tov = MBX_TOV_SECONDS;
- mcp->flags = 0;
- rval = qla2x00_mailbox_command(vha, mcp);
+ /* Fraction part */
+ rval = qla2x00_read_sfp(vha, 0, &byte, 0x98, 0x10, 1, BIT_13|BIT_0);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk(KERN_WARNING
- "%s(%ld): failed=%x (%x).\n", __func__,
- vha->host_no, rval, mcp->mb[0]));
+ "%s(%ld): failed=%x.\n", __func__, vha->host_no, rval));
ha->flags.thermal_supported = 0;
goto fail;
}
- *frac = ((mcp->mb[1] & 0xFF) >> 6) * 25;
+ *frac = (byte >> 6) * 25;
- if (rval == QLA_SUCCESS)
- DEBUG11(printk(KERN_INFO
- "%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk(KERN_INFO "%s(%ld): done.\n", __func__, vha->host_no));
fail:
return rval;
}
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index 2b69392a71a..5e343919aca 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
@@ -136,7 +136,7 @@ qla2x00_mark_vp_devices_dead(scsi_qla_host_t *vha)
vha->host_no, fcport->loop_id, fcport->vp_idx));
qla2x00_mark_device_lost(vha, fcport, 0, 0);
- atomic_set(&fcport->state, FCS_UNCONFIGURED);
+ qla2x00_set_fcport_state(fcport, FCS_UNCONFIGURED);
}
}
@@ -456,7 +456,7 @@ qla24xx_create_vhost(struct fc_vport *fc_vport)
else
host->max_cmd_len = MAX_CMDSZ;
host->max_channel = MAX_BUSES - 1;
- host->max_lun = MAX_LUNS;
+ host->max_lun = ql2xmaxlun;
host->unique_id = host->host_no;
host->max_id = MAX_TARGETS_2200;
host->transportt = qla2xxx_transport_vport_template;
diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c
index 455fe134d31..e1138bcc834 100644
--- a/drivers/scsi/qla2xxx/qla_nx.c
+++ b/drivers/scsi/qla2xxx/qla_nx.c
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
@@ -844,6 +844,12 @@ qla82xx_rom_lock(struct qla_hw_data *ha)
return 0;
}
+static void
+qla82xx_rom_unlock(struct qla_hw_data *ha)
+{
+ qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_UNLOCK));
+}
+
static int
qla82xx_wait_rom_busy(struct qla_hw_data *ha)
{
@@ -924,7 +930,7 @@ qla82xx_rom_fast_read(struct qla_hw_data *ha, int addr, int *valp)
return -1;
}
ret = qla82xx_do_rom_fast_read(ha, addr, valp);
- qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_UNLOCK));
+ qla82xx_rom_unlock(ha);
return ret;
}
@@ -1056,7 +1062,7 @@ qla82xx_write_flash_dword(struct qla_hw_data *ha, uint32_t flashaddr,
ret = qla82xx_flash_wait_write_finish(ha);
done_write:
- qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_UNLOCK));
+ qla82xx_rom_unlock(ha);
return ret;
}
@@ -1081,12 +1087,26 @@ qla82xx_pinit_from_rom(scsi_qla_host_t *vha)
/* Halt all the indiviual PEGs and other blocks of the ISP */
qla82xx_rom_lock(ha);
- /* mask all niu interrupts */
+ /* disable all I2Q */
+ qla82xx_wr_32(ha, QLA82XX_CRB_I2Q + 0x10, 0x0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_I2Q + 0x14, 0x0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_I2Q + 0x18, 0x0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_I2Q + 0x1c, 0x0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_I2Q + 0x20, 0x0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_I2Q + 0x24, 0x0);
+
+ /* disable all niu interrupts */
qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0x40, 0xff);
/* disable xge rx/tx */
qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0x70000, 0x00);
/* disable xg1 rx/tx */
qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0x80000, 0x00);
+ /* disable sideband mac */
+ qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0x90000, 0x00);
+ /* disable ap0 mac */
+ qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0xa0000, 0x00);
+ /* disable ap1 mac */
+ qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0xb0000, 0x00);
/* halt sre */
val = qla82xx_rd_32(ha, QLA82XX_CRB_SRE + 0x1000);
@@ -1101,6 +1121,7 @@ qla82xx_pinit_from_rom(scsi_qla_host_t *vha)
qla82xx_wr_32(ha, QLA82XX_CRB_TIMER + 0x10, 0x0);
qla82xx_wr_32(ha, QLA82XX_CRB_TIMER + 0x18, 0x0);
qla82xx_wr_32(ha, QLA82XX_CRB_TIMER + 0x100, 0x0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_TIMER + 0x200, 0x0);
/* halt pegs */
qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_0 + 0x3c, 1);
@@ -1108,9 +1129,9 @@ qla82xx_pinit_from_rom(scsi_qla_host_t *vha)
qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_2 + 0x3c, 1);
qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_3 + 0x3c, 1);
qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_4 + 0x3c, 1);
+ msleep(20);
/* big hammer */
- msleep(1000);
if (test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))
/* don't reset CAM block on reset */
qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0xfeffffff);
@@ -1129,7 +1150,7 @@ qla82xx_pinit_from_rom(scsi_qla_host_t *vha)
qla82xx_wr_32(ha, QLA82XX_CRB_QDR_NET + 0xe4, val);
msleep(20);
- qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_UNLOCK));
+ qla82xx_rom_unlock(ha);
/* Read the signature value from the flash.
* Offset 0: Contain signature (0xcafecafe)
@@ -2395,9 +2416,13 @@ qla82xx_load_fw(scsi_qla_host_t *vha)
if (qla82xx_fw_load_from_flash(ha) == QLA_SUCCESS) {
qla_printk(KERN_ERR, ha,
- "Firmware loaded successfully from flash\n");
+ "Firmware loaded successfully from flash\n");
return QLA_SUCCESS;
+ } else {
+ qla_printk(KERN_ERR, ha,
+ "Firmware load from flash failed\n");
}
+
try_blob_fw:
qla_printk(KERN_INFO, ha,
"Attempting to load firmware from blob\n");
@@ -2548,11 +2573,11 @@ qla2xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6 *cmd_pkt,
dsd_seg = (uint32_t *)&cmd_pkt->fcp_data_dseg_address;
*dsd_seg++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma));
*dsd_seg++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma));
- cmd_pkt->fcp_data_dseg_len = dsd_list_len;
+ *dsd_seg++ = cpu_to_le32(dsd_list_len);
} else {
*cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma));
*cur_dsd++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma));
- *cur_dsd++ = dsd_list_len;
+ *cur_dsd++ = cpu_to_le32(dsd_list_len);
}
cur_dsd = (uint32_t *)next_dsd;
while (avail_dsds) {
@@ -2991,7 +3016,7 @@ qla82xx_unprotect_flash(struct qla_hw_data *ha)
qla_printk(KERN_WARNING, ha, "Write disable failed\n");
done_unprotect:
- qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_UNLOCK));
+ qla82xx_rom_unlock(ha);
return ret;
}
@@ -3020,7 +3045,7 @@ qla82xx_protect_flash(struct qla_hw_data *ha)
if (qla82xx_write_disable_flash(ha) != 0)
qla_printk(KERN_WARNING, ha, "Write disable failed\n");
done_protect:
- qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_UNLOCK));
+ qla82xx_rom_unlock(ha);
return ret;
}
@@ -3048,7 +3073,7 @@ qla82xx_erase_sector(struct qla_hw_data *ha, int addr)
}
ret = qla82xx_flash_wait_write_finish(ha);
done:
- qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_UNLOCK));
+ qla82xx_rom_unlock(ha);
return ret;
}
@@ -3228,7 +3253,7 @@ void qla82xx_rom_lock_recovery(struct qla_hw_data *ha)
* else died while holding it.
* In either case, unlock.
*/
- qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_UNLOCK));
+ qla82xx_rom_unlock(ha);
}
/*
@@ -3528,15 +3553,18 @@ int
qla82xx_device_state_handler(scsi_qla_host_t *vha)
{
uint32_t dev_state;
+ uint32_t old_dev_state;
int rval = QLA_SUCCESS;
unsigned long dev_init_timeout;
struct qla_hw_data *ha = vha->hw;
+ int loopcount = 0;
qla82xx_idc_lock(ha);
if (!vha->flags.init_done)
qla82xx_set_drv_active(vha);
dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
+ old_dev_state = dev_state;
qla_printk(KERN_INFO, ha, "1:Device state is 0x%x = %s\n", dev_state,
dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown");
@@ -3553,10 +3581,16 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha)
break;
}
dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
- qla_printk(KERN_INFO, ha,
- "2:Device state is 0x%x = %s\n", dev_state,
- dev_state < MAX_STATES ?
- qdev_state[dev_state] : "Unknown");
+ if (old_dev_state != dev_state) {
+ loopcount = 0;
+ old_dev_state = dev_state;
+ }
+ if (loopcount < 5) {
+ qla_printk(KERN_INFO, ha,
+ "2:Device state is 0x%x = %s\n", dev_state,
+ dev_state < MAX_STATES ?
+ qdev_state[dev_state] : "Unknown");
+ }
switch (dev_state) {
case QLA82XX_DEV_READY:
@@ -3570,6 +3604,7 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha)
qla82xx_idc_lock(ha);
break;
case QLA82XX_DEV_NEED_RESET:
+ if (!ql2xdontresethba)
qla82xx_need_reset_handler(vha);
dev_init_timeout = jiffies +
(ha->nx_dev_init_timeout * HZ);
@@ -3604,6 +3639,7 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha)
msleep(1000);
qla82xx_idc_lock(ha);
}
+ loopcount++;
}
exit:
qla82xx_idc_unlock(ha);
@@ -3621,7 +3657,8 @@ void qla82xx_watchdog(scsi_qla_host_t *vha)
if (dev_state == QLA82XX_DEV_NEED_RESET &&
!test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) {
qla_printk(KERN_WARNING, ha,
- "%s(): Adapter reset needed!\n", __func__);
+ "scsi(%ld) %s: Adapter reset needed!\n",
+ vha->host_no, __func__);
set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
qla2xxx_wake_dpc(vha);
} else if (dev_state == QLA82XX_DEV_NEED_QUIESCENT &&
@@ -3632,10 +3669,27 @@ void qla82xx_watchdog(scsi_qla_host_t *vha)
set_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags);
qla2xxx_wake_dpc(vha);
} else {
- qla82xx_check_fw_alive(vha);
if (qla82xx_check_fw_alive(vha)) {
halt_status = qla82xx_rd_32(ha,
QLA82XX_PEG_HALT_STATUS1);
+ qla_printk(KERN_INFO, ha,
+ "scsi(%ld): %s, Dumping hw/fw registers:\n "
+ " PEG_HALT_STATUS1: 0x%x, PEG_HALT_STATUS2: 0x%x,\n "
+ " PEG_NET_0_PC: 0x%x, PEG_NET_1_PC: 0x%x,\n "
+ " PEG_NET_2_PC: 0x%x, PEG_NET_3_PC: 0x%x,\n "
+ " PEG_NET_4_PC: 0x%x\n",
+ vha->host_no, __func__, halt_status,
+ qla82xx_rd_32(ha, QLA82XX_PEG_HALT_STATUS2),
+ qla82xx_rd_32(ha,
+ QLA82XX_CRB_PEG_NET_0 + 0x3c),
+ qla82xx_rd_32(ha,
+ QLA82XX_CRB_PEG_NET_1 + 0x3c),
+ qla82xx_rd_32(ha,
+ QLA82XX_CRB_PEG_NET_2 + 0x3c),
+ qla82xx_rd_32(ha,
+ QLA82XX_CRB_PEG_NET_3 + 0x3c),
+ qla82xx_rd_32(ha,
+ QLA82XX_CRB_PEG_NET_4 + 0x3c));
if (halt_status & HALT_STATUS_UNRECOVERABLE) {
set_bit(ISP_UNRECOVERABLE,
&vha->dpc_flags);
@@ -3651,8 +3705,9 @@ void qla82xx_watchdog(scsi_qla_host_t *vha)
if (ha->flags.mbox_busy) {
ha->flags.mbox_int = 1;
DEBUG2(qla_printk(KERN_ERR, ha,
- "Due to fw hung, doing premature "
- "completion of mbx command\n"));
+ "scsi(%ld) Due to fw hung, doing "
+ "premature completion of mbx "
+ "command\n", vha->host_no));
if (test_bit(MBX_INTR_WAIT,
&ha->mbx_cmd_flags))
complete(&ha->mbx_intr_comp);
diff --git a/drivers/scsi/qla2xxx/qla_nx.h b/drivers/scsi/qla2xxx/qla_nx.h
index ed5883f1778..8a21832c669 100644
--- a/drivers/scsi/qla2xxx/qla_nx.h
+++ b/drivers/scsi/qla2xxx/qla_nx.h
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index aa774752916..f461925a9df 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
@@ -164,6 +164,20 @@ module_param(ql2xasynctmfenable, int, S_IRUGO);
MODULE_PARM_DESC(ql2xasynctmfenable,
"Enables issue of TM IOCBs asynchronously via IOCB mechanism"
"Default is 0 - Issue TM IOCBs via mailbox mechanism.");
+
+int ql2xdontresethba;
+module_param(ql2xdontresethba, int, S_IRUGO);
+MODULE_PARM_DESC(ql2xdontresethba,
+ "Option to specify reset behaviour\n"
+ " 0 (Default) -- Reset on failure.\n"
+ " 1 -- Do not reset on failure.\n");
+
+uint ql2xmaxlun = MAX_LUNS;
+module_param(ql2xmaxlun, uint, S_IRUGO);
+MODULE_PARM_DESC(ql2xmaxlun,
+ "Defines the maximum LU number to register with the SCSI "
+ "midlayer. Default is 65535.");
+
/*
* SCSI host template entry points
*/
@@ -528,7 +542,7 @@ qla2x00_get_new_sp(scsi_qla_host_t *vha, fc_port_t *fcport,
static int
qla2xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
{
- scsi_qla_host_t *vha = shost_priv(cmd->device->host);
+ scsi_qla_host_t *vha = shost_priv(host);
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
struct fc_rport *rport = starget_to_rport(scsi_target(cmd->device));
struct qla_hw_data *ha = vha->hw;
@@ -2128,7 +2142,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
else
host->max_cmd_len = MAX_CMDSZ;
host->max_channel = MAX_BUSES - 1;
- host->max_lun = MAX_LUNS;
+ host->max_lun = ql2xmaxlun;
host->transportt = qla2xxx_transport_template;
sht->vendor_id = (SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_QLOGIC);
@@ -2360,21 +2374,26 @@ qla2x00_remove_one(struct pci_dev *pdev)
base_vha = pci_get_drvdata(pdev);
ha = base_vha->hw;
- spin_lock_irqsave(&ha->vport_slock, flags);
- list_for_each_entry(vha, &ha->vp_list, list) {
- atomic_inc(&vha->vref_count);
+ mutex_lock(&ha->vport_lock);
+ while (ha->cur_vport_count) {
+ struct Scsi_Host *scsi_host;
- if (vha->fc_vport) {
- spin_unlock_irqrestore(&ha->vport_slock, flags);
+ spin_lock_irqsave(&ha->vport_slock, flags);
- fc_vport_terminate(vha->fc_vport);
+ BUG_ON(base_vha->list.next == &ha->vp_list);
+ /* This assumes first entry in ha->vp_list is always base vha */
+ vha = list_first_entry(&base_vha->list, scsi_qla_host_t, list);
+ scsi_host = scsi_host_get(vha->host);
- spin_lock_irqsave(&ha->vport_slock, flags);
- }
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+ mutex_unlock(&ha->vport_lock);
+
+ fc_vport_terminate(vha->fc_vport);
+ scsi_host_put(vha->host);
- atomic_dec(&vha->vref_count);
+ mutex_lock(&ha->vport_lock);
}
- spin_unlock_irqrestore(&ha->vport_slock, flags);
+ mutex_unlock(&ha->vport_lock);
set_bit(UNLOADING, &base_vha->dpc_flags);
@@ -2544,7 +2563,7 @@ void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport,
{
if (atomic_read(&fcport->state) == FCS_ONLINE &&
vha->vp_idx == fcport->vp_idx) {
- atomic_set(&fcport->state, FCS_DEVICE_LOST);
+ qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST);
qla2x00_schedule_rport_del(vha, fcport, defer);
}
/*
@@ -2552,7 +2571,7 @@ void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport,
* port but do the retries.
*/
if (atomic_read(&fcport->state) != FCS_DEVICE_DEAD)
- atomic_set(&fcport->state, FCS_DEVICE_LOST);
+ qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST);
if (!do_login)
return;
@@ -2607,7 +2626,7 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *vha, int defer)
if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD)
continue;
if (atomic_read(&fcport->state) == FCS_ONLINE) {
- atomic_set(&fcport->state, FCS_DEVICE_LOST);
+ qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST);
if (defer)
qla2x00_schedule_rport_del(vha, fcport, defer);
else if (vha->vp_idx == fcport->vp_idx)
@@ -3214,6 +3233,17 @@ void qla2x00_relogin(struct scsi_qla_host *vha)
fcport->d_id.b.area,
fcport->d_id.b.al_pa);
+ if (fcport->loop_id == FC_NO_LOOP_ID) {
+ fcport->loop_id = next_loopid =
+ ha->min_external_loopid;
+ status = qla2x00_find_new_loop_id(
+ vha, fcport);
+ if (status != QLA_SUCCESS) {
+ /* Ran out of IDs to use */
+ break;
+ }
+ }
+
if (IS_ALOGIO_CAPABLE(ha)) {
fcport->flags |= FCF_ASYNC_SENT;
data[0] = 0;
@@ -3604,7 +3634,8 @@ qla2x00_timer(scsi_qla_host_t *vha)
if (!pci_channel_offline(ha->pdev))
pci_read_config_word(ha->pdev, PCI_VENDOR_ID, &w);
- if (IS_QLA82XX(ha)) {
+ /* Make sure qla82xx_watchdog is run only for physical port */
+ if (!vha->vp_idx && IS_QLA82XX(ha)) {
if (test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags))
start_dpc++;
qla82xx_watchdog(vha);
@@ -3612,7 +3643,8 @@ qla2x00_timer(scsi_qla_host_t *vha)
/* Loop down handler. */
if (atomic_read(&vha->loop_down_timer) > 0 &&
- !(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))
+ !(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) &&
+ !(test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags))
&& vha->flags.online) {
if (atomic_read(&vha->loop_down_timer) ==
@@ -3648,7 +3680,11 @@ qla2x00_timer(scsi_qla_host_t *vha)
if (!(sfcp->flags & FCF_FCP2_DEVICE))
continue;
- set_bit(ISP_ABORT_NEEDED,
+ if (IS_QLA82XX(ha))
+ set_bit(FCOE_CTX_RESET_NEEDED,
+ &vha->dpc_flags);
+ else
+ set_bit(ISP_ABORT_NEEDED,
&vha->dpc_flags);
break;
}
@@ -3667,7 +3703,12 @@ qla2x00_timer(scsi_qla_host_t *vha)
qla_printk(KERN_WARNING, ha,
"Loop down - aborting ISP.\n");
- set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ if (IS_QLA82XX(ha))
+ set_bit(FCOE_CTX_RESET_NEEDED,
+ &vha->dpc_flags);
+ else
+ set_bit(ISP_ABORT_NEEDED,
+ &vha->dpc_flags);
}
}
DEBUG3(printk("scsi(%ld): Loop Down - seconds remaining %d\n",
@@ -3675,8 +3716,8 @@ qla2x00_timer(scsi_qla_host_t *vha)
atomic_read(&vha->loop_down_timer)));
}
- /* Check if beacon LED needs to be blinked */
- if (ha->beacon_blink_led == 1) {
+ /* Check if beacon LED needs to be blinked for physical host only */
+ if (!vha->vp_idx && (ha->beacon_blink_led == 1)) {
set_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags);
start_dpc++;
}
diff --git a/drivers/scsi/qla2xxx/qla_settings.h b/drivers/scsi/qla2xxx/qla_settings.h
index f0b2b9986a5..d70f0300898 100644
--- a/drivers/scsi/qla2xxx/qla_settings.h
+++ b/drivers/scsi/qla2xxx/qla_settings.h
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
index 22070621206..693647661ed 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -1,6 +1,6 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h
index 3a260c3f055..062c97bf62f 100644
--- a/drivers/scsi/qla2xxx/qla_version.h
+++ b/drivers/scsi/qla2xxx/qla_version.h
@@ -1,15 +1,15 @@
/*
* QLogic Fibre Channel HBA Driver
- * Copyright (c) 2003-2010 QLogic Corporation
+ * Copyright (c) 2003-2011 QLogic Corporation
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
/*
* Driver version
*/
-#define QLA2XXX_VERSION "8.03.07.00"
+#define QLA2XXX_VERSION "8.03.07.03-k"
#define QLA_DRIVER_MAJOR_VER 8
#define QLA_DRIVER_MINOR_VER 3
#define QLA_DRIVER_PATCH_VER 7
-#define QLA_DRIVER_BETA_VER 0
+#define QLA_DRIVER_BETA_VER 3
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index 230ba097d28..c22f2a764d9 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -2068,15 +2068,14 @@ static int qla4xxx_eh_abort(struct scsi_cmnd *cmd)
struct scsi_qla_host *ha = to_qla_host(cmd->device->host);
unsigned int id = cmd->device->id;
unsigned int lun = cmd->device->lun;
- unsigned long serial = cmd->serial_number;
unsigned long flags;
struct srb *srb = NULL;
int ret = SUCCESS;
int wait = 0;
ql4_printk(KERN_INFO, ha,
- "scsi%ld:%d:%d: Abort command issued cmd=%p, pid=%ld\n",
- ha->host_no, id, lun, cmd, serial);
+ "scsi%ld:%d:%d: Abort command issued cmd=%p\n",
+ ha->host_no, id, lun, cmd);
spin_lock_irqsave(&ha->hardware_lock, flags);
srb = (struct srb *) CMD_SP(cmd);
diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c
index e2d45c91b8e..9689d41c788 100644
--- a/drivers/scsi/qlogicpti.c
+++ b/drivers/scsi/qlogicpti.c
@@ -1292,8 +1292,10 @@ static struct scsi_host_template qpti_template = {
.use_clustering = ENABLE_CLUSTERING,
};
+static const struct of_device_id qpti_match[];
static int __devinit qpti_sbus_probe(struct platform_device *op)
{
+ const struct of_device_id *match;
struct scsi_host_template *tpnt;
struct device_node *dp = op->dev.of_node;
struct Scsi_Host *host;
@@ -1301,9 +1303,10 @@ static int __devinit qpti_sbus_probe(struct platform_device *op)
static int nqptis;
const char *fcode;
- if (!op->dev.of_match)
+ match = of_match_device(qpti_match, &op->dev);
+ if (!match)
return -EINVAL;
- tpnt = op->dev.of_match->data;
+ tpnt = match->data;
/* Sometimes Antares cards come up not completely
* setup, and we get a report of a zero IRQ.
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 633c2395a92..abea2cf05c2 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -321,6 +321,12 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
"changed. The Linux SCSI layer does not "
"automatically adjust these parameters.\n");
+ if (sshdr.asc == 0x38 && sshdr.ascq == 0x07)
+ scmd_printk(KERN_WARNING, scmd,
+ "Warning! Received an indication that the "
+ "LUN reached a thin provisioning soft "
+ "threshold.\n");
+
/*
* Pass the UA upwards for a determination in the completion
* functions.
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 6d5c7ff43f5..ec1803a4872 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -74,8 +74,6 @@ struct kmem_cache *scsi_sdb_cache;
*/
#define SCSI_QUEUE_DELAY 3
-static void scsi_run_queue(struct request_queue *q);
-
/*
* Function: scsi_unprep_request()
*
@@ -161,7 +159,7 @@ static int __scsi_queue_insert(struct scsi_cmnd *cmd, int reason, int unbusy)
blk_requeue_request(q, cmd->request);
spin_unlock_irqrestore(q->queue_lock, flags);
- scsi_run_queue(q);
+ kblockd_schedule_work(q, &device->requeue_work);
return 0;
}
@@ -400,10 +398,15 @@ static inline int scsi_host_is_busy(struct Scsi_Host *shost)
static void scsi_run_queue(struct request_queue *q)
{
struct scsi_device *sdev = q->queuedata;
- struct Scsi_Host *shost = sdev->host;
+ struct Scsi_Host *shost;
LIST_HEAD(starved_list);
unsigned long flags;
+ /* if the device is dead, sdev will be NULL, so no queue to run */
+ if (!sdev)
+ return;
+
+ shost = sdev->host;
if (scsi_target(sdev)->single_lun)
scsi_single_lun_run(sdev);
@@ -411,8 +414,6 @@ static void scsi_run_queue(struct request_queue *q)
list_splice_init(&shost->starved_list, &starved_list);
while (!list_empty(&starved_list)) {
- int flagset;
-
/*
* As long as shost is accepting commands and we have
* starved queues, call blk_run_queue. scsi_request_fn
@@ -436,18 +437,9 @@ static void scsi_run_queue(struct request_queue *q)
}
spin_unlock(shost->host_lock);
-
spin_lock(sdev->request_queue->queue_lock);
- flagset = test_bit(QUEUE_FLAG_REENTER, &q->queue_flags) &&
- !test_bit(QUEUE_FLAG_REENTER,
- &sdev->request_queue->queue_flags);
- if (flagset)
- queue_flag_set(QUEUE_FLAG_REENTER, sdev->request_queue);
- __blk_run_queue(sdev->request_queue, false);
- if (flagset)
- queue_flag_clear(QUEUE_FLAG_REENTER, sdev->request_queue);
+ __blk_run_queue(sdev->request_queue);
spin_unlock(sdev->request_queue->queue_lock);
-
spin_lock(shost->host_lock);
}
/* put any unprocessed entries back */
@@ -457,6 +449,16 @@ static void scsi_run_queue(struct request_queue *q)
blk_run_queue(q);
}
+void scsi_requeue_run_queue(struct work_struct *work)
+{
+ struct scsi_device *sdev;
+ struct request_queue *q;
+
+ sdev = container_of(work, struct scsi_device, requeue_work);
+ q = sdev->request_queue;
+ scsi_run_queue(q);
+}
+
/*
* Function: scsi_requeue_command()
*
diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c
index c99da926fda..f46855cd853 100644
--- a/drivers/scsi/scsi_proc.c
+++ b/drivers/scsi/scsi_proc.c
@@ -386,13 +386,59 @@ static ssize_t proc_scsi_write(struct file *file, const char __user *buf,
* @s: output goes here
* @p: not used
*/
-static int proc_scsi_show(struct seq_file *s, void *p)
+static int always_match(struct device *dev, void *data)
{
- seq_printf(s, "Attached devices:\n");
- bus_for_each_dev(&scsi_bus_type, NULL, s, proc_print_scsidevice);
- return 0;
+ return 1;
+}
+
+static inline struct device *next_scsi_device(struct device *start)
+{
+ struct device *next = bus_find_device(&scsi_bus_type, start, NULL,
+ always_match);
+ put_device(start);
+ return next;
}
+static void *scsi_seq_start(struct seq_file *sfile, loff_t *pos)
+{
+ struct device *dev = NULL;
+ loff_t n = *pos;
+
+ while ((dev = next_scsi_device(dev))) {
+ if (!n--)
+ break;
+ sfile->private++;
+ }
+ return dev;
+}
+
+static void *scsi_seq_next(struct seq_file *sfile, void *v, loff_t *pos)
+{
+ (*pos)++;
+ sfile->private++;
+ return next_scsi_device(v);
+}
+
+static void scsi_seq_stop(struct seq_file *sfile, void *v)
+{
+ put_device(v);
+}
+
+static int scsi_seq_show(struct seq_file *sfile, void *dev)
+{
+ if (!sfile->private)
+ seq_puts(sfile, "Attached devices:\n");
+
+ return proc_print_scsidevice(dev, sfile);
+}
+
+static const struct seq_operations scsi_seq_ops = {
+ .start = scsi_seq_start,
+ .next = scsi_seq_next,
+ .stop = scsi_seq_stop,
+ .show = scsi_seq_show
+};
+
/**
* proc_scsi_open - glue function
* @inode: not used
@@ -406,7 +452,7 @@ static int proc_scsi_open(struct inode *inode, struct file *file)
* We don't really need this for the write case but it doesn't
* harm either.
*/
- return single_open(file, proc_scsi_show, NULL);
+ return seq_open(file, &scsi_seq_ops);
}
static const struct file_operations proc_scsi_operations = {
@@ -415,7 +461,7 @@ static const struct file_operations proc_scsi_operations = {
.read = seq_read,
.write = proc_scsi_write,
.llseek = seq_lseek,
- .release = single_release,
+ .release = seq_release,
};
/**
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 087821fac8f..58584dc0724 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -242,6 +242,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
int display_failure_msg = 1, ret;
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
extern void scsi_evt_thread(struct work_struct *work);
+ extern void scsi_requeue_run_queue(struct work_struct *work);
sdev = kzalloc(sizeof(*sdev) + shost->transportt->device_size,
GFP_ATOMIC);
@@ -264,6 +265,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
INIT_LIST_HEAD(&sdev->event_list);
spin_lock_init(&sdev->list_lock);
INIT_WORK(&sdev->event_work, scsi_evt_thread);
+ INIT_WORK(&sdev->requeue_work, scsi_requeue_run_queue);
sdev->sdev_gendev.parent = get_device(&starget->dev);
sdev->sdev_target = starget;
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index e44ff64233f..e63912510fb 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -322,14 +322,8 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work)
kfree(evt);
}
- if (sdev->request_queue) {
- sdev->request_queue->queuedata = NULL;
- /* user context needed to free queue */
- scsi_free_queue(sdev->request_queue);
- /* temporary expedient, try to catch use of queue lock
- * after free of sdev */
- sdev->request_queue = NULL;
- }
+ /* NULL queue means the device can't be used */
+ sdev->request_queue = NULL;
scsi_target_reap(scsi_target(sdev));
@@ -937,6 +931,12 @@ void __scsi_remove_device(struct scsi_device *sdev)
if (sdev->host->hostt->slave_destroy)
sdev->host->hostt->slave_destroy(sdev);
transport_destroy_device(dev);
+
+ /* cause the request function to reject all I/O requests */
+ sdev->request_queue->queuedata = NULL;
+
+ /* Freeing the queue signals to block that we're done */
+ scsi_free_queue(sdev->request_queue);
put_device(dev);
}
diff --git a/drivers/scsi/scsi_tgt_lib.c b/drivers/scsi/scsi_tgt_lib.c
index 8bca8c25ba6..84a1fdf6786 100644
--- a/drivers/scsi/scsi_tgt_lib.c
+++ b/drivers/scsi/scsi_tgt_lib.c
@@ -275,10 +275,8 @@ void scsi_tgt_free_queue(struct Scsi_Host *shost)
for (i = 0; i < ARRAY_SIZE(qdata->cmd_hash); i++) {
list_for_each_entry_safe(tcmd, n, &qdata->cmd_hash[i],
- hash_list) {
- list_del(&tcmd->hash_list);
- list_add(&tcmd->hash_list, &cmds);
- }
+ hash_list)
+ list_move(&tcmd->hash_list, &cmds);
}
spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags);
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index fdf3fa63905..1b214910b71 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -422,8 +422,7 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev,
snprintf(fc_host->work_q_name, sizeof(fc_host->work_q_name),
"fc_wq_%d", shost->host_no);
- fc_host->work_q = create_singlethread_workqueue(
- fc_host->work_q_name);
+ fc_host->work_q = alloc_workqueue(fc_host->work_q_name, 0, 0);
if (!fc_host->work_q)
return -ENOMEM;
@@ -431,8 +430,8 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev,
snprintf(fc_host->devloss_work_q_name,
sizeof(fc_host->devloss_work_q_name),
"fc_dl_%d", shost->host_no);
- fc_host->devloss_work_q = create_singlethread_workqueue(
- fc_host->devloss_work_q_name);
+ fc_host->devloss_work_q =
+ alloc_workqueue(fc_host->devloss_work_q_name, 0, 0);
if (!fc_host->devloss_work_q) {
destroy_workqueue(fc_host->work_q);
fc_host->work_q = NULL;
@@ -2489,6 +2488,8 @@ fc_rport_final_delete(struct work_struct *work)
unsigned long flags;
int do_callback = 0;
+ fc_terminate_rport_io(rport);
+
/*
* if a scan is pending, flush the SCSI Host work_q so that
* that we can reclaim the rport scan work element.
@@ -2496,8 +2497,6 @@ fc_rport_final_delete(struct work_struct *work)
if (rport->flags & FC_RPORT_SCAN_PENDING)
scsi_flush_work(shost);
- fc_terminate_rport_io(rport);
-
/*
* Cancel any outstanding timers. These should really exist
* only when rmmod'ing the LLDD and we're asking for
@@ -3816,28 +3815,17 @@ fail_host_msg:
static void
fc_bsg_goose_queue(struct fc_rport *rport)
{
- int flagset;
- unsigned long flags;
-
if (!rport->rqst_q)
return;
+ /*
+ * This get/put dance makes no sense
+ */
get_device(&rport->dev);
-
- spin_lock_irqsave(rport->rqst_q->queue_lock, flags);
- flagset = test_bit(QUEUE_FLAG_REENTER, &rport->rqst_q->queue_flags) &&
- !test_bit(QUEUE_FLAG_REENTER, &rport->rqst_q->queue_flags);
- if (flagset)
- queue_flag_set(QUEUE_FLAG_REENTER, rport->rqst_q);
- __blk_run_queue(rport->rqst_q, false);
- if (flagset)
- queue_flag_clear(QUEUE_FLAG_REENTER, rport->rqst_q);
- spin_unlock_irqrestore(rport->rqst_q->queue_lock, flags);
-
+ blk_run_queue_async(rport->rqst_q);
put_device(&rport->dev);
}
-
/**
* fc_bsg_rport_dispatch - process rport bsg requests and dispatch to LLDD
* @q: rport request queue
diff --git a/drivers/scsi/tmscsim.c b/drivers/scsi/tmscsim.c
index a124a28f2cc..a1baccce05f 100644
--- a/drivers/scsi/tmscsim.c
+++ b/drivers/scsi/tmscsim.c
@@ -565,12 +565,12 @@ dc390_StartSCSI( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_sr
pDCB->TagMask |= 1 << tag[1];
pSRB->TagNumber = tag[1];
DC390_write8(ScsiFifo, tag[1]);
- DEBUG1(printk(KERN_INFO "DC390: Select w/DisCn for Cmd %li (SRB %p), block tag %02x\n", scmd->serial_number, pSRB, tag[1]));
+ DEBUG1(printk(KERN_INFO "DC390: Select w/DisCn for SRB %p, block tag %02x\n", pSRB, tag[1]));
cmd = SEL_W_ATN3;
} else {
/* No TagQ */
//no_tag:
- DEBUG1(printk(KERN_INFO "DC390: Select w%s/DisCn for Cmd %li (SRB %p), No TagQ\n", disc_allowed ? "" : "o", scmd->serial_number, pSRB));
+ DEBUG1(printk(KERN_INFO "DC390: Select w%s/DisCn for SRB %p, No TagQ\n", disc_allowed ? "" : "o", pSRB));
}
pSRB->SRBState = SRB_START_;
@@ -620,8 +620,8 @@ dc390_StartSCSI( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_sr
if (DC390_read8 (Scsi_Status) & INTERRUPT)
{
dc390_freetag (pDCB, pSRB);
- DEBUG0(printk ("DC390: Interrupt during Start SCSI (pid %li, target %02i-%02i)\n",
- scmd->serial_number, scmd->device->id, scmd->device->lun));
+ DEBUG0(printk ("DC390: Interrupt during Start SCSI (target %02i-%02i)\n",
+ scmd->device->id, scmd->device->lun));
pSRB->SRBState = SRB_READY;
//DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
pACB->SelLost++;
@@ -1705,8 +1705,7 @@ dc390_SRBdone( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb*
status = pSRB->TargetStatus;
- DEBUG0(printk (" SRBdone (%02x,%08x), SRB %p, pid %li\n", status, pcmd->result,\
- pSRB, pcmd->serial_number));
+ DEBUG0(printk (" SRBdone (%02x,%08x), SRB %p\n", status, pcmd->result, pSRB));
if(pSRB->SRBFlag & AUTO_REQSENSE)
{ /* Last command was a Request Sense */
pSRB->SRBFlag &= ~AUTO_REQSENSE;
@@ -1727,7 +1726,7 @@ dc390_SRBdone( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb*
} else {
SET_RES_DRV(pcmd->result, DRIVER_SENSE);
//pSRB->ScsiCmdLen = (u8) (pSRB->Segment1[0] >> 8);
- DEBUG0 (printk ("DC390: RETRY pid %li (%02x), target %02i-%02i\n", pcmd->serial_number, pcmd->cmnd[0], pcmd->device->id, pcmd->device->lun));
+ DEBUG0 (printk ("DC390: RETRY (%02x), target %02i-%02i\n", pcmd->cmnd[0], pcmd->device->id, pcmd->device->lun));
pSRB->TotalXferredLen = 0;
SET_RES_DID(pcmd->result, DID_SOFT_ERROR);
}
@@ -1747,7 +1746,7 @@ dc390_SRBdone( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb*
else if (status == SAM_STAT_TASK_SET_FULL)
{
scsi_track_queue_full(pcmd->device, pDCB->GoingSRBCnt - 1);
- DEBUG0 (printk ("DC390: RETRY pid %li (%02x), target %02i-%02i\n", pcmd->serial_number, pcmd->cmnd[0], pcmd->device->id, pcmd->device->lun));
+ DEBUG0 (printk ("DC390: RETRY (%02x), target %02i-%02i\n", pcmd->cmnd[0], pcmd->device->id, pcmd->device->lun));
pSRB->TotalXferredLen = 0;
SET_RES_DID(pcmd->result, DID_SOFT_ERROR);
}
@@ -1801,7 +1800,7 @@ cmd_done:
/* Add to free list */
dc390_Free_insert (pACB, pSRB);
- DEBUG0(printk (KERN_DEBUG "DC390: SRBdone: done pid %li\n", pcmd->serial_number));
+ DEBUG0(printk (KERN_DEBUG "DC390: SRBdone: done\n"));
pcmd->scsi_done (pcmd);
return;
@@ -1997,8 +1996,7 @@ static int DC390_abort(struct scsi_cmnd *cmd)
struct dc390_acb *pACB = (struct dc390_acb*) cmd->device->host->hostdata;
struct dc390_dcb *pDCB = (struct dc390_dcb*) cmd->device->hostdata;
- scmd_printk(KERN_WARNING, cmd,
- "DC390: Abort command (pid %li)\n", cmd->serial_number);
+ scmd_printk(KERN_WARNING, cmd, "DC390: Abort command\n");
/* abort() is too stupid for already sent commands at the moment.
* If it's called we are in trouble anyway, so let's dump some info
@@ -2006,7 +2004,7 @@ static int DC390_abort(struct scsi_cmnd *cmd)
dc390_dumpinfo(pACB, pDCB, NULL);
pDCB->DCBFlag |= ABORT_DEV_;
- printk(KERN_INFO "DC390: Aborted pid %li\n", cmd->serial_number);
+ printk(KERN_INFO "DC390: Aborted.\n");
return FAILED;
}
diff --git a/drivers/scsi/u14-34f.c b/drivers/scsi/u14-34f.c
index edfc5da8be4..90e104d6b55 100644
--- a/drivers/scsi/u14-34f.c
+++ b/drivers/scsi/u14-34f.c
@@ -1256,8 +1256,8 @@ static int u14_34f_queuecommand_lck(struct scsi_cmnd *SCpnt, void (*done)(struct
j = ((struct hostdata *) SCpnt->device->host->hostdata)->board_number;
if (SCpnt->host_scribble)
- panic("%s: qcomm, pid %ld, SCpnt %p already active.\n",
- BN(j), SCpnt->serial_number, SCpnt);
+ panic("%s: qcomm, SCpnt %p already active.\n",
+ BN(j), SCpnt);
/* i is the mailbox number, look for the first free mailbox
starting from last_cp_used */
@@ -1286,9 +1286,9 @@ static int u14_34f_queuecommand_lck(struct scsi_cmnd *SCpnt, void (*done)(struct
cpp->cpp_index = i;
SCpnt->host_scribble = (unsigned char *) &cpp->cpp_index;
- if (do_trace) printk("%s: qcomm, mbox %d, target %d.%d:%d, pid %ld.\n",
+ if (do_trace) printk("%s: qcomm, mbox %d, target %d.%d:%d.\n",
BN(j), i, SCpnt->device->channel, SCpnt->device->id,
- SCpnt->device->lun, SCpnt->serial_number);
+ SCpnt->device->lun);
cpp->opcode = OP_SCSI;
cpp->channel = SCpnt->device->channel;
@@ -1315,7 +1315,7 @@ static int u14_34f_queuecommand_lck(struct scsi_cmnd *SCpnt, void (*done)(struct
unmap_dma(i, j);
SCpnt->host_scribble = NULL;
scmd_printk(KERN_INFO, SCpnt,
- "qcomm, pid %ld, adapter busy.\n", SCpnt->serial_number);
+ "qcomm, adapter busy.\n");
return 1;
}
@@ -1337,14 +1337,12 @@ static int u14_34f_eh_abort(struct scsi_cmnd *SCarg) {
j = ((struct hostdata *) SCarg->device->host->hostdata)->board_number;
if (SCarg->host_scribble == NULL) {
- scmd_printk(KERN_INFO, SCarg, "abort, pid %ld inactive.\n",
- SCarg->serial_number);
+ scmd_printk(KERN_INFO, SCarg, "abort, command inactive.\n");
return SUCCESS;
}
i = *(unsigned int *)SCarg->host_scribble;
- scmd_printk(KERN_INFO, SCarg, "abort, mbox %d, pid %ld.\n",
- i, SCarg->serial_number);
+ scmd_printk(KERN_INFO, SCarg, "abort, mbox %d.\n", i);
if (i >= sh[j]->can_queue)
panic("%s: abort, invalid SCarg->host_scribble.\n", BN(j));
@@ -1387,8 +1385,7 @@ static int u14_34f_eh_abort(struct scsi_cmnd *SCarg) {
SCarg->result = DID_ABORT << 16;
SCarg->host_scribble = NULL;
HD(j)->cp_stat[i] = FREE;
- printk("%s, abort, mbox %d ready, DID_ABORT, pid %ld done.\n",
- BN(j), i, SCarg->serial_number);
+ printk("%s, abort, mbox %d ready, DID_ABORT, done.\n", BN(j), i);
SCarg->scsi_done(SCarg);
return SUCCESS;
}
@@ -1403,12 +1400,12 @@ static int u14_34f_eh_host_reset(struct scsi_cmnd *SCarg) {
struct scsi_cmnd *SCpnt;
j = ((struct hostdata *) SCarg->device->host->hostdata)->board_number;
- scmd_printk(KERN_INFO, SCarg, "reset, enter, pid %ld.\n", SCarg->serial_number);
+ scmd_printk(KERN_INFO, SCarg, "reset, enter.\n");
spin_lock_irq(sh[j]->host_lock);
if (SCarg->host_scribble == NULL)
- printk("%s: reset, pid %ld inactive.\n", BN(j), SCarg->serial_number);
+ printk("%s: reset, inactive.\n", BN(j));
if (HD(j)->in_reset) {
printk("%s: reset, exit, already in reset.\n", BN(j));
@@ -1445,14 +1442,12 @@ static int u14_34f_eh_host_reset(struct scsi_cmnd *SCarg) {
if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) {
HD(j)->cp_stat[i] = ABORTING;
- printk("%s: reset, mbox %d aborting, pid %ld.\n",
- BN(j), i, SCpnt->serial_number);
+ printk("%s: reset, mbox %d aborting.\n", BN(j), i);
}
else {
HD(j)->cp_stat[i] = IN_RESET;
- printk("%s: reset, mbox %d in reset, pid %ld.\n",
- BN(j), i, SCpnt->serial_number);
+ printk("%s: reset, mbox %d in reset.\n", BN(j), i);
}
if (SCpnt->host_scribble == NULL)
@@ -1500,8 +1495,7 @@ static int u14_34f_eh_host_reset(struct scsi_cmnd *SCarg) {
/* This mailbox is still waiting for its interrupt */
HD(j)->cp_stat[i] = LOCKED;
- printk("%s, reset, mbox %d locked, DID_RESET, pid %ld done.\n",
- BN(j), i, SCpnt->serial_number);
+ printk("%s, reset, mbox %d locked, DID_RESET, done.\n", BN(j), i);
}
else if (HD(j)->cp_stat[i] == ABORTING) {
@@ -1513,8 +1507,7 @@ static int u14_34f_eh_host_reset(struct scsi_cmnd *SCarg) {
/* This mailbox was never queued to the adapter */
HD(j)->cp_stat[i] = FREE;
- printk("%s, reset, mbox %d aborting, DID_RESET, pid %ld done.\n",
- BN(j), i, SCpnt->serial_number);
+ printk("%s, reset, mbox %d aborting, DID_RESET, done.\n", BN(j), i);
}
else
@@ -1528,7 +1521,7 @@ static int u14_34f_eh_host_reset(struct scsi_cmnd *SCarg) {
HD(j)->in_reset = FALSE;
do_trace = FALSE;
- if (arg_done) printk("%s: reset, exit, pid %ld done.\n", BN(j), SCarg->serial_number);
+ if (arg_done) printk("%s: reset, exit, done.\n", BN(j));
else printk("%s: reset, exit.\n", BN(j));
spin_unlock_irq(sh[j]->host_lock);
@@ -1671,10 +1664,10 @@ static int reorder(unsigned int j, unsigned long cursec,
if (link_statistics && (overlap || !(flushcount % link_statistics)))
for (n = 0; n < n_ready; n++) {
k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
- printk("%s %d.%d:%d pid %ld mb %d fc %d nr %d sec %ld ns %u"\
+ printk("%s %d.%d:%d mb %d fc %d nr %d sec %ld ns %u"\
" cur %ld s:%c r:%c rev:%c in:%c ov:%c xd %d.\n",
(ihdlr ? "ihdlr" : "qcomm"), SCpnt->channel, SCpnt->target,
- SCpnt->lun, SCpnt->serial_number, k, flushcount, n_ready,
+ SCpnt->lun, k, flushcount, n_ready,
blk_rq_pos(SCpnt->request), blk_rq_sectors(SCpnt->request),
cursec, YESNO(s), YESNO(r), YESNO(rev), YESNO(input_only),
YESNO(overlap), cpp->xdir);
@@ -1709,9 +1702,9 @@ static void flush_dev(struct scsi_device *dev, unsigned long cursec, unsigned in
if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
scmd_printk(KERN_INFO, SCpnt,
- "%s, pid %ld, mbox %d, adapter"
+ "%s, mbox %d, adapter"
" busy, will abort.\n", (ihdlr ? "ihdlr" : "qcomm"),
- SCpnt->serial_number, k);
+ k);
HD(j)->cp_stat[k] = ABORTING;
continue;
}
@@ -1793,12 +1786,12 @@ static irqreturn_t ihdlr(unsigned int j)
if (SCpnt == NULL) panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", BN(j), i);
if (SCpnt->host_scribble == NULL)
- panic("%s: ihdlr, mbox %d, pid %ld, SCpnt %p garbled.\n", BN(j), i,
- SCpnt->serial_number, SCpnt);
+ panic("%s: ihdlr, mbox %d, SCpnt %p garbled.\n", BN(j), i,
+ SCpnt);
if (*(unsigned int *)SCpnt->host_scribble != i)
- panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d.\n",
- BN(j), i, SCpnt->serial_number, *(unsigned int *)SCpnt->host_scribble);
+ panic("%s: ihdlr, mbox %d, index mismatch %d.\n",
+ BN(j), i, *(unsigned int *)SCpnt->host_scribble);
sync_dma(i, j);
@@ -1841,8 +1834,8 @@ static irqreturn_t ihdlr(unsigned int j)
(!(tstatus == CHECK_CONDITION && HD(j)->iocount <= 1000 &&
(SCpnt->sense_buffer[2] & 0xf) == NOT_READY)))
scmd_printk(KERN_INFO, SCpnt,
- "ihdlr, pid %ld, target_status 0x%x, sense key 0x%x.\n",
- SCpnt->serial_number, spp->target_status,
+ "ihdlr, target_status 0x%x, sense key 0x%x.\n",
+ spp->target_status,
SCpnt->sense_buffer[2]);
HD(j)->target_to[scmd_id(SCpnt)][scmd_channel(SCpnt)] = 0;
@@ -1913,8 +1906,8 @@ static irqreturn_t ihdlr(unsigned int j)
do_trace || msg_byte(spp->target_status))
#endif
scmd_printk(KERN_INFO, SCpnt, "ihdlr, mbox %2d, err 0x%x:%x,"\
- " pid %ld, reg 0x%x, count %d.\n",
- i, spp->adapter_status, spp->target_status, SCpnt->serial_number,
+ " reg 0x%x, count %d.\n",
+ i, spp->adapter_status, spp->target_status,
reg, HD(j)->iocount);
unmap_dma(i, j);
diff --git a/drivers/scsi/wd33c93.c b/drivers/scsi/wd33c93.c
index 4468ae3610f..97ae716134d 100644
--- a/drivers/scsi/wd33c93.c
+++ b/drivers/scsi/wd33c93.c
@@ -381,7 +381,7 @@ wd33c93_queuecommand_lck(struct scsi_cmnd *cmd,
hostdata = (struct WD33C93_hostdata *) cmd->device->host->hostdata;
DB(DB_QUEUE_COMMAND,
- printk("Q-%d-%02x-%ld( ", cmd->device->id, cmd->cmnd[0], cmd->serial_number))
+ printk("Q-%d-%02x( ", cmd->device->id, cmd->cmnd[0]))
/* Set up a few fields in the scsi_cmnd structure for our own use:
* - host_scribble is the pointer to the next cmd in the input queue
@@ -462,7 +462,7 @@ wd33c93_queuecommand_lck(struct scsi_cmnd *cmd,
wd33c93_execute(cmd->device->host);
- DB(DB_QUEUE_COMMAND, printk(")Q-%ld ", cmd->serial_number))
+ DB(DB_QUEUE_COMMAND, printk(")Q "))
spin_unlock_irq(&hostdata->lock);
return 0;
@@ -687,7 +687,7 @@ wd33c93_execute(struct Scsi_Host *instance)
*/
DB(DB_EXECUTE,
- printk("%s%ld)EX-2 ", (cmd->SCp.phase) ? "d:" : "", cmd->serial_number))
+ printk("%s)EX-2 ", (cmd->SCp.phase) ? "d:" : ""))
}
static void
@@ -963,7 +963,7 @@ wd33c93_intr(struct Scsi_Host *instance)
case CSR_XFER_DONE | PHS_COMMAND:
case CSR_UNEXP | PHS_COMMAND:
case CSR_SRV_REQ | PHS_COMMAND:
- DB(DB_INTR, printk("CMND-%02x,%ld", cmd->cmnd[0], cmd->serial_number))
+ DB(DB_INTR, printk("CMND-%02x", cmd->cmnd[0]))
transfer_pio(regs, cmd->cmnd, cmd->cmd_len, DATA_OUT_DIR,
hostdata);
hostdata->state = S_CONNECTED;
@@ -1007,7 +1007,7 @@ wd33c93_intr(struct Scsi_Host *instance)
switch (msg) {
case COMMAND_COMPLETE:
- DB(DB_INTR, printk("CCMP-%ld", cmd->serial_number))
+ DB(DB_INTR, printk("CCMP"))
write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
hostdata->state = S_PRE_CMP_DISC;
break;
@@ -1174,7 +1174,7 @@ wd33c93_intr(struct Scsi_Host *instance)
write_wd33c93(regs, WD_SOURCE_ID, SRCID_ER);
if (phs == 0x60) {
- DB(DB_INTR, printk("SX-DONE-%ld", cmd->serial_number))
+ DB(DB_INTR, printk("SX-DONE"))
cmd->SCp.Message = COMMAND_COMPLETE;
lun = read_wd33c93(regs, WD_TARGET_LUN);
DB(DB_INTR, printk(":%d.%d", cmd->SCp.Status, lun))
@@ -1200,8 +1200,8 @@ wd33c93_intr(struct Scsi_Host *instance)
wd33c93_execute(instance);
} else {
printk
- ("%02x:%02x:%02x-%ld: Unknown SEL_XFER_DONE phase!!---",
- asr, sr, phs, cmd->serial_number);
+ ("%02x:%02x:%02x: Unknown SEL_XFER_DONE phase!!---",
+ asr, sr, phs);
spin_unlock_irqrestore(&hostdata->lock, flags);
}
break;
@@ -1266,7 +1266,7 @@ wd33c93_intr(struct Scsi_Host *instance)
spin_unlock_irqrestore(&hostdata->lock, flags);
return;
}
- DB(DB_INTR, printk("UNEXP_DISC-%ld", cmd->serial_number))
+ DB(DB_INTR, printk("UNEXP_DISC"))
hostdata->connected = NULL;
hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
hostdata->state = S_UNCONNECTED;
@@ -1292,7 +1292,7 @@ wd33c93_intr(struct Scsi_Host *instance)
*/
write_wd33c93(regs, WD_SOURCE_ID, SRCID_ER);
- DB(DB_INTR, printk("DISC-%ld", cmd->serial_number))
+ DB(DB_INTR, printk("DISC"))
if (cmd == NULL) {
printk(" - Already disconnected! ");
hostdata->state = S_UNCONNECTED;
@@ -1491,7 +1491,6 @@ wd33c93_intr(struct Scsi_Host *instance)
} else
hostdata->state = S_CONNECTED;
- DB(DB_INTR, printk("-%ld", cmd->serial_number))
spin_unlock_irqrestore(&hostdata->lock, flags);
break;
@@ -1637,8 +1636,8 @@ wd33c93_abort(struct scsi_cmnd * cmd)
cmd->host_scribble = NULL;
cmd->result = DID_ABORT << 16;
printk
- ("scsi%d: Abort - removing command %ld from input_Q. ",
- instance->host_no, cmd->serial_number);
+ ("scsi%d: Abort - removing command from input_Q. ",
+ instance->host_no);
enable_irq(cmd->device->host->irq);
cmd->scsi_done(cmd);
return SUCCESS;
@@ -1662,8 +1661,8 @@ wd33c93_abort(struct scsi_cmnd * cmd)
uchar sr, asr;
unsigned long timeout;
- printk("scsi%d: Aborting connected command %ld - ",
- instance->host_no, cmd->serial_number);
+ printk("scsi%d: Aborting connected command - ",
+ instance->host_no);
printk("stopping DMA - ");
if (hostdata->dma == D_DMA_RUNNING) {
@@ -1729,8 +1728,8 @@ wd33c93_abort(struct scsi_cmnd * cmd)
while (tmp) {
if (tmp == cmd) {
printk
- ("scsi%d: Abort - command %ld found on disconnected_Q - ",
- instance->host_no, cmd->serial_number);
+ ("scsi%d: Abort - command found on disconnected_Q - ",
+ instance->host_no);
printk("Abort SNOOZE. ");
enable_irq(cmd->device->host->irq);
return FAILED;
@@ -2180,8 +2179,8 @@ wd33c93_proc_info(struct Scsi_Host *instance, char *buf, char **start, off_t off
strcat(bp, "\nconnected: ");
if (hd->connected) {
cmd = (struct scsi_cmnd *) hd->connected;
- sprintf(tbuf, " %ld-%d:%d(%02x)",
- cmd->serial_number, cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
+ sprintf(tbuf, " %d:%d(%02x)",
+ cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
strcat(bp, tbuf);
}
}
@@ -2189,8 +2188,8 @@ wd33c93_proc_info(struct Scsi_Host *instance, char *buf, char **start, off_t off
strcat(bp, "\ninput_Q: ");
cmd = (struct scsi_cmnd *) hd->input_Q;
while (cmd) {
- sprintf(tbuf, " %ld-%d:%d(%02x)",
- cmd->serial_number, cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
+ sprintf(tbuf, " %d:%d(%02x)",
+ cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
strcat(bp, tbuf);
cmd = (struct scsi_cmnd *) cmd->host_scribble;
}
@@ -2199,8 +2198,8 @@ wd33c93_proc_info(struct Scsi_Host *instance, char *buf, char **start, off_t off
strcat(bp, "\ndisconnected_Q:");
cmd = (struct scsi_cmnd *) hd->disconnected_Q;
while (cmd) {
- sprintf(tbuf, " %ld-%d:%d(%02x)",
- cmd->serial_number, cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
+ sprintf(tbuf, " %d:%d(%02x)",
+ cmd->device->id, cmd->device->lun, cmd->cmnd[0]);
strcat(bp, tbuf);
cmd = (struct scsi_cmnd *) cmd->host_scribble;
}
diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c
index 5825370bad2..08de58e7f59 100644
--- a/drivers/spi/amba-pl022.c
+++ b/drivers/spi/amba-pl022.c
@@ -1555,7 +1555,7 @@ static int stop_queue(struct pl022 *pl022)
* A wait_queue on the pl022->busy could be used, but then the common
* execution path (pump_messages) would be required to call wake_up or
* friends on every SPI message. Do this instead */
- while (!list_empty(&pl022->queue) && pl022->busy && limit--) {
+ while ((!list_empty(&pl022->queue) || pl022->busy) && limit--) {
spin_unlock_irqrestore(&pl022->queue_lock, flags);
msleep(10);
spin_lock_irqsave(&pl022->queue_lock, flags);
diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c
index b1a4b9f503a..871e337c917 100644
--- a/drivers/spi/dw_spi.c
+++ b/drivers/spi/dw_spi.c
@@ -821,7 +821,7 @@ static int stop_queue(struct dw_spi *dws)
spin_lock_irqsave(&dws->lock, flags);
dws->run = QUEUE_STOPPED;
- while (!list_empty(&dws->queue) && dws->busy && limit--) {
+ while ((!list_empty(&dws->queue) || dws->busy) && limit--) {
spin_unlock_irqrestore(&dws->lock, flags);
msleep(10);
spin_lock_irqsave(&dws->lock, flags);
diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c
index 9c74aad6be9..dc25bee8d33 100644
--- a/drivers/spi/pxa2xx_spi.c
+++ b/drivers/spi/pxa2xx_spi.c
@@ -1493,7 +1493,7 @@ static int stop_queue(struct driver_data *drv_data)
* execution path (pump_messages) would be required to call wake_up or
* friends on every SPI message. Do this instead */
drv_data->run = QUEUE_STOPPED;
- while (!list_empty(&drv_data->queue) && drv_data->busy && limit--) {
+ while ((!list_empty(&drv_data->queue) || drv_data->busy) && limit--) {
spin_unlock_irqrestore(&drv_data->lock, flags);
msleep(10);
spin_lock_irqsave(&drv_data->lock, flags);
diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c
index bdb7289a1d2..f706dba165c 100644
--- a/drivers/spi/spi_bfin5xx.c
+++ b/drivers/spi/spi_bfin5xx.c
@@ -1284,7 +1284,7 @@ static inline int bfin_spi_stop_queue(struct bfin_spi_master_data *drv_data)
* friends on every SPI message. Do this instead
*/
drv_data->running = false;
- while (!list_empty(&drv_data->queue) && drv_data->busy && limit--) {
+ while ((!list_empty(&drv_data->queue) || drv_data->busy) && limit--) {
spin_unlock_irqrestore(&drv_data->lock, flags);
msleep(10);
spin_lock_irqsave(&drv_data->lock, flags);
diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c
index 6f34963b3c6..7ad48585c5e 100644
--- a/drivers/ssb/pci.c
+++ b/drivers/ssb/pci.c
@@ -662,7 +662,6 @@ static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out,
static int ssb_pci_sprom_get(struct ssb_bus *bus,
struct ssb_sprom *sprom)
{
- const struct ssb_sprom *fallback;
int err;
u16 *buf;
@@ -707,10 +706,17 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus,
if (err) {
/* All CRC attempts failed.
* Maybe there is no SPROM on the device?
- * If we have a fallback, use that. */
- fallback = ssb_get_fallback_sprom();
- if (fallback) {
- memcpy(sprom, fallback, sizeof(*sprom));
+ * Now we ask the arch code if there is some sprom
+ * available for this device in some other storage */
+ err = ssb_fill_sprom_with_fallback(bus, sprom);
+ if (err) {
+ ssb_printk(KERN_WARNING PFX "WARNING: Using"
+ " fallback SPROM failed (err %d)\n",
+ err);
+ } else {
+ ssb_dprintk(KERN_DEBUG PFX "Using SPROM"
+ " revision %d provided by"
+ " platform.\n", sprom->revision);
err = 0;
goto out_free;
}
diff --git a/drivers/ssb/sprom.c b/drivers/ssb/sprom.c
index 5f34d7a3e3a..45ff0e3a382 100644
--- a/drivers/ssb/sprom.c
+++ b/drivers/ssb/sprom.c
@@ -17,7 +17,7 @@
#include <linux/slab.h>
-static const struct ssb_sprom *fallback_sprom;
+static int(*get_fallback_sprom)(struct ssb_bus *dev, struct ssb_sprom *out);
static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len,
@@ -145,36 +145,43 @@ out:
}
/**
- * ssb_arch_set_fallback_sprom - Set a fallback SPROM for use if no SPROM is found.
+ * ssb_arch_register_fallback_sprom - Registers a method providing a
+ * fallback SPROM if no SPROM is found.
*
- * @sprom: The SPROM data structure to register.
+ * @sprom_callback: The callback function.
*
- * With this function the architecture implementation may register a fallback
- * SPROM data structure. The fallback is only used for PCI based SSB devices,
- * where no valid SPROM can be found in the shadow registers.
+ * With this function the architecture implementation may register a
+ * callback handler which fills the SPROM data structure. The fallback is
+ * only used for PCI based SSB devices, where no valid SPROM can be found
+ * in the shadow registers.
*
- * This function is useful for weird architectures that have a half-assed SSB device
- * hardwired to their PCI bus.
+ * This function is useful for weird architectures that have a half-assed
+ * SSB device hardwired to their PCI bus.
*
- * Note that it does only work with PCI attached SSB devices. PCMCIA devices currently
- * don't use this fallback.
- * Architectures must provide the SPROM for native SSB devices anyway,
- * so the fallback also isn't used for native devices.
+ * Note that it does only work with PCI attached SSB devices. PCMCIA
+ * devices currently don't use this fallback.
+ * Architectures must provide the SPROM for native SSB devices anyway, so
+ * the fallback also isn't used for native devices.
*
- * This function is available for architecture code, only. So it is not exported.
+ * This function is available for architecture code, only. So it is not
+ * exported.
*/
-int ssb_arch_set_fallback_sprom(const struct ssb_sprom *sprom)
+int ssb_arch_register_fallback_sprom(int (*sprom_callback)(struct ssb_bus *bus,
+ struct ssb_sprom *out))
{
- if (fallback_sprom)
+ if (get_fallback_sprom)
return -EEXIST;
- fallback_sprom = sprom;
+ get_fallback_sprom = sprom_callback;
return 0;
}
-const struct ssb_sprom *ssb_get_fallback_sprom(void)
+int ssb_fill_sprom_with_fallback(struct ssb_bus *bus, struct ssb_sprom *out)
{
- return fallback_sprom;
+ if (!get_fallback_sprom)
+ return -ENOENT;
+
+ return get_fallback_sprom(bus, out);
}
/* http://bcm-v4.sipsolutions.net/802.11/IsSpromAvailable */
diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h
index 0331139a726..77653014db0 100644
--- a/drivers/ssb/ssb_private.h
+++ b/drivers/ssb/ssb_private.h
@@ -171,7 +171,8 @@ ssize_t ssb_attr_sprom_store(struct ssb_bus *bus,
const char *buf, size_t count,
int (*sprom_check_crc)(const u16 *sprom, size_t size),
int (*sprom_write)(struct ssb_bus *bus, const u16 *sprom));
-extern const struct ssb_sprom *ssb_get_fallback_sprom(void);
+extern int ssb_fill_sprom_with_fallback(struct ssb_bus *bus,
+ struct ssb_sprom *out);
/* core.c */
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index dca4a0bb6ca..e3786f161bc 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -131,8 +131,6 @@ source "drivers/staging/wlags49_h2/Kconfig"
source "drivers/staging/wlags49_h25/Kconfig"
-source "drivers/staging/samsung-laptop/Kconfig"
-
source "drivers/staging/sm7xx/Kconfig"
source "drivers/staging/dt3155v4l/Kconfig"
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index eb93012b6f5..f0d5c531561 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -48,7 +48,6 @@ obj-$(CONFIG_XVMALLOC) += zram/
obj-$(CONFIG_ZCACHE) += zcache/
obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/
obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/
-obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop/
obj-$(CONFIG_FB_SM7XX) += sm7xx/
obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/
obj-$(CONFIG_CRYSTALHD) += crystalhd/
diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c
index eeb7dd43f9a..830822f86e4 100644
--- a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c
+++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c
@@ -2288,7 +2288,3 @@ err_dev:
free_netdev(dev);
return NULL;
}
-
-EXPORT_SYMBOL(init_ft1000_card);
-EXPORT_SYMBOL(stop_ft1000_card);
-EXPORT_SYMBOL(flarion_ft1000_cnt);
diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c
index 935608e7200..bdfb1aec58d 100644
--- a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c
+++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c
@@ -214,6 +214,3 @@ void ft1000CleanupProc(struct net_device *dev)
remove_proc_entry(FT1000_PROC, init_net.proc_net);
unregister_netdevice_notifier(&ft1000_netdev_notifier);
}
-
-EXPORT_SYMBOL(ft1000InitProc);
-EXPORT_SYMBOL(ft1000CleanupProc);
diff --git a/drivers/staging/gma500/Kconfig b/drivers/staging/gma500/Kconfig
index 5501eb9b335..ce8bedaeaac 100644
--- a/drivers/staging/gma500/Kconfig
+++ b/drivers/staging/gma500/Kconfig
@@ -1,6 +1,6 @@
config DRM_PSB
tristate "Intel GMA500 KMS Framebuffer"
- depends on DRM && PCI
+ depends on DRM && PCI && X86
select FB_CFB_COPYAREA
select FB_CFB_FILLRECT
select FB_CFB_IMAGEBLIT
diff --git a/drivers/staging/intel_sst/intelmid_v1_control.c b/drivers/staging/intel_sst/intelmid_v1_control.c
index 9cc15c1c18d..1ea81421805 100644
--- a/drivers/staging/intel_sst/intelmid_v1_control.c
+++ b/drivers/staging/intel_sst/intelmid_v1_control.c
@@ -28,6 +28,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/pci.h>
+#include <linux/delay.h>
#include <linux/file.h>
#include <asm/mrst.h>
#include <sound/pcm.h>
diff --git a/drivers/staging/intel_sst/intelmid_v2_control.c b/drivers/staging/intel_sst/intelmid_v2_control.c
index 26d815a67eb..3c6b3abff3c 100644
--- a/drivers/staging/intel_sst/intelmid_v2_control.c
+++ b/drivers/staging/intel_sst/intelmid_v2_control.c
@@ -29,6 +29,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/pci.h>
+#include <linux/delay.h>
#include <linux/file.h>
#include "intel_sst.h"
#include "intelmid_snd_control.h"
diff --git a/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c b/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c
index b5d21f6497f..22c04eabed4 100644
--- a/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c
+++ b/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c
@@ -12,6 +12,7 @@
*/
#include <linux/cs5535.h>
#include <linux/gpio.h>
+#include <linux/delay.h>
#include <asm/olpc.h>
#include "olpc_dcon.h"
diff --git a/drivers/staging/pohmelfs/inode.c b/drivers/staging/pohmelfs/inode.c
index c93ef207b0b..c0f0ac7c1cd 100644
--- a/drivers/staging/pohmelfs/inode.c
+++ b/drivers/staging/pohmelfs/inode.c
@@ -29,6 +29,7 @@
#include <linux/slab.h>
#include <linux/statfs.h>
#include <linux/writeback.h>
+#include <linux/prefetch.h>
#include "netfs.h"
diff --git a/drivers/staging/rt2860/common/cmm_data_pci.c b/drivers/staging/rt2860/common/cmm_data_pci.c
index bef0bbd8cef..f01a51c381f 100644
--- a/drivers/staging/rt2860/common/cmm_data_pci.c
+++ b/drivers/staging/rt2860/common/cmm_data_pci.c
@@ -444,7 +444,7 @@ int RTMPCheckRxError(struct rt_rtmp_adapter *pAd,
return (NDIS_STATUS_FAILURE);
}
}
- /* Drop not U2M frames, can't's drop here because we will drop beacon in this case */
+ /* Drop not U2M frames, can't drop here because we will drop beacon in this case */
/* I am kind of doubting the U2M bit operation */
/* if (pRxD->U2M == 0) */
/* return(NDIS_STATUS_FAILURE); */
diff --git a/drivers/staging/rt2860/common/cmm_data_usb.c b/drivers/staging/rt2860/common/cmm_data_usb.c
index 5637857ae9e..83a62faa7e5 100644
--- a/drivers/staging/rt2860/common/cmm_data_usb.c
+++ b/drivers/staging/rt2860/common/cmm_data_usb.c
@@ -860,7 +860,7 @@ int RTMPCheckRxError(struct rt_rtmp_adapter *pAd,
DBGPRINT_RAW(RT_DEBUG_ERROR, ("received packet too long\n"));
return NDIS_STATUS_FAILURE;
}
- /* Drop not U2M frames, can't's drop here because we will drop beacon in this case */
+ /* Drop not U2M frames, can't drop here because we will drop beacon in this case */
/* I am kind of doubting the U2M bit operation */
/* if (pRxD->U2M == 0) */
/* return(NDIS_STATUS_FAILURE); */
diff --git a/drivers/staging/rts_pstor/debug.h b/drivers/staging/rts_pstor/debug.h
index e1408b0e7ae..ab305be96fb 100644
--- a/drivers/staging/rts_pstor/debug.h
+++ b/drivers/staging/rts_pstor/debug.h
@@ -28,7 +28,7 @@
#define RTSX_STOR "rts_pstor: "
-#if CONFIG_RTS_PSTOR_DEBUG
+#ifdef CONFIG_RTS_PSTOR_DEBUG
#define RTSX_DEBUGP(x...) printk(KERN_DEBUG RTSX_STOR x)
#define RTSX_DEBUGPN(x...) printk(KERN_DEBUG x)
#define RTSX_DEBUGPX(x...) printk(x)
diff --git a/drivers/staging/rts_pstor/ms.c b/drivers/staging/rts_pstor/ms.c
index 810e170894f..d89795c6a3a 100644
--- a/drivers/staging/rts_pstor/ms.c
+++ b/drivers/staging/rts_pstor/ms.c
@@ -23,6 +23,7 @@
#include <linux/blkdev.h>
#include <linux/kthread.h>
#include <linux/sched.h>
+#include <linux/vmalloc.h>
#include "rtsx.h"
#include "rtsx_transport.h"
diff --git a/drivers/staging/rts_pstor/rtsx_chip.c b/drivers/staging/rts_pstor/rtsx_chip.c
index d2f1c715a68..4e60780ea80 100644
--- a/drivers/staging/rts_pstor/rtsx_chip.c
+++ b/drivers/staging/rts_pstor/rtsx_chip.c
@@ -24,6 +24,7 @@
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/workqueue.h>
+#include <linux/vmalloc.h>
#include "rtsx.h"
#include "rtsx_transport.h"
@@ -1311,11 +1312,11 @@ void rtsx_polling_func(struct rtsx_chip *chip)
#ifdef SUPPORT_OCP
if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
- #if CONFIG_RTS_PSTOR_DEBUG
+#ifdef CONFIG_RTS_PSTOR_DEBUG
if (chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER | MS_OC_NOW | MS_OC_EVER)) {
RTSX_DEBUGP("Over current, OCPSTAT is 0x%x\n", chip->ocp_stat);
}
- #endif
+#endif
if (chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER)) {
if (chip->card_exist & SD_CARD) {
diff --git a/drivers/staging/rts_pstor/rtsx_scsi.c b/drivers/staging/rts_pstor/rtsx_scsi.c
index 20c2464a20f..7de1fae443f 100644
--- a/drivers/staging/rts_pstor/rtsx_scsi.c
+++ b/drivers/staging/rts_pstor/rtsx_scsi.c
@@ -23,6 +23,7 @@
#include <linux/blkdev.h>
#include <linux/kthread.h>
#include <linux/sched.h>
+#include <linux/vmalloc.h>
#include "rtsx.h"
#include "rtsx_transport.h"
diff --git a/drivers/staging/rts_pstor/sd.c b/drivers/staging/rts_pstor/sd.c
index 8d066bd428c..b1277a6c7a8 100644
--- a/drivers/staging/rts_pstor/sd.c
+++ b/drivers/staging/rts_pstor/sd.c
@@ -909,7 +909,7 @@ static int sd_change_phase(struct rtsx_chip *chip, u8 sample_point, u8 tune_dir)
RTSX_WRITE_REG(chip, SD_VPCLK0_CTL, PHASE_NOT_RESET, PHASE_NOT_RESET);
RTSX_WRITE_REG(chip, CLK_CTL, CHANGE_CLK, 0);
} else {
-#if CONFIG_RTS_PSTOR_DEBUG
+#ifdef CONFIG_RTS_PSTOR_DEBUG
rtsx_read_register(chip, SD_VP_CTL, &val);
RTSX_DEBUGP("SD_VP_CTL: 0x%x\n", val);
rtsx_read_register(chip, SD_DCMPS_CTL, &val);
@@ -958,7 +958,7 @@ static int sd_change_phase(struct rtsx_chip *chip, u8 sample_point, u8 tune_dir)
return STATUS_SUCCESS;
Fail:
-#if CONFIG_RTS_PSTOR_DEBUG
+#ifdef CONFIG_RTS_PSTOR_DEBUG
rtsx_read_register(chip, SD_VP_CTL, &val);
RTSX_DEBUGP("SD_VP_CTL: 0x%x\n", val);
rtsx_read_register(chip, SD_DCMPS_CTL, &val);
diff --git a/drivers/staging/rts_pstor/trace.h b/drivers/staging/rts_pstor/trace.h
index 2c668bae6ff..bc83b49a4eb 100644
--- a/drivers/staging/rts_pstor/trace.h
+++ b/drivers/staging/rts_pstor/trace.h
@@ -82,7 +82,7 @@ do { \
#define TRACE_GOTO(chip, label) goto label
#endif
-#if CONFIG_RTS_PSTOR_DEBUG
+#ifdef CONFIG_RTS_PSTOR_DEBUG
static inline void rtsx_dump(u8 *buf, int buf_len)
{
int i;
diff --git a/drivers/staging/rts_pstor/xd.c b/drivers/staging/rts_pstor/xd.c
index 7bcd468b8f2..9f3add1e8f5 100644
--- a/drivers/staging/rts_pstor/xd.c
+++ b/drivers/staging/rts_pstor/xd.c
@@ -23,6 +23,7 @@
#include <linux/blkdev.h>
#include <linux/kthread.h>
#include <linux/sched.h>
+#include <linux/vmalloc.h>
#include "rtsx.h"
#include "rtsx_transport.h"
diff --git a/drivers/staging/samsung-laptop/Kconfig b/drivers/staging/samsung-laptop/Kconfig
deleted file mode 100644
index f27c60864c2..00000000000
--- a/drivers/staging/samsung-laptop/Kconfig
+++ /dev/null
@@ -1,10 +0,0 @@
-config SAMSUNG_LAPTOP
- tristate "Samsung Laptop driver"
- default n
- depends on RFKILL && BACKLIGHT_CLASS_DEVICE && X86
- help
- This module implements a driver for the N128 Samsung Laptop
- providing control over the Wireless LED and the LCD backlight
-
- To compile this driver as a module, choose
- M here: the module will be called samsung-laptop.
diff --git a/drivers/staging/samsung-laptop/Makefile b/drivers/staging/samsung-laptop/Makefile
deleted file mode 100644
index 3c6f4204521..00000000000
--- a/drivers/staging/samsung-laptop/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o
diff --git a/drivers/staging/samsung-laptop/TODO b/drivers/staging/samsung-laptop/TODO
deleted file mode 100644
index f7a6d589916..00000000000
--- a/drivers/staging/samsung-laptop/TODO
+++ /dev/null
@@ -1,5 +0,0 @@
-TODO:
- - review from other developers
- - figure out ACPI video issues
-
-Please send patches to Greg Kroah-Hartman <gregkh@suse.de>
diff --git a/drivers/staging/samsung-laptop/samsung-laptop.c b/drivers/staging/samsung-laptop/samsung-laptop.c
deleted file mode 100644
index 25294462b8b..00000000000
--- a/drivers/staging/samsung-laptop/samsung-laptop.c
+++ /dev/null
@@ -1,843 +0,0 @@
-/*
- * Samsung Laptop driver
- *
- * Copyright (C) 2009,2011 Greg Kroah-Hartman (gregkh@suse.de)
- * Copyright (C) 2009,2011 Novell Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- */
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/backlight.h>
-#include <linux/fb.h>
-#include <linux/dmi.h>
-#include <linux/platform_device.h>
-#include <linux/rfkill.h>
-
-/*
- * This driver is needed because a number of Samsung laptops do not hook
- * their control settings through ACPI. So we have to poke around in the
- * BIOS to do things like brightness values, and "special" key controls.
- */
-
-/*
- * We have 0 - 8 as valid brightness levels. The specs say that level 0 should
- * be reserved by the BIOS (which really doesn't make much sense), we tell
- * userspace that the value is 0 - 7 and then just tell the hardware 1 - 8
- */
-#define MAX_BRIGHT 0x07
-
-
-#define SABI_IFACE_MAIN 0x00
-#define SABI_IFACE_SUB 0x02
-#define SABI_IFACE_COMPLETE 0x04
-#define SABI_IFACE_DATA 0x05
-
-/* Structure to get data back to the calling function */
-struct sabi_retval {
- u8 retval[20];
-};
-
-struct sabi_header_offsets {
- u8 port;
- u8 re_mem;
- u8 iface_func;
- u8 en_mem;
- u8 data_offset;
- u8 data_segment;
-};
-
-struct sabi_commands {
- /*
- * Brightness is 0 - 8, as described above.
- * Value 0 is for the BIOS to use
- */
- u8 get_brightness;
- u8 set_brightness;
-
- /*
- * first byte:
- * 0x00 - wireless is off
- * 0x01 - wireless is on
- * second byte:
- * 0x02 - 3G is off
- * 0x03 - 3G is on
- * TODO, verify 3G is correct, that doesn't seem right...
- */
- u8 get_wireless_button;
- u8 set_wireless_button;
-
- /* 0 is off, 1 is on */
- u8 get_backlight;
- u8 set_backlight;
-
- /*
- * 0x80 or 0x00 - no action
- * 0x81 - recovery key pressed
- */
- u8 get_recovery_mode;
- u8 set_recovery_mode;
-
- /*
- * on seclinux: 0 is low, 1 is high,
- * on swsmi: 0 is normal, 1 is silent, 2 is turbo
- */
- u8 get_performance_level;
- u8 set_performance_level;
-
- /*
- * Tell the BIOS that Linux is running on this machine.
- * 81 is on, 80 is off
- */
- u8 set_linux;
-};
-
-struct sabi_performance_level {
- const char *name;
- u8 value;
-};
-
-struct sabi_config {
- const char *test_string;
- u16 main_function;
- const struct sabi_header_offsets header_offsets;
- const struct sabi_commands commands;
- const struct sabi_performance_level performance_levels[4];
- u8 min_brightness;
- u8 max_brightness;
-};
-
-static const struct sabi_config sabi_configs[] = {
- {
- .test_string = "SECLINUX",
-
- .main_function = 0x4c49,
-
- .header_offsets = {
- .port = 0x00,
- .re_mem = 0x02,
- .iface_func = 0x03,
- .en_mem = 0x04,
- .data_offset = 0x05,
- .data_segment = 0x07,
- },
-
- .commands = {
- .get_brightness = 0x00,
- .set_brightness = 0x01,
-
- .get_wireless_button = 0x02,
- .set_wireless_button = 0x03,
-
- .get_backlight = 0x04,
- .set_backlight = 0x05,
-
- .get_recovery_mode = 0x06,
- .set_recovery_mode = 0x07,
-
- .get_performance_level = 0x08,
- .set_performance_level = 0x09,
-
- .set_linux = 0x0a,
- },
-
- .performance_levels = {
- {
- .name = "silent",
- .value = 0,
- },
- {
- .name = "normal",
- .value = 1,
- },
- { },
- },
- .min_brightness = 1,
- .max_brightness = 8,
- },
- {
- .test_string = "SwSmi@",
-
- .main_function = 0x5843,
-
- .header_offsets = {
- .port = 0x00,
- .re_mem = 0x04,
- .iface_func = 0x02,
- .en_mem = 0x03,
- .data_offset = 0x05,
- .data_segment = 0x07,
- },
-
- .commands = {
- .get_brightness = 0x10,
- .set_brightness = 0x11,
-
- .get_wireless_button = 0x12,
- .set_wireless_button = 0x13,
-
- .get_backlight = 0x2d,
- .set_backlight = 0x2e,
-
- .get_recovery_mode = 0xff,
- .set_recovery_mode = 0xff,
-
- .get_performance_level = 0x31,
- .set_performance_level = 0x32,
-
- .set_linux = 0xff,
- },
-
- .performance_levels = {
- {
- .name = "normal",
- .value = 0,
- },
- {
- .name = "silent",
- .value = 1,
- },
- {
- .name = "overclock",
- .value = 2,
- },
- { },
- },
- .min_brightness = 0,
- .max_brightness = 8,
- },
- { },
-};
-
-static const struct sabi_config *sabi_config;
-
-static void __iomem *sabi;
-static void __iomem *sabi_iface;
-static void __iomem *f0000_segment;
-static struct backlight_device *backlight_device;
-static struct mutex sabi_mutex;
-static struct platform_device *sdev;
-static struct rfkill *rfk;
-
-static int force;
-module_param(force, bool, 0);
-MODULE_PARM_DESC(force,
- "Disable the DMI check and forces the driver to be loaded");
-
-static int debug;
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
-
-static int sabi_get_command(u8 command, struct sabi_retval *sretval)
-{
- int retval = 0;
- u16 port = readw(sabi + sabi_config->header_offsets.port);
- u8 complete, iface_data;
-
- mutex_lock(&sabi_mutex);
-
- /* enable memory to be able to write to it */
- outb(readb(sabi + sabi_config->header_offsets.en_mem), port);
-
- /* write out the command */
- writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN);
- writew(command, sabi_iface + SABI_IFACE_SUB);
- writeb(0, sabi_iface + SABI_IFACE_COMPLETE);
- outb(readb(sabi + sabi_config->header_offsets.iface_func), port);
-
- /* write protect memory to make it safe */
- outb(readb(sabi + sabi_config->header_offsets.re_mem), port);
-
- /* see if the command actually succeeded */
- complete = readb(sabi_iface + SABI_IFACE_COMPLETE);
- iface_data = readb(sabi_iface + SABI_IFACE_DATA);
- if (complete != 0xaa || iface_data == 0xff) {
- pr_warn("SABI get command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n",
- command, complete, iface_data);
- retval = -EINVAL;
- goto exit;
- }
- /*
- * Save off the data into a structure so the caller use it.
- * Right now we only want the first 4 bytes,
- * There are commands that need more, but not for the ones we
- * currently care about.
- */
- sretval->retval[0] = readb(sabi_iface + SABI_IFACE_DATA);
- sretval->retval[1] = readb(sabi_iface + SABI_IFACE_DATA + 1);
- sretval->retval[2] = readb(sabi_iface + SABI_IFACE_DATA + 2);
- sretval->retval[3] = readb(sabi_iface + SABI_IFACE_DATA + 3);
-
-exit:
- mutex_unlock(&sabi_mutex);
- return retval;
-
-}
-
-static int sabi_set_command(u8 command, u8 data)
-{
- int retval = 0;
- u16 port = readw(sabi + sabi_config->header_offsets.port);
- u8 complete, iface_data;
-
- mutex_lock(&sabi_mutex);
-
- /* enable memory to be able to write to it */
- outb(readb(sabi + sabi_config->header_offsets.en_mem), port);
-
- /* write out the command */
- writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN);
- writew(command, sabi_iface + SABI_IFACE_SUB);
- writeb(0, sabi_iface + SABI_IFACE_COMPLETE);
- writeb(data, sabi_iface + SABI_IFACE_DATA);
- outb(readb(sabi + sabi_config->header_offsets.iface_func), port);
-
- /* write protect memory to make it safe */
- outb(readb(sabi + sabi_config->header_offsets.re_mem), port);
-
- /* see if the command actually succeeded */
- complete = readb(sabi_iface + SABI_IFACE_COMPLETE);
- iface_data = readb(sabi_iface + SABI_IFACE_DATA);
- if (complete != 0xaa || iface_data == 0xff) {
- pr_warn("SABI set command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n",
- command, complete, iface_data);
- retval = -EINVAL;
- }
-
- mutex_unlock(&sabi_mutex);
- return retval;
-}
-
-static void test_backlight(void)
-{
- struct sabi_retval sretval;
-
- sabi_get_command(sabi_config->commands.get_backlight, &sretval);
- printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
-
- sabi_set_command(sabi_config->commands.set_backlight, 0);
- printk(KERN_DEBUG "backlight should be off\n");
-
- sabi_get_command(sabi_config->commands.get_backlight, &sretval);
- printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
-
- msleep(1000);
-
- sabi_set_command(sabi_config->commands.set_backlight, 1);
- printk(KERN_DEBUG "backlight should be on\n");
-
- sabi_get_command(sabi_config->commands.get_backlight, &sretval);
- printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
-}
-
-static void test_wireless(void)
-{
- struct sabi_retval sretval;
-
- sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
- printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
-
- sabi_set_command(sabi_config->commands.set_wireless_button, 0);
- printk(KERN_DEBUG "wireless led should be off\n");
-
- sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
- printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
-
- msleep(1000);
-
- sabi_set_command(sabi_config->commands.set_wireless_button, 1);
- printk(KERN_DEBUG "wireless led should be on\n");
-
- sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
- printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
-}
-
-static u8 read_brightness(void)
-{
- struct sabi_retval sretval;
- int user_brightness = 0;
- int retval;
-
- retval = sabi_get_command(sabi_config->commands.get_brightness,
- &sretval);
- if (!retval) {
- user_brightness = sretval.retval[0];
- if (user_brightness != 0)
- user_brightness -= sabi_config->min_brightness;
- }
- return user_brightness;
-}
-
-static void set_brightness(u8 user_brightness)
-{
- u8 user_level = user_brightness - sabi_config->min_brightness;
-
- sabi_set_command(sabi_config->commands.set_brightness, user_level);
-}
-
-static int get_brightness(struct backlight_device *bd)
-{
- return (int)read_brightness();
-}
-
-static int update_status(struct backlight_device *bd)
-{
- set_brightness(bd->props.brightness);
-
- if (bd->props.power == FB_BLANK_UNBLANK)
- sabi_set_command(sabi_config->commands.set_backlight, 1);
- else
- sabi_set_command(sabi_config->commands.set_backlight, 0);
- return 0;
-}
-
-static const struct backlight_ops backlight_ops = {
- .get_brightness = get_brightness,
- .update_status = update_status,
-};
-
-static int rfkill_set(void *data, bool blocked)
-{
- /* Do something with blocked...*/
- /*
- * blocked == false is on
- * blocked == true is off
- */
- if (blocked)
- sabi_set_command(sabi_config->commands.set_wireless_button, 0);
- else
- sabi_set_command(sabi_config->commands.set_wireless_button, 1);
-
- return 0;
-}
-
-static struct rfkill_ops rfkill_ops = {
- .set_block = rfkill_set,
-};
-
-static int init_wireless(struct platform_device *sdev)
-{
- int retval;
-
- rfk = rfkill_alloc("samsung-wifi", &sdev->dev, RFKILL_TYPE_WLAN,
- &rfkill_ops, NULL);
- if (!rfk)
- return -ENOMEM;
-
- retval = rfkill_register(rfk);
- if (retval) {
- rfkill_destroy(rfk);
- return -ENODEV;
- }
-
- return 0;
-}
-
-static void destroy_wireless(void)
-{
- rfkill_unregister(rfk);
- rfkill_destroy(rfk);
-}
-
-static ssize_t get_performance_level(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct sabi_retval sretval;
- int retval;
- int i;
-
- /* Read the state */
- retval = sabi_get_command(sabi_config->commands.get_performance_level,
- &sretval);
- if (retval)
- return retval;
-
- /* The logic is backwards, yeah, lots of fun... */
- for (i = 0; sabi_config->performance_levels[i].name; ++i) {
- if (sretval.retval[0] == sabi_config->performance_levels[i].value)
- return sprintf(buf, "%s\n", sabi_config->performance_levels[i].name);
- }
- return sprintf(buf, "%s\n", "unknown");
-}
-
-static ssize_t set_performance_level(struct device *dev,
- struct device_attribute *attr, const char *buf,
- size_t count)
-{
- if (count >= 1) {
- int i;
- for (i = 0; sabi_config->performance_levels[i].name; ++i) {
- const struct sabi_performance_level *level =
- &sabi_config->performance_levels[i];
- if (!strncasecmp(level->name, buf, strlen(level->name))) {
- sabi_set_command(sabi_config->commands.set_performance_level,
- level->value);
- break;
- }
- }
- if (!sabi_config->performance_levels[i].name)
- return -EINVAL;
- }
- return count;
-}
-static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO,
- get_performance_level, set_performance_level);
-
-
-static int __init dmi_check_cb(const struct dmi_system_id *id)
-{
- pr_info("found laptop model '%s'\n",
- id->ident);
- return 0;
-}
-
-static struct dmi_system_id __initdata samsung_dmi_table[] = {
- {
- .ident = "N128",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N128"),
- DMI_MATCH(DMI_BOARD_NAME, "N128"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N130",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N130"),
- DMI_MATCH(DMI_BOARD_NAME, "N130"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "X125",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "X125"),
- DMI_MATCH(DMI_BOARD_NAME, "X125"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "X120/X170",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "X120/X170"),
- DMI_MATCH(DMI_BOARD_NAME, "X120/X170"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "NC10",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "NC10"),
- DMI_MATCH(DMI_BOARD_NAME, "NC10"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "NP-Q45",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"),
- DMI_MATCH(DMI_BOARD_NAME, "SQ45S70S"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "X360",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
- DMI_MATCH(DMI_BOARD_NAME, "X360"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R410 Plus",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R410P"),
- DMI_MATCH(DMI_BOARD_NAME, "R460"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R518",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R518"),
- DMI_MATCH(DMI_BOARD_NAME, "R518"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R519/R719",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R519/R719"),
- DMI_MATCH(DMI_BOARD_NAME, "R519/R719"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N150/N210/N220/N230",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220/N230"),
- DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220/N230"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N150P/N210P/N220P",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N150P/N210P/N220P"),
- DMI_MATCH(DMI_BOARD_NAME, "N150P/N210P/N220P"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R530/R730",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R530/R730"),
- DMI_MATCH(DMI_BOARD_NAME, "R530/R730"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "NF110/NF210/NF310",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"),
- DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N145P/N250P/N260P",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"),
- DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R70/R71",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R70/R71"),
- DMI_MATCH(DMI_BOARD_NAME, "R70/R71"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "P460",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "P460"),
- DMI_MATCH(DMI_BOARD_NAME, "P460"),
- },
- .callback = dmi_check_cb,
- },
- { },
-};
-MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
-
-static int find_signature(void __iomem *memcheck, const char *testStr)
-{
- int i = 0;
- int loca;
-
- for (loca = 0; loca < 0xffff; loca++) {
- char temp = readb(memcheck + loca);
-
- if (temp == testStr[i]) {
- if (i == strlen(testStr)-1)
- break;
- ++i;
- } else {
- i = 0;
- }
- }
- return loca;
-}
-
-static int __init samsung_init(void)
-{
- struct backlight_properties props;
- struct sabi_retval sretval;
- unsigned int ifaceP;
- int i;
- int loca;
- int retval;
-
- mutex_init(&sabi_mutex);
-
- if (!force && !dmi_check_system(samsung_dmi_table))
- return -ENODEV;
-
- f0000_segment = ioremap_nocache(0xf0000, 0xffff);
- if (!f0000_segment) {
- pr_err("Can't map the segment at 0xf0000\n");
- return -EINVAL;
- }
-
- /* Try to find one of the signatures in memory to find the header */
- for (i = 0; sabi_configs[i].test_string != 0; ++i) {
- sabi_config = &sabi_configs[i];
- loca = find_signature(f0000_segment, sabi_config->test_string);
- if (loca != 0xffff)
- break;
- }
-
- if (loca == 0xffff) {
- pr_err("This computer does not support SABI\n");
- goto error_no_signature;
- }
-
- /* point to the SMI port Number */
- loca += 1;
- sabi = (f0000_segment + loca);
-
- if (debug) {
- printk(KERN_DEBUG "This computer supports SABI==%x\n",
- loca + 0xf0000 - 6);
- printk(KERN_DEBUG "SABI header:\n");
- printk(KERN_DEBUG " SMI Port Number = 0x%04x\n",
- readw(sabi + sabi_config->header_offsets.port));
- printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n",
- readb(sabi + sabi_config->header_offsets.iface_func));
- printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n",
- readb(sabi + sabi_config->header_offsets.en_mem));
- printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n",
- readb(sabi + sabi_config->header_offsets.re_mem));
- printk(KERN_DEBUG " SABI data offset = 0x%04x\n",
- readw(sabi + sabi_config->header_offsets.data_offset));
- printk(KERN_DEBUG " SABI data segment = 0x%04x\n",
- readw(sabi + sabi_config->header_offsets.data_segment));
- }
-
- /* Get a pointer to the SABI Interface */
- ifaceP = (readw(sabi + sabi_config->header_offsets.data_segment) & 0x0ffff) << 4;
- ifaceP += readw(sabi + sabi_config->header_offsets.data_offset) & 0x0ffff;
- sabi_iface = ioremap_nocache(ifaceP, 16);
- if (!sabi_iface) {
- pr_err("Can't remap %x\n", ifaceP);
- goto exit;
- }
- if (debug) {
- printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP);
- printk(KERN_DEBUG "sabi_iface = %p\n", sabi_iface);
-
- test_backlight();
- test_wireless();
-
- retval = sabi_get_command(sabi_config->commands.get_brightness,
- &sretval);
- printk(KERN_DEBUG "brightness = 0x%02x\n", sretval.retval[0]);
- }
-
- /* Turn on "Linux" mode in the BIOS */
- if (sabi_config->commands.set_linux != 0xff) {
- retval = sabi_set_command(sabi_config->commands.set_linux,
- 0x81);
- if (retval) {
- pr_warn("Linux mode was not set!\n");
- goto error_no_platform;
- }
- }
-
- /* knock up a platform device to hang stuff off of */
- sdev = platform_device_register_simple("samsung", -1, NULL, 0);
- if (IS_ERR(sdev))
- goto error_no_platform;
-
- /* create a backlight device to talk to this one */
- memset(&props, 0, sizeof(struct backlight_properties));
- props.type = BACKLIGHT_PLATFORM;
- props.max_brightness = sabi_config->max_brightness;
- backlight_device = backlight_device_register("samsung", &sdev->dev,
- NULL, &backlight_ops,
- &props);
- if (IS_ERR(backlight_device))
- goto error_no_backlight;
-
- backlight_device->props.brightness = read_brightness();
- backlight_device->props.power = FB_BLANK_UNBLANK;
- backlight_update_status(backlight_device);
-
- retval = init_wireless(sdev);
- if (retval)
- goto error_no_rfk;
-
- retval = device_create_file(&sdev->dev, &dev_attr_performance_level);
- if (retval)
- goto error_file_create;
-
-exit:
- return 0;
-
-error_file_create:
- destroy_wireless();
-
-error_no_rfk:
- backlight_device_unregister(backlight_device);
-
-error_no_backlight:
- platform_device_unregister(sdev);
-
-error_no_platform:
- iounmap(sabi_iface);
-
-error_no_signature:
- iounmap(f0000_segment);
- return -EINVAL;
-}
-
-static void __exit samsung_exit(void)
-{
- /* Turn off "Linux" mode in the BIOS */
- if (sabi_config->commands.set_linux != 0xff)
- sabi_set_command(sabi_config->commands.set_linux, 0x80);
-
- device_remove_file(&sdev->dev, &dev_attr_performance_level);
- backlight_device_unregister(backlight_device);
- destroy_wireless();
- iounmap(sabi_iface);
- iounmap(f0000_segment);
- platform_device_unregister(sdev);
-}
-
-module_init(samsung_init);
-module_exit(samsung_exit);
-
-MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>");
-MODULE_DESCRIPTION("Samsung Backlight driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/solo6x10/Kconfig b/drivers/staging/solo6x10/Kconfig
index 2cf77c94086..03dcac4ea4d 100644
--- a/drivers/staging/solo6x10/Kconfig
+++ b/drivers/staging/solo6x10/Kconfig
@@ -2,6 +2,7 @@ config SOLO6X10
tristate "Softlogic 6x10 MPEG codec cards"
depends on PCI && VIDEO_DEV && SND && I2C
select VIDEOBUF_DMA_SG
+ select SND_PCM
---help---
This driver supports the Softlogic based MPEG-4 and h.264 codec
codec cards.
diff --git a/drivers/staging/spectra/ffsport.c b/drivers/staging/spectra/ffsport.c
index 20dae73d3b7..506547b603e 100644
--- a/drivers/staging/spectra/ffsport.c
+++ b/drivers/staging/spectra/ffsport.c
@@ -653,7 +653,7 @@ static int SBD_setup_device(struct spectra_nand_dev *dev, int which)
}
dev->queue->queuedata = dev;
- /* As Linux block layer does't support >4KB hardware sector, */
+ /* As Linux block layer doesn't support >4KB hardware sector, */
/* Here we force report 512 byte hardware sector size to Kernel */
blk_queue_logical_block_size(dev->queue, 512);
diff --git a/drivers/staging/tidspbridge/dynload/cload.c b/drivers/staging/tidspbridge/dynload/cload.c
index 5cecd237e3f..fe1ef0addb0 100644
--- a/drivers/staging/tidspbridge/dynload/cload.c
+++ b/drivers/staging/tidspbridge/dynload/cload.c
@@ -718,7 +718,7 @@ static void dload_symbols(struct dload_state *dlthis)
* as a temporary for .dllview record construction.
* Allocate storage for the whole table. Add 1 to the section count
* in case a trampoline section is auto-generated as well as the
- * size of the trampoline section name so DLLView does't get lost.
+ * size of the trampoline section name so DLLView doesn't get lost.
*/
siz = sym_count * sizeof(struct local_symbol);
diff --git a/drivers/staging/tty/specialix.c b/drivers/staging/tty/specialix.c
index cb24c6d999d..5c3598ec745 100644
--- a/drivers/staging/tty/specialix.c
+++ b/drivers/staging/tty/specialix.c
@@ -978,7 +978,7 @@ static void sx_change_speed(struct specialix_board *bp,
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_CAR, port_No(port));
- /* The Specialix board does't implement the RTS lines.
+ /* The Specialix board doesn't implement the RTS lines.
They are used to set the IRQ level. Don't touch them. */
if (sx_crtscts(tty))
port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
diff --git a/drivers/staging/usbip/vhci_hcd.c b/drivers/staging/usbip/vhci_hcd.c
index 0f02a4b12ae..4f4f13321f4 100644
--- a/drivers/staging/usbip/vhci_hcd.c
+++ b/drivers/staging/usbip/vhci_hcd.c
@@ -876,8 +876,10 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
}
/* kill threads related to this sdev, if v.c. exists */
- kthread_stop(vdev->ud.tcp_rx);
- kthread_stop(vdev->ud.tcp_tx);
+ if (vdev->ud.tcp_rx)
+ kthread_stop(vdev->ud.tcp_rx);
+ if (vdev->ud.tcp_tx)
+ kthread_stop(vdev->ud.tcp_tx);
usbip_uinfo("stop threads\n");
@@ -949,9 +951,6 @@ static void vhci_device_init(struct vhci_device *vdev)
{
memset(vdev, 0, sizeof(*vdev));
- vdev->ud.tcp_rx = kthread_create(vhci_rx_loop, &vdev->ud, "vhci_rx");
- vdev->ud.tcp_tx = kthread_create(vhci_tx_loop, &vdev->ud, "vhci_tx");
-
vdev->ud.side = USBIP_VHCI;
vdev->ud.status = VDEV_ST_NULL;
/* vdev->ud.lock = SPIN_LOCK_UNLOCKED; */
@@ -1139,7 +1138,7 @@ static int vhci_hcd_probe(struct platform_device *pdev)
usbip_uerr("create hcd failed\n");
return -ENOMEM;
}
-
+ hcd->has_tt = 1;
/* this is private data for vhci_hcd */
the_controller = hcd_to_vhci(hcd);
diff --git a/drivers/staging/usbip/vhci_sysfs.c b/drivers/staging/usbip/vhci_sysfs.c
index 3f2459f3041..e2dadbd5ef1 100644
--- a/drivers/staging/usbip/vhci_sysfs.c
+++ b/drivers/staging/usbip/vhci_sysfs.c
@@ -21,6 +21,7 @@
#include "vhci.h"
#include <linux/in.h>
+#include <linux/kthread.h>
/* TODO: refine locking ?*/
@@ -220,13 +221,13 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
vdev->ud.tcp_socket = socket;
vdev->ud.status = VDEV_ST_NOTASSIGNED;
- wake_up_process(vdev->ud.tcp_rx);
- wake_up_process(vdev->ud.tcp_tx);
-
spin_unlock(&vdev->ud.lock);
spin_unlock(&the_controller->lock);
/* end the lock */
+ vdev->ud.tcp_rx = kthread_run(vhci_rx_loop, &vdev->ud, "vhci_rx");
+ vdev->ud.tcp_tx = kthread_run(vhci_tx_loop, &vdev->ud, "vhci_tx");
+
rh_port_connect(rhport, speed);
return count;
diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c
index 6a71f52c59b..76378397b76 100644
--- a/drivers/staging/wlan-ng/cfg80211.c
+++ b/drivers/staging/wlan-ng/cfg80211.c
@@ -273,7 +273,7 @@ exit:
}
int prism2_set_default_key(struct wiphy *wiphy, struct net_device *dev,
- u8 key_index)
+ u8 key_index, bool unicast, bool multicast)
{
wlandevice_t *wlandev = dev->ml_priv;
diff --git a/drivers/target/Kconfig b/drivers/target/Kconfig
index 9ef2dbbfa62..5cb0f0ef6af 100644
--- a/drivers/target/Kconfig
+++ b/drivers/target/Kconfig
@@ -30,5 +30,6 @@ config TCM_PSCSI
passthrough access to Linux/SCSI device
source "drivers/target/loopback/Kconfig"
+source "drivers/target/tcm_fc/Kconfig"
endif
diff --git a/drivers/target/Makefile b/drivers/target/Makefile
index 1178bbfc68f..21df808a992 100644
--- a/drivers/target/Makefile
+++ b/drivers/target/Makefile
@@ -24,3 +24,5 @@ obj-$(CONFIG_TCM_PSCSI) += target_core_pscsi.o
# Fabric modules
obj-$(CONFIG_LOOPBACK_TARGET) += loopback/
+
+obj-$(CONFIG_TCM_FC) += tcm_fc/
diff --git a/drivers/target/tcm_fc/Kconfig b/drivers/target/tcm_fc/Kconfig
new file mode 100644
index 00000000000..40caf458e89
--- /dev/null
+++ b/drivers/target/tcm_fc/Kconfig
@@ -0,0 +1,5 @@
+config TCM_FC
+ tristate "TCM_FC fabric Plugin"
+ depends on LIBFC
+ help
+ Say Y here to enable the TCM FC plugin for accessing FC fabrics in TCM
diff --git a/drivers/target/tcm_fc/Makefile b/drivers/target/tcm_fc/Makefile
new file mode 100644
index 00000000000..7a5c2b64cf6
--- /dev/null
+++ b/drivers/target/tcm_fc/Makefile
@@ -0,0 +1,15 @@
+EXTRA_CFLAGS += -I$(srctree)/drivers/target/ \
+ -I$(srctree)/drivers/scsi/ \
+ -I$(srctree)/include/scsi/ \
+ -I$(srctree)/drivers/target/tcm_fc/
+
+tcm_fc-y += tfc_cmd.o \
+ tfc_conf.o \
+ tfc_io.o \
+ tfc_sess.o
+
+obj-$(CONFIG_TCM_FC) += tcm_fc.o
+
+ifdef CONFIGFS_TCM_FC_DEBUG
+EXTRA_CFLAGS += -DTCM_FC_DEBUG
+endif
diff --git a/drivers/target/tcm_fc/tcm_fc.h b/drivers/target/tcm_fc/tcm_fc.h
new file mode 100644
index 00000000000..defff32b788
--- /dev/null
+++ b/drivers/target/tcm_fc/tcm_fc.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2010 Cisco Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef __TCM_FC_H__
+#define __TCM_FC_H__
+
+#define FT_VERSION "0.3"
+
+#define FT_NAMELEN 32 /* length of ASCII WWPNs including pad */
+#define FT_TPG_NAMELEN 32 /* max length of TPG name */
+#define FT_LUN_NAMELEN 32 /* max length of LUN name */
+
+/*
+ * Debug options.
+ */
+#define FT_DEBUG_CONF 0x01 /* configuration messages */
+#define FT_DEBUG_SESS 0x02 /* session messages */
+#define FT_DEBUG_TM 0x04 /* TM operations */
+#define FT_DEBUG_IO 0x08 /* I/O commands */
+#define FT_DEBUG_DATA 0x10 /* Data transfer */
+
+extern unsigned int ft_debug_logging; /* debug options */
+
+#define FT_DEBUG(mask, fmt, args...) \
+ do { \
+ if (ft_debug_logging & (mask)) \
+ printk(KERN_INFO "tcm_fc: %s: " fmt, \
+ __func__, ##args); \
+ } while (0)
+
+#define FT_CONF_DBG(fmt, args...) FT_DEBUG(FT_DEBUG_CONF, fmt, ##args)
+#define FT_SESS_DBG(fmt, args...) FT_DEBUG(FT_DEBUG_SESS, fmt, ##args)
+#define FT_TM_DBG(fmt, args...) FT_DEBUG(FT_DEBUG_TM, fmt, ##args)
+#define FT_IO_DBG(fmt, args...) FT_DEBUG(FT_DEBUG_IO, fmt, ##args)
+#define FT_DATA_DBG(fmt, args...) FT_DEBUG(FT_DEBUG_DATA, fmt, ##args)
+
+struct ft_transport_id {
+ __u8 format;
+ __u8 __resvd1[7];
+ __u8 wwpn[8];
+ __u8 __resvd2[8];
+} __attribute__((__packed__));
+
+/*
+ * Session (remote port).
+ */
+struct ft_sess {
+ u32 port_id; /* for hash lookup use only */
+ u32 params;
+ u16 max_frame; /* maximum frame size */
+ u64 port_name; /* port name for transport ID */
+ struct ft_tport *tport;
+ struct se_session *se_sess;
+ struct hlist_node hash; /* linkage in ft_sess_hash table */
+ struct rcu_head rcu;
+ struct kref kref; /* ref for hash and outstanding I/Os */
+};
+
+/*
+ * Hash table of sessions per local port.
+ * Hash lookup by remote port FC_ID.
+ */
+#define FT_SESS_HASH_BITS 6
+#define FT_SESS_HASH_SIZE (1 << FT_SESS_HASH_BITS)
+
+/*
+ * Per local port data.
+ * This is created only after a TPG exists that allows target function
+ * for the local port. If the TPG exists, this is allocated when
+ * we're notified that the local port has been created, or when
+ * the first PRLI provider callback is received.
+ */
+struct ft_tport {
+ struct fc_lport *lport;
+ struct ft_tpg *tpg; /* NULL if TPG deleted before tport */
+ u32 sess_count; /* number of sessions in hash */
+ struct rcu_head rcu;
+ struct hlist_head hash[FT_SESS_HASH_SIZE]; /* list of sessions */
+};
+
+/*
+ * Node ID and authentication.
+ */
+struct ft_node_auth {
+ u64 port_name;
+ u64 node_name;
+};
+
+/*
+ * Node ACL for FC remote port session.
+ */
+struct ft_node_acl {
+ struct ft_node_auth node_auth;
+ struct se_node_acl se_node_acl;
+};
+
+struct ft_lun {
+ u32 index;
+ char name[FT_LUN_NAMELEN];
+};
+
+/*
+ * Target portal group (local port).
+ */
+struct ft_tpg {
+ u32 index;
+ struct ft_lport_acl *lport_acl;
+ struct ft_tport *tport; /* active tport or NULL */
+ struct list_head list; /* linkage in ft_lport_acl tpg_list */
+ struct list_head lun_list; /* head of LUNs */
+ struct se_portal_group se_tpg;
+ struct task_struct *thread; /* processing thread */
+ struct se_queue_obj qobj; /* queue for processing thread */
+};
+
+struct ft_lport_acl {
+ u64 wwpn;
+ char name[FT_NAMELEN];
+ struct list_head list;
+ struct list_head tpg_list;
+ struct se_wwn fc_lport_wwn;
+};
+
+enum ft_cmd_state {
+ FC_CMD_ST_NEW = 0,
+ FC_CMD_ST_REJ
+};
+
+/*
+ * Commands
+ */
+struct ft_cmd {
+ enum ft_cmd_state state;
+ u16 lun; /* LUN from request */
+ struct ft_sess *sess; /* session held for cmd */
+ struct fc_seq *seq; /* sequence in exchange mgr */
+ struct se_cmd se_cmd; /* Local TCM I/O descriptor */
+ struct fc_frame *req_frame;
+ unsigned char *cdb; /* pointer to CDB inside frame */
+ u32 write_data_len; /* data received on writes */
+ struct se_queue_req se_req;
+ /* Local sense buffer */
+ unsigned char ft_sense_buffer[TRANSPORT_SENSE_BUFFER];
+ u32 was_ddp_setup:1; /* Set only if ddp is setup */
+ struct scatterlist *sg; /* Set only if DDP is setup */
+ u32 sg_cnt; /* No. of item in scatterlist */
+};
+
+extern struct list_head ft_lport_list;
+extern struct mutex ft_lport_lock;
+extern struct fc4_prov ft_prov;
+extern struct target_fabric_configfs *ft_configfs;
+
+/*
+ * Fabric methods.
+ */
+
+/*
+ * Session ops.
+ */
+void ft_sess_put(struct ft_sess *);
+int ft_sess_shutdown(struct se_session *);
+void ft_sess_close(struct se_session *);
+void ft_sess_stop(struct se_session *, int, int);
+int ft_sess_logged_in(struct se_session *);
+u32 ft_sess_get_index(struct se_session *);
+u32 ft_sess_get_port_name(struct se_session *, unsigned char *, u32);
+void ft_sess_set_erl0(struct se_session *);
+
+void ft_lport_add(struct fc_lport *, void *);
+void ft_lport_del(struct fc_lport *, void *);
+int ft_lport_notify(struct notifier_block *, unsigned long, void *);
+
+/*
+ * IO methods.
+ */
+void ft_check_stop_free(struct se_cmd *);
+void ft_release_cmd(struct se_cmd *);
+int ft_queue_status(struct se_cmd *);
+int ft_queue_data_in(struct se_cmd *);
+int ft_write_pending(struct se_cmd *);
+int ft_write_pending_status(struct se_cmd *);
+u32 ft_get_task_tag(struct se_cmd *);
+int ft_get_cmd_state(struct se_cmd *);
+void ft_new_cmd_failure(struct se_cmd *);
+int ft_queue_tm_resp(struct se_cmd *);
+int ft_is_state_remove(struct se_cmd *);
+
+/*
+ * other internal functions.
+ */
+int ft_thread(void *);
+void ft_recv_req(struct ft_sess *, struct fc_frame *);
+struct ft_tpg *ft_lport_find_tpg(struct fc_lport *);
+struct ft_node_acl *ft_acl_get(struct ft_tpg *, struct fc_rport_priv *);
+
+void ft_recv_write_data(struct ft_cmd *, struct fc_frame *);
+void ft_dump_cmd(struct ft_cmd *, const char *caller);
+
+ssize_t ft_format_wwn(char *, size_t, u64);
+
+#endif /* __TCM_FC_H__ */
diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c
new file mode 100644
index 00000000000..49e51778f73
--- /dev/null
+++ b/drivers/target/tcm_fc/tfc_cmd.c
@@ -0,0 +1,696 @@
+/*
+ * Copyright (c) 2010 Cisco Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* XXX TBD some includes may be extraneous */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <generated/utsrelease.h>
+#include <linux/utsname.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/hash.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_transport.h>
+#include <target/target_core_fabric_ops.h>
+#include <target/target_core_device.h>
+#include <target/target_core_tpg.h>
+#include <target/target_core_configfs.h>
+#include <target/target_core_base.h>
+#include <target/target_core_tmr.h>
+#include <target/configfs_macros.h>
+
+#include "tcm_fc.h"
+
+/*
+ * Dump cmd state for debugging.
+ */
+void ft_dump_cmd(struct ft_cmd *cmd, const char *caller)
+{
+ struct fc_exch *ep;
+ struct fc_seq *sp;
+ struct se_cmd *se_cmd;
+ struct se_mem *mem;
+ struct se_transport_task *task;
+
+ if (!(ft_debug_logging & FT_DEBUG_IO))
+ return;
+
+ se_cmd = &cmd->se_cmd;
+ printk(KERN_INFO "%s: cmd %p state %d sess %p seq %p se_cmd %p\n",
+ caller, cmd, cmd->state, cmd->sess, cmd->seq, se_cmd);
+ printk(KERN_INFO "%s: cmd %p cdb %p\n",
+ caller, cmd, cmd->cdb);
+ printk(KERN_INFO "%s: cmd %p lun %d\n", caller, cmd, cmd->lun);
+
+ task = T_TASK(se_cmd);
+ printk(KERN_INFO "%s: cmd %p task %p se_num %u buf %p len %u se_cmd_flags <0x%x>\n",
+ caller, cmd, task, task->t_tasks_se_num,
+ task->t_task_buf, se_cmd->data_length, se_cmd->se_cmd_flags);
+ if (task->t_mem_list)
+ list_for_each_entry(mem, task->t_mem_list, se_list)
+ printk(KERN_INFO "%s: cmd %p mem %p page %p "
+ "len 0x%x off 0x%x\n",
+ caller, cmd, mem,
+ mem->se_page, mem->se_len, mem->se_off);
+ sp = cmd->seq;
+ if (sp) {
+ ep = fc_seq_exch(sp);
+ printk(KERN_INFO "%s: cmd %p sid %x did %x "
+ "ox_id %x rx_id %x seq_id %x e_stat %x\n",
+ caller, cmd, ep->sid, ep->did, ep->oxid, ep->rxid,
+ sp->id, ep->esb_stat);
+ }
+ print_hex_dump(KERN_INFO, "ft_dump_cmd ", DUMP_PREFIX_NONE,
+ 16, 4, cmd->cdb, MAX_COMMAND_SIZE, 0);
+}
+
+/*
+ * Get LUN from CDB.
+ */
+static int ft_get_lun_for_cmd(struct ft_cmd *cmd, u8 *lunp)
+{
+ u64 lun;
+
+ lun = lunp[1];
+ switch (lunp[0] >> 6) {
+ case 0:
+ break;
+ case 1:
+ lun |= (lunp[0] & 0x3f) << 8;
+ break;
+ default:
+ return -1;
+ }
+ if (lun >= TRANSPORT_MAX_LUNS_PER_TPG)
+ return -1;
+ cmd->lun = lun;
+ return transport_get_lun_for_cmd(&cmd->se_cmd, NULL, lun);
+}
+
+static void ft_queue_cmd(struct ft_sess *sess, struct ft_cmd *cmd)
+{
+ struct se_queue_obj *qobj;
+ unsigned long flags;
+
+ qobj = &sess->tport->tpg->qobj;
+ spin_lock_irqsave(&qobj->cmd_queue_lock, flags);
+ list_add_tail(&cmd->se_req.qr_list, &qobj->qobj_list);
+ spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags);
+ atomic_inc(&qobj->queue_cnt);
+ wake_up_interruptible(&qobj->thread_wq);
+}
+
+static struct ft_cmd *ft_dequeue_cmd(struct se_queue_obj *qobj)
+{
+ unsigned long flags;
+ struct se_queue_req *qr;
+
+ spin_lock_irqsave(&qobj->cmd_queue_lock, flags);
+ if (list_empty(&qobj->qobj_list)) {
+ spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags);
+ return NULL;
+ }
+ qr = list_first_entry(&qobj->qobj_list, struct se_queue_req, qr_list);
+ list_del(&qr->qr_list);
+ atomic_dec(&qobj->queue_cnt);
+ spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags);
+ return container_of(qr, struct ft_cmd, se_req);
+}
+
+static void ft_free_cmd(struct ft_cmd *cmd)
+{
+ struct fc_frame *fp;
+ struct fc_lport *lport;
+
+ if (!cmd)
+ return;
+ fp = cmd->req_frame;
+ lport = fr_dev(fp);
+ if (fr_seq(fp))
+ lport->tt.seq_release(fr_seq(fp));
+ fc_frame_free(fp);
+ ft_sess_put(cmd->sess); /* undo get from lookup at recv */
+ kfree(cmd);
+}
+
+void ft_release_cmd(struct se_cmd *se_cmd)
+{
+ struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
+
+ ft_free_cmd(cmd);
+}
+
+void ft_check_stop_free(struct se_cmd *se_cmd)
+{
+ transport_generic_free_cmd(se_cmd, 0, 1, 0);
+}
+
+/*
+ * Send response.
+ */
+int ft_queue_status(struct se_cmd *se_cmd)
+{
+ struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
+ struct fc_frame *fp;
+ struct fcp_resp_with_ext *fcp;
+ struct fc_lport *lport;
+ struct fc_exch *ep;
+ size_t len;
+
+ ft_dump_cmd(cmd, __func__);
+ ep = fc_seq_exch(cmd->seq);
+ lport = ep->lp;
+ len = sizeof(*fcp) + se_cmd->scsi_sense_length;
+ fp = fc_frame_alloc(lport, len);
+ if (!fp) {
+ /* XXX shouldn't just drop it - requeue and retry? */
+ return 0;
+ }
+ fcp = fc_frame_payload_get(fp, len);
+ memset(fcp, 0, len);
+ fcp->resp.fr_status = se_cmd->scsi_status;
+
+ len = se_cmd->scsi_sense_length;
+ if (len) {
+ fcp->resp.fr_flags |= FCP_SNS_LEN_VAL;
+ fcp->ext.fr_sns_len = htonl(len);
+ memcpy((fcp + 1), se_cmd->sense_buffer, len);
+ }
+
+ /*
+ * Test underflow and overflow with one mask. Usually both are off.
+ * Bidirectional commands are not handled yet.
+ */
+ if (se_cmd->se_cmd_flags & (SCF_OVERFLOW_BIT | SCF_UNDERFLOW_BIT)) {
+ if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT)
+ fcp->resp.fr_flags |= FCP_RESID_OVER;
+ else
+ fcp->resp.fr_flags |= FCP_RESID_UNDER;
+ fcp->ext.fr_resid = cpu_to_be32(se_cmd->residual_count);
+ }
+
+ /*
+ * Send response.
+ */
+ cmd->seq = lport->tt.seq_start_next(cmd->seq);
+ fc_fill_fc_hdr(fp, FC_RCTL_DD_CMD_STATUS, ep->did, ep->sid, FC_TYPE_FCP,
+ FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ, 0);
+
+ lport->tt.seq_send(lport, cmd->seq, fp);
+ lport->tt.exch_done(cmd->seq);
+ return 0;
+}
+
+int ft_write_pending_status(struct se_cmd *se_cmd)
+{
+ struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
+
+ return cmd->write_data_len != se_cmd->data_length;
+}
+
+/*
+ * Send TX_RDY (transfer ready).
+ */
+int ft_write_pending(struct se_cmd *se_cmd)
+{
+ struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
+ struct fc_frame *fp;
+ struct fcp_txrdy *txrdy;
+ struct fc_lport *lport;
+ struct fc_exch *ep;
+ struct fc_frame_header *fh;
+ u32 f_ctl;
+
+ ft_dump_cmd(cmd, __func__);
+
+ ep = fc_seq_exch(cmd->seq);
+ lport = ep->lp;
+ fp = fc_frame_alloc(lport, sizeof(*txrdy));
+ if (!fp)
+ return PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES;
+
+ txrdy = fc_frame_payload_get(fp, sizeof(*txrdy));
+ memset(txrdy, 0, sizeof(*txrdy));
+ txrdy->ft_burst_len = htonl(se_cmd->data_length);
+
+ cmd->seq = lport->tt.seq_start_next(cmd->seq);
+ fc_fill_fc_hdr(fp, FC_RCTL_DD_DATA_DESC, ep->did, ep->sid, FC_TYPE_FCP,
+ FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+
+ fh = fc_frame_header_get(fp);
+ f_ctl = ntoh24(fh->fh_f_ctl);
+
+ /* Only if it is 'Exchange Responder' */
+ if (f_ctl & FC_FC_EX_CTX) {
+ /* Target is 'exchange responder' and sending XFER_READY
+ * to 'exchange initiator (initiator)'
+ */
+ if ((ep->xid <= lport->lro_xid) &&
+ (fh->fh_r_ctl == FC_RCTL_DD_DATA_DESC)) {
+ if (se_cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB) {
+ /*
+ * Map se_mem list to scatterlist, so that
+ * DDP can be setup. DDP setup function require
+ * scatterlist. se_mem_list is internal to
+ * TCM/LIO target
+ */
+ transport_do_task_sg_chain(se_cmd);
+ cmd->sg = T_TASK(se_cmd)->t_tasks_sg_chained;
+ cmd->sg_cnt =
+ T_TASK(se_cmd)->t_tasks_sg_chained_no;
+ }
+ if (cmd->sg && lport->tt.ddp_setup(lport, ep->xid,
+ cmd->sg, cmd->sg_cnt))
+ cmd->was_ddp_setup = 1;
+ }
+ }
+ lport->tt.seq_send(lport, cmd->seq, fp);
+ return 0;
+}
+
+u32 ft_get_task_tag(struct se_cmd *se_cmd)
+{
+ struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
+
+ return fc_seq_exch(cmd->seq)->rxid;
+}
+
+int ft_get_cmd_state(struct se_cmd *se_cmd)
+{
+ struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
+
+ return cmd->state;
+}
+
+int ft_is_state_remove(struct se_cmd *se_cmd)
+{
+ return 0; /* XXX TBD */
+}
+
+void ft_new_cmd_failure(struct se_cmd *se_cmd)
+{
+ /* XXX TBD */
+ printk(KERN_INFO "%s: se_cmd %p\n", __func__, se_cmd);
+}
+
+/*
+ * FC sequence response handler for follow-on sequences (data) and aborts.
+ */
+static void ft_recv_seq(struct fc_seq *sp, struct fc_frame *fp, void *arg)
+{
+ struct ft_cmd *cmd = arg;
+ struct fc_frame_header *fh;
+
+ if (IS_ERR(fp)) {
+ /* XXX need to find cmd if queued */
+ cmd->se_cmd.t_state = TRANSPORT_REMOVE;
+ cmd->seq = NULL;
+ transport_generic_free_cmd(&cmd->se_cmd, 0, 1, 0);
+ return;
+ }
+
+ fh = fc_frame_header_get(fp);
+
+ switch (fh->fh_r_ctl) {
+ case FC_RCTL_DD_SOL_DATA: /* write data */
+ ft_recv_write_data(cmd, fp);
+ break;
+ case FC_RCTL_DD_UNSOL_CTL: /* command */
+ case FC_RCTL_DD_SOL_CTL: /* transfer ready */
+ case FC_RCTL_DD_DATA_DESC: /* transfer ready */
+ default:
+ printk(KERN_INFO "%s: unhandled frame r_ctl %x\n",
+ __func__, fh->fh_r_ctl);
+ fc_frame_free(fp);
+ transport_generic_free_cmd(&cmd->se_cmd, 0, 1, 0);
+ break;
+ }
+}
+
+/*
+ * Send a FCP response including SCSI status and optional FCP rsp_code.
+ * status is SAM_STAT_GOOD (zero) iff code is valid.
+ * This is used in error cases, such as allocation failures.
+ */
+static void ft_send_resp_status(struct fc_lport *lport,
+ const struct fc_frame *rx_fp,
+ u32 status, enum fcp_resp_rsp_codes code)
+{
+ struct fc_frame *fp;
+ struct fc_seq *sp;
+ const struct fc_frame_header *fh;
+ size_t len;
+ struct fcp_resp_with_ext *fcp;
+ struct fcp_resp_rsp_info *info;
+
+ fh = fc_frame_header_get(rx_fp);
+ FT_IO_DBG("FCP error response: did %x oxid %x status %x code %x\n",
+ ntoh24(fh->fh_s_id), ntohs(fh->fh_ox_id), status, code);
+ len = sizeof(*fcp);
+ if (status == SAM_STAT_GOOD)
+ len += sizeof(*info);
+ fp = fc_frame_alloc(lport, len);
+ if (!fp)
+ return;
+ fcp = fc_frame_payload_get(fp, len);
+ memset(fcp, 0, len);
+ fcp->resp.fr_status = status;
+ if (status == SAM_STAT_GOOD) {
+ fcp->ext.fr_rsp_len = htonl(sizeof(*info));
+ fcp->resp.fr_flags |= FCP_RSP_LEN_VAL;
+ info = (struct fcp_resp_rsp_info *)(fcp + 1);
+ info->rsp_code = code;
+ }
+
+ fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_DD_CMD_STATUS, 0);
+ sp = fr_seq(fp);
+ if (sp)
+ lport->tt.seq_send(lport, sp, fp);
+ else
+ lport->tt.frame_send(lport, fp);
+}
+
+/*
+ * Send error or task management response.
+ * Always frees the cmd and associated state.
+ */
+static void ft_send_resp_code(struct ft_cmd *cmd, enum fcp_resp_rsp_codes code)
+{
+ ft_send_resp_status(cmd->sess->tport->lport,
+ cmd->req_frame, SAM_STAT_GOOD, code);
+ ft_free_cmd(cmd);
+}
+
+/*
+ * Handle Task Management Request.
+ */
+static void ft_send_tm(struct ft_cmd *cmd)
+{
+ struct se_tmr_req *tmr;
+ struct fcp_cmnd *fcp;
+ u8 tm_func;
+
+ fcp = fc_frame_payload_get(cmd->req_frame, sizeof(*fcp));
+
+ switch (fcp->fc_tm_flags) {
+ case FCP_TMF_LUN_RESET:
+ tm_func = TMR_LUN_RESET;
+ if (ft_get_lun_for_cmd(cmd, fcp->fc_lun) < 0) {
+ ft_dump_cmd(cmd, __func__);
+ transport_send_check_condition_and_sense(&cmd->se_cmd,
+ cmd->se_cmd.scsi_sense_reason, 0);
+ ft_sess_put(cmd->sess);
+ return;
+ }
+ break;
+ case FCP_TMF_TGT_RESET:
+ tm_func = TMR_TARGET_WARM_RESET;
+ break;
+ case FCP_TMF_CLR_TASK_SET:
+ tm_func = TMR_CLEAR_TASK_SET;
+ break;
+ case FCP_TMF_ABT_TASK_SET:
+ tm_func = TMR_ABORT_TASK_SET;
+ break;
+ case FCP_TMF_CLR_ACA:
+ tm_func = TMR_CLEAR_ACA;
+ break;
+ default:
+ /*
+ * FCP4r01 indicates having a combination of
+ * tm_flags set is invalid.
+ */
+ FT_TM_DBG("invalid FCP tm_flags %x\n", fcp->fc_tm_flags);
+ ft_send_resp_code(cmd, FCP_CMND_FIELDS_INVALID);
+ return;
+ }
+
+ FT_TM_DBG("alloc tm cmd fn %d\n", tm_func);
+ tmr = core_tmr_alloc_req(&cmd->se_cmd, cmd, tm_func);
+ if (!tmr) {
+ FT_TM_DBG("alloc failed\n");
+ ft_send_resp_code(cmd, FCP_TMF_FAILED);
+ return;
+ }
+ cmd->se_cmd.se_tmr_req = tmr;
+ transport_generic_handle_tmr(&cmd->se_cmd);
+}
+
+/*
+ * Send status from completed task management request.
+ */
+int ft_queue_tm_resp(struct se_cmd *se_cmd)
+{
+ struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
+ struct se_tmr_req *tmr = se_cmd->se_tmr_req;
+ enum fcp_resp_rsp_codes code;
+
+ switch (tmr->response) {
+ case TMR_FUNCTION_COMPLETE:
+ code = FCP_TMF_CMPL;
+ break;
+ case TMR_LUN_DOES_NOT_EXIST:
+ code = FCP_TMF_INVALID_LUN;
+ break;
+ case TMR_FUNCTION_REJECTED:
+ code = FCP_TMF_REJECTED;
+ break;
+ case TMR_TASK_DOES_NOT_EXIST:
+ case TMR_TASK_STILL_ALLEGIANT:
+ case TMR_TASK_FAILOVER_NOT_SUPPORTED:
+ case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED:
+ case TMR_FUNCTION_AUTHORIZATION_FAILED:
+ default:
+ code = FCP_TMF_FAILED;
+ break;
+ }
+ FT_TM_DBG("tmr fn %d resp %d fcp code %d\n",
+ tmr->function, tmr->response, code);
+ ft_send_resp_code(cmd, code);
+ return 0;
+}
+
+/*
+ * Handle incoming FCP command.
+ */
+static void ft_recv_cmd(struct ft_sess *sess, struct fc_frame *fp)
+{
+ struct ft_cmd *cmd;
+ struct fc_lport *lport = sess->tport->lport;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC);
+ if (!cmd)
+ goto busy;
+ cmd->sess = sess;
+ cmd->seq = lport->tt.seq_assign(lport, fp);
+ if (!cmd->seq) {
+ kfree(cmd);
+ goto busy;
+ }
+ cmd->req_frame = fp; /* hold frame during cmd */
+ ft_queue_cmd(sess, cmd);
+ return;
+
+busy:
+ FT_IO_DBG("cmd or seq allocation failure - sending BUSY\n");
+ ft_send_resp_status(lport, fp, SAM_STAT_BUSY, 0);
+ fc_frame_free(fp);
+ ft_sess_put(sess); /* undo get from lookup */
+}
+
+
+/*
+ * Handle incoming FCP frame.
+ * Caller has verified that the frame is type FCP.
+ */
+void ft_recv_req(struct ft_sess *sess, struct fc_frame *fp)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+
+ switch (fh->fh_r_ctl) {
+ case FC_RCTL_DD_UNSOL_CMD: /* command */
+ ft_recv_cmd(sess, fp);
+ break;
+ case FC_RCTL_DD_SOL_DATA: /* write data */
+ case FC_RCTL_DD_UNSOL_CTL:
+ case FC_RCTL_DD_SOL_CTL:
+ case FC_RCTL_DD_DATA_DESC: /* transfer ready */
+ case FC_RCTL_ELS4_REQ: /* SRR, perhaps */
+ default:
+ printk(KERN_INFO "%s: unhandled frame r_ctl %x\n",
+ __func__, fh->fh_r_ctl);
+ fc_frame_free(fp);
+ ft_sess_put(sess); /* undo get from lookup */
+ break;
+ }
+}
+
+/*
+ * Send new command to target.
+ */
+static void ft_send_cmd(struct ft_cmd *cmd)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(cmd->req_frame);
+ struct se_cmd *se_cmd;
+ struct fcp_cmnd *fcp;
+ int data_dir;
+ u32 data_len;
+ int task_attr;
+ int ret;
+
+ fcp = fc_frame_payload_get(cmd->req_frame, sizeof(*fcp));
+ if (!fcp)
+ goto err;
+
+ if (fcp->fc_flags & FCP_CFL_LEN_MASK)
+ goto err; /* not handling longer CDBs yet */
+
+ if (fcp->fc_tm_flags) {
+ task_attr = FCP_PTA_SIMPLE;
+ data_dir = DMA_NONE;
+ data_len = 0;
+ } else {
+ switch (fcp->fc_flags & (FCP_CFL_RDDATA | FCP_CFL_WRDATA)) {
+ case 0:
+ data_dir = DMA_NONE;
+ break;
+ case FCP_CFL_RDDATA:
+ data_dir = DMA_FROM_DEVICE;
+ break;
+ case FCP_CFL_WRDATA:
+ data_dir = DMA_TO_DEVICE;
+ break;
+ case FCP_CFL_WRDATA | FCP_CFL_RDDATA:
+ goto err; /* TBD not supported by tcm_fc yet */
+ }
+
+ /* FCP_PTA_ maps 1:1 to TASK_ATTR_ */
+ task_attr = fcp->fc_pri_ta & FCP_PTA_MASK;
+ data_len = ntohl(fcp->fc_dl);
+ cmd->cdb = fcp->fc_cdb;
+ }
+
+ se_cmd = &cmd->se_cmd;
+ /*
+ * Initialize struct se_cmd descriptor from target_core_mod
+ * infrastructure
+ */
+ transport_init_se_cmd(se_cmd, &ft_configfs->tf_ops, cmd->sess->se_sess,
+ data_len, data_dir, task_attr,
+ &cmd->ft_sense_buffer[0]);
+ /*
+ * Check for FCP task management flags
+ */
+ if (fcp->fc_tm_flags) {
+ ft_send_tm(cmd);
+ return;
+ }
+
+ fc_seq_exch(cmd->seq)->lp->tt.seq_set_resp(cmd->seq, ft_recv_seq, cmd);
+
+ ret = ft_get_lun_for_cmd(cmd, fcp->fc_lun);
+ if (ret < 0) {
+ ft_dump_cmd(cmd, __func__);
+ transport_send_check_condition_and_sense(&cmd->se_cmd,
+ cmd->se_cmd.scsi_sense_reason, 0);
+ return;
+ }
+
+ ret = transport_generic_allocate_tasks(se_cmd, cmd->cdb);
+
+ FT_IO_DBG("r_ctl %x alloc task ret %d\n", fh->fh_r_ctl, ret);
+ ft_dump_cmd(cmd, __func__);
+
+ if (ret == -1) {
+ transport_send_check_condition_and_sense(se_cmd,
+ TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0);
+ transport_generic_free_cmd(se_cmd, 0, 1, 0);
+ return;
+ }
+ if (ret == -2) {
+ if (se_cmd->se_cmd_flags & SCF_SCSI_RESERVATION_CONFLICT)
+ ft_queue_status(se_cmd);
+ else
+ transport_send_check_condition_and_sense(se_cmd,
+ se_cmd->scsi_sense_reason, 0);
+ transport_generic_free_cmd(se_cmd, 0, 1, 0);
+ return;
+ }
+ transport_generic_handle_cdb(se_cmd);
+ return;
+
+err:
+ ft_send_resp_code(cmd, FCP_CMND_FIELDS_INVALID);
+ return;
+}
+
+/*
+ * Handle request in the command thread.
+ */
+static void ft_exec_req(struct ft_cmd *cmd)
+{
+ FT_IO_DBG("cmd state %x\n", cmd->state);
+ switch (cmd->state) {
+ case FC_CMD_ST_NEW:
+ ft_send_cmd(cmd);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Processing thread.
+ * Currently one thread per tpg.
+ */
+int ft_thread(void *arg)
+{
+ struct ft_tpg *tpg = arg;
+ struct se_queue_obj *qobj = &tpg->qobj;
+ struct ft_cmd *cmd;
+ int ret;
+
+ set_user_nice(current, -20);
+
+ while (!kthread_should_stop()) {
+ ret = wait_event_interruptible(qobj->thread_wq,
+ atomic_read(&qobj->queue_cnt) || kthread_should_stop());
+ if (ret < 0 || kthread_should_stop())
+ goto out;
+ cmd = ft_dequeue_cmd(qobj);
+ if (cmd)
+ ft_exec_req(cmd);
+ }
+
+out:
+ return 0;
+}
diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c
new file mode 100644
index 00000000000..fcdbbffe88c
--- /dev/null
+++ b/drivers/target/tcm_fc/tfc_conf.c
@@ -0,0 +1,677 @@
+/*******************************************************************************
+ * Filename: tcm_fc.c
+ *
+ * This file contains the configfs implementation for TCM_fc fabric node.
+ * Based on tcm_loop_configfs.c
+ *
+ * Copyright (c) 2010 Cisco Systems, Inc.
+ * Copyright (c) 2009,2010 Rising Tide, Inc.
+ * Copyright (c) 2009,2010 Linux-iSCSI.org
+ *
+ * Copyright (c) 2009,2010 Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <generated/utsrelease.h>
+#include <linux/utsname.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/libfc.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_transport.h>
+#include <target/target_core_fabric_ops.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_fabric_lib.h>
+#include <target/target_core_device.h>
+#include <target/target_core_tpg.h>
+#include <target/target_core_configfs.h>
+#include <target/target_core_base.h>
+#include <target/configfs_macros.h>
+
+#include "tcm_fc.h"
+
+struct target_fabric_configfs *ft_configfs;
+
+LIST_HEAD(ft_lport_list);
+DEFINE_MUTEX(ft_lport_lock);
+
+unsigned int ft_debug_logging;
+module_param_named(debug_logging, ft_debug_logging, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
+
+/*
+ * Parse WWN.
+ * If strict, we require lower-case hex and colon separators to be sure
+ * the name is the same as what would be generated by ft_format_wwn()
+ * so the name and wwn are mapped one-to-one.
+ */
+static ssize_t ft_parse_wwn(const char *name, u64 *wwn, int strict)
+{
+ const char *cp;
+ char c;
+ u32 nibble;
+ u32 byte = 0;
+ u32 pos = 0;
+ u32 err;
+
+ *wwn = 0;
+ for (cp = name; cp < &name[FT_NAMELEN - 1]; cp++) {
+ c = *cp;
+ if (c == '\n' && cp[1] == '\0')
+ continue;
+ if (strict && pos++ == 2 && byte++ < 7) {
+ pos = 0;
+ if (c == ':')
+ continue;
+ err = 1;
+ goto fail;
+ }
+ if (c == '\0') {
+ err = 2;
+ if (strict && byte != 8)
+ goto fail;
+ return cp - name;
+ }
+ err = 3;
+ if (isdigit(c))
+ nibble = c - '0';
+ else if (isxdigit(c) && (islower(c) || !strict))
+ nibble = tolower(c) - 'a' + 10;
+ else
+ goto fail;
+ *wwn = (*wwn << 4) | nibble;
+ }
+ err = 4;
+fail:
+ FT_CONF_DBG("err %u len %zu pos %u byte %u\n",
+ err, cp - name, pos, byte);
+ return -1;
+}
+
+ssize_t ft_format_wwn(char *buf, size_t len, u64 wwn)
+{
+ u8 b[8];
+
+ put_unaligned_be64(wwn, b);
+ return snprintf(buf, len,
+ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
+ b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
+}
+
+static ssize_t ft_wwn_show(void *arg, char *buf)
+{
+ u64 *wwn = arg;
+ ssize_t len;
+
+ len = ft_format_wwn(buf, PAGE_SIZE - 2, *wwn);
+ buf[len++] = '\n';
+ return len;
+}
+
+static ssize_t ft_wwn_store(void *arg, const char *buf, size_t len)
+{
+ ssize_t ret;
+ u64 wwn;
+
+ ret = ft_parse_wwn(buf, &wwn, 0);
+ if (ret > 0)
+ *(u64 *)arg = wwn;
+ return ret;
+}
+
+/*
+ * ACL auth ops.
+ */
+
+static ssize_t ft_nacl_show_port_name(
+ struct se_node_acl *se_nacl,
+ char *page)
+{
+ struct ft_node_acl *acl = container_of(se_nacl,
+ struct ft_node_acl, se_node_acl);
+
+ return ft_wwn_show(&acl->node_auth.port_name, page);
+}
+
+static ssize_t ft_nacl_store_port_name(
+ struct se_node_acl *se_nacl,
+ const char *page,
+ size_t count)
+{
+ struct ft_node_acl *acl = container_of(se_nacl,
+ struct ft_node_acl, se_node_acl);
+
+ return ft_wwn_store(&acl->node_auth.port_name, page, count);
+}
+
+TF_NACL_BASE_ATTR(ft, port_name, S_IRUGO | S_IWUSR);
+
+static ssize_t ft_nacl_show_node_name(
+ struct se_node_acl *se_nacl,
+ char *page)
+{
+ struct ft_node_acl *acl = container_of(se_nacl,
+ struct ft_node_acl, se_node_acl);
+
+ return ft_wwn_show(&acl->node_auth.node_name, page);
+}
+
+static ssize_t ft_nacl_store_node_name(
+ struct se_node_acl *se_nacl,
+ const char *page,
+ size_t count)
+{
+ struct ft_node_acl *acl = container_of(se_nacl,
+ struct ft_node_acl, se_node_acl);
+
+ return ft_wwn_store(&acl->node_auth.node_name, page, count);
+}
+
+TF_NACL_BASE_ATTR(ft, node_name, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *ft_nacl_base_attrs[] = {
+ &ft_nacl_port_name.attr,
+ &ft_nacl_node_name.attr,
+ NULL,
+};
+
+/*
+ * ACL ops.
+ */
+
+/*
+ * Add ACL for an initiator. The ACL is named arbitrarily.
+ * The port_name and/or node_name are attributes.
+ */
+static struct se_node_acl *ft_add_acl(
+ struct se_portal_group *se_tpg,
+ struct config_group *group,
+ const char *name)
+{
+ struct ft_node_acl *acl;
+ struct ft_tpg *tpg;
+ u64 wwpn;
+ u32 q_depth;
+
+ FT_CONF_DBG("add acl %s\n", name);
+ tpg = container_of(se_tpg, struct ft_tpg, se_tpg);
+
+ if (ft_parse_wwn(name, &wwpn, 1) < 0)
+ return ERR_PTR(-EINVAL);
+
+ acl = kzalloc(sizeof(struct ft_node_acl), GFP_KERNEL);
+ if (!(acl))
+ return ERR_PTR(-ENOMEM);
+ acl->node_auth.port_name = wwpn;
+
+ q_depth = 32; /* XXX bogus default - get from tpg? */
+ return core_tpg_add_initiator_node_acl(&tpg->se_tpg,
+ &acl->se_node_acl, name, q_depth);
+}
+
+static void ft_del_acl(struct se_node_acl *se_acl)
+{
+ struct se_portal_group *se_tpg = se_acl->se_tpg;
+ struct ft_tpg *tpg;
+ struct ft_node_acl *acl = container_of(se_acl,
+ struct ft_node_acl, se_node_acl);
+
+ FT_CONF_DBG("del acl %s\n",
+ config_item_name(&se_acl->acl_group.cg_item));
+
+ tpg = container_of(se_tpg, struct ft_tpg, se_tpg);
+ FT_CONF_DBG("del acl %p se_acl %p tpg %p se_tpg %p\n",
+ acl, se_acl, tpg, &tpg->se_tpg);
+
+ core_tpg_del_initiator_node_acl(&tpg->se_tpg, se_acl, 1);
+ kfree(acl);
+}
+
+struct ft_node_acl *ft_acl_get(struct ft_tpg *tpg, struct fc_rport_priv *rdata)
+{
+ struct ft_node_acl *found = NULL;
+ struct ft_node_acl *acl;
+ struct se_portal_group *se_tpg = &tpg->se_tpg;
+ struct se_node_acl *se_acl;
+
+ spin_lock_bh(&se_tpg->acl_node_lock);
+ list_for_each_entry(se_acl, &se_tpg->acl_node_list, acl_list) {
+ acl = container_of(se_acl, struct ft_node_acl, se_node_acl);
+ FT_CONF_DBG("acl %p port_name %llx\n",
+ acl, (unsigned long long)acl->node_auth.port_name);
+ if (acl->node_auth.port_name == rdata->ids.port_name ||
+ acl->node_auth.node_name == rdata->ids.node_name) {
+ FT_CONF_DBG("acl %p port_name %llx matched\n", acl,
+ (unsigned long long)rdata->ids.port_name);
+ found = acl;
+ /* XXX need to hold onto ACL */
+ break;
+ }
+ }
+ spin_unlock_bh(&se_tpg->acl_node_lock);
+ return found;
+}
+
+struct se_node_acl *ft_tpg_alloc_fabric_acl(struct se_portal_group *se_tpg)
+{
+ struct ft_node_acl *acl;
+
+ acl = kzalloc(sizeof(*acl), GFP_KERNEL);
+ if (!(acl)) {
+ printk(KERN_ERR "Unable to allocate struct ft_node_acl\n");
+ return NULL;
+ }
+ FT_CONF_DBG("acl %p\n", acl);
+ return &acl->se_node_acl;
+}
+
+static void ft_tpg_release_fabric_acl(struct se_portal_group *se_tpg,
+ struct se_node_acl *se_acl)
+{
+ struct ft_node_acl *acl = container_of(se_acl,
+ struct ft_node_acl, se_node_acl);
+
+ FT_CONF_DBG(KERN_INFO "acl %p\n", acl);
+ kfree(acl);
+}
+
+/*
+ * local_port port_group (tpg) ops.
+ */
+static struct se_portal_group *ft_add_tpg(
+ struct se_wwn *wwn,
+ struct config_group *group,
+ const char *name)
+{
+ struct ft_lport_acl *lacl;
+ struct ft_tpg *tpg;
+ unsigned long index;
+ int ret;
+
+ FT_CONF_DBG("tcm_fc: add tpg %s\n", name);
+
+ /*
+ * Name must be "tpgt_" followed by the index.
+ */
+ if (strstr(name, "tpgt_") != name)
+ return NULL;
+ if (strict_strtoul(name + 5, 10, &index) || index > UINT_MAX)
+ return NULL;
+
+ lacl = container_of(wwn, struct ft_lport_acl, fc_lport_wwn);
+ tpg = kzalloc(sizeof(*tpg), GFP_KERNEL);
+ if (!tpg)
+ return NULL;
+ tpg->index = index;
+ tpg->lport_acl = lacl;
+ INIT_LIST_HEAD(&tpg->lun_list);
+ transport_init_queue_obj(&tpg->qobj);
+
+ ret = core_tpg_register(&ft_configfs->tf_ops, wwn, &tpg->se_tpg,
+ (void *)tpg, TRANSPORT_TPG_TYPE_NORMAL);
+ if (ret < 0) {
+ kfree(tpg);
+ return NULL;
+ }
+
+ tpg->thread = kthread_run(ft_thread, tpg, "ft_tpg%lu", index);
+ if (IS_ERR(tpg->thread)) {
+ kfree(tpg);
+ return NULL;
+ }
+
+ mutex_lock(&ft_lport_lock);
+ list_add_tail(&tpg->list, &lacl->tpg_list);
+ mutex_unlock(&ft_lport_lock);
+
+ return &tpg->se_tpg;
+}
+
+static void ft_del_tpg(struct se_portal_group *se_tpg)
+{
+ struct ft_tpg *tpg = container_of(se_tpg, struct ft_tpg, se_tpg);
+
+ FT_CONF_DBG("del tpg %s\n",
+ config_item_name(&tpg->se_tpg.tpg_group.cg_item));
+
+ kthread_stop(tpg->thread);
+
+ /* Wait for sessions to be freed thru RCU, for BUG_ON below */
+ synchronize_rcu();
+
+ mutex_lock(&ft_lport_lock);
+ list_del(&tpg->list);
+ if (tpg->tport) {
+ tpg->tport->tpg = NULL;
+ tpg->tport = NULL;
+ }
+ mutex_unlock(&ft_lport_lock);
+
+ core_tpg_deregister(se_tpg);
+ kfree(tpg);
+}
+
+/*
+ * Verify that an lport is configured to use the tcm_fc module, and return
+ * the target port group that should be used.
+ *
+ * The caller holds ft_lport_lock.
+ */
+struct ft_tpg *ft_lport_find_tpg(struct fc_lport *lport)
+{
+ struct ft_lport_acl *lacl;
+ struct ft_tpg *tpg;
+
+ list_for_each_entry(lacl, &ft_lport_list, list) {
+ if (lacl->wwpn == lport->wwpn) {
+ list_for_each_entry(tpg, &lacl->tpg_list, list)
+ return tpg; /* XXX for now return first entry */
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * target config instance ops.
+ */
+
+/*
+ * Add lport to allowed config.
+ * The name is the WWPN in lower-case ASCII, colon-separated bytes.
+ */
+static struct se_wwn *ft_add_lport(
+ struct target_fabric_configfs *tf,
+ struct config_group *group,
+ const char *name)
+{
+ struct ft_lport_acl *lacl;
+ struct ft_lport_acl *old_lacl;
+ u64 wwpn;
+
+ FT_CONF_DBG("add lport %s\n", name);
+ if (ft_parse_wwn(name, &wwpn, 1) < 0)
+ return NULL;
+ lacl = kzalloc(sizeof(*lacl), GFP_KERNEL);
+ if (!lacl)
+ return NULL;
+ lacl->wwpn = wwpn;
+ INIT_LIST_HEAD(&lacl->tpg_list);
+
+ mutex_lock(&ft_lport_lock);
+ list_for_each_entry(old_lacl, &ft_lport_list, list) {
+ if (old_lacl->wwpn == wwpn) {
+ mutex_unlock(&ft_lport_lock);
+ kfree(lacl);
+ return NULL;
+ }
+ }
+ list_add_tail(&lacl->list, &ft_lport_list);
+ ft_format_wwn(lacl->name, sizeof(lacl->name), wwpn);
+ mutex_unlock(&ft_lport_lock);
+
+ return &lacl->fc_lport_wwn;
+}
+
+static void ft_del_lport(struct se_wwn *wwn)
+{
+ struct ft_lport_acl *lacl = container_of(wwn,
+ struct ft_lport_acl, fc_lport_wwn);
+
+ FT_CONF_DBG("del lport %s\n",
+ config_item_name(&wwn->wwn_group.cg_item));
+ mutex_lock(&ft_lport_lock);
+ list_del(&lacl->list);
+ mutex_unlock(&ft_lport_lock);
+
+ kfree(lacl);
+}
+
+static ssize_t ft_wwn_show_attr_version(
+ struct target_fabric_configfs *tf,
+ char *page)
+{
+ return sprintf(page, "TCM FC " FT_VERSION " on %s/%s on "
+ ""UTS_RELEASE"\n", utsname()->sysname, utsname()->machine);
+}
+
+TF_WWN_ATTR_RO(ft, version);
+
+static struct configfs_attribute *ft_wwn_attrs[] = {
+ &ft_wwn_version.attr,
+ NULL,
+};
+
+static char *ft_get_fabric_name(void)
+{
+ return "fc";
+}
+
+static char *ft_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+ struct ft_tpg *tpg = se_tpg->se_tpg_fabric_ptr;
+
+ return tpg->lport_acl->name;
+}
+
+static u16 ft_get_tag(struct se_portal_group *se_tpg)
+{
+ struct ft_tpg *tpg = se_tpg->se_tpg_fabric_ptr;
+
+ /*
+ * This tag is used when forming SCSI Name identifier in EVPD=1 0x83
+ * to represent the SCSI Target Port.
+ */
+ return tpg->index;
+}
+
+static u32 ft_get_default_depth(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static int ft_check_false(struct se_portal_group *se_tpg)
+{
+ return 0;
+}
+
+static void ft_set_default_node_attr(struct se_node_acl *se_nacl)
+{
+}
+
+static u16 ft_get_fabric_sense_len(void)
+{
+ return 0;
+}
+
+static u16 ft_set_fabric_sense_len(struct se_cmd *se_cmd, u32 sense_len)
+{
+ return 0;
+}
+
+static u32 ft_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+ struct ft_tpg *tpg = se_tpg->se_tpg_fabric_ptr;
+
+ return tpg->index;
+}
+
+static u64 ft_pack_lun(unsigned int index)
+{
+ WARN_ON(index >= 256);
+ /* Caller wants this byte-swapped */
+ return cpu_to_le64((index & 0xff) << 8);
+}
+
+static struct target_core_fabric_ops ft_fabric_ops = {
+ .get_fabric_name = ft_get_fabric_name,
+ .get_fabric_proto_ident = fc_get_fabric_proto_ident,
+ .tpg_get_wwn = ft_get_fabric_wwn,
+ .tpg_get_tag = ft_get_tag,
+ .tpg_get_default_depth = ft_get_default_depth,
+ .tpg_get_pr_transport_id = fc_get_pr_transport_id,
+ .tpg_get_pr_transport_id_len = fc_get_pr_transport_id_len,
+ .tpg_parse_pr_out_transport_id = fc_parse_pr_out_transport_id,
+ .tpg_check_demo_mode = ft_check_false,
+ .tpg_check_demo_mode_cache = ft_check_false,
+ .tpg_check_demo_mode_write_protect = ft_check_false,
+ .tpg_check_prod_mode_write_protect = ft_check_false,
+ .tpg_alloc_fabric_acl = ft_tpg_alloc_fabric_acl,
+ .tpg_release_fabric_acl = ft_tpg_release_fabric_acl,
+ .tpg_get_inst_index = ft_tpg_get_inst_index,
+ .check_stop_free = ft_check_stop_free,
+ .release_cmd_to_pool = ft_release_cmd,
+ .release_cmd_direct = ft_release_cmd,
+ .shutdown_session = ft_sess_shutdown,
+ .close_session = ft_sess_close,
+ .stop_session = ft_sess_stop,
+ .fall_back_to_erl0 = ft_sess_set_erl0,
+ .sess_logged_in = ft_sess_logged_in,
+ .sess_get_index = ft_sess_get_index,
+ .sess_get_initiator_sid = NULL,
+ .write_pending = ft_write_pending,
+ .write_pending_status = ft_write_pending_status,
+ .set_default_node_attributes = ft_set_default_node_attr,
+ .get_task_tag = ft_get_task_tag,
+ .get_cmd_state = ft_get_cmd_state,
+ .new_cmd_failure = ft_new_cmd_failure,
+ .queue_data_in = ft_queue_data_in,
+ .queue_status = ft_queue_status,
+ .queue_tm_rsp = ft_queue_tm_resp,
+ .get_fabric_sense_len = ft_get_fabric_sense_len,
+ .set_fabric_sense_len = ft_set_fabric_sense_len,
+ .is_state_remove = ft_is_state_remove,
+ .pack_lun = ft_pack_lun,
+ /*
+ * Setup function pointers for generic logic in
+ * target_core_fabric_configfs.c
+ */
+ .fabric_make_wwn = &ft_add_lport,
+ .fabric_drop_wwn = &ft_del_lport,
+ .fabric_make_tpg = &ft_add_tpg,
+ .fabric_drop_tpg = &ft_del_tpg,
+ .fabric_post_link = NULL,
+ .fabric_pre_unlink = NULL,
+ .fabric_make_np = NULL,
+ .fabric_drop_np = NULL,
+ .fabric_make_nodeacl = &ft_add_acl,
+ .fabric_drop_nodeacl = &ft_del_acl,
+};
+
+int ft_register_configfs(void)
+{
+ struct target_fabric_configfs *fabric;
+ int ret;
+
+ /*
+ * Register the top level struct config_item_type with TCM core
+ */
+ fabric = target_fabric_configfs_init(THIS_MODULE, "fc");
+ if (!fabric) {
+ printk(KERN_INFO "%s: target_fabric_configfs_init() failed!\n",
+ __func__);
+ return -1;
+ }
+ fabric->tf_ops = ft_fabric_ops;
+
+ /* Allowing support for task_sg_chaining */
+ fabric->tf_ops.task_sg_chaining = 1;
+
+ /*
+ * Setup default attribute lists for various fabric->tf_cit_tmpl
+ */
+ TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = ft_wwn_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs =
+ ft_nacl_base_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+ /*
+ * register the fabric for use within TCM
+ */
+ ret = target_fabric_configfs_register(fabric);
+ if (ret < 0) {
+ FT_CONF_DBG("target_fabric_configfs_register() for"
+ " FC Target failed!\n");
+ printk(KERN_INFO
+ "%s: target_fabric_configfs_register() failed!\n",
+ __func__);
+ target_fabric_configfs_free(fabric);
+ return -1;
+ }
+
+ /*
+ * Setup our local pointer to *fabric.
+ */
+ ft_configfs = fabric;
+ return 0;
+}
+
+void ft_deregister_configfs(void)
+{
+ if (!ft_configfs)
+ return;
+ target_fabric_configfs_deregister(ft_configfs);
+ ft_configfs = NULL;
+}
+
+static struct notifier_block ft_notifier = {
+ .notifier_call = ft_lport_notify
+};
+
+static int __init ft_init(void)
+{
+ if (ft_register_configfs())
+ return -1;
+ if (fc_fc4_register_provider(FC_TYPE_FCP, &ft_prov)) {
+ ft_deregister_configfs();
+ return -1;
+ }
+ blocking_notifier_chain_register(&fc_lport_notifier_head, &ft_notifier);
+ fc_lport_iterate(ft_lport_add, NULL);
+ return 0;
+}
+
+static void __exit ft_exit(void)
+{
+ blocking_notifier_chain_unregister(&fc_lport_notifier_head,
+ &ft_notifier);
+ fc_fc4_deregister_provider(FC_TYPE_FCP, &ft_prov);
+ fc_lport_iterate(ft_lport_del, NULL);
+ ft_deregister_configfs();
+ synchronize_rcu();
+}
+
+#ifdef MODULE
+MODULE_DESCRIPTION("FC TCM fabric driver " FT_VERSION);
+MODULE_LICENSE("GPL");
+module_init(ft_init);
+module_exit(ft_exit);
+#endif /* MODULE */
diff --git a/drivers/target/tcm_fc/tfc_io.c b/drivers/target/tcm_fc/tfc_io.c
new file mode 100644
index 00000000000..4c3c0efbe13
--- /dev/null
+++ b/drivers/target/tcm_fc/tfc_io.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2010 Cisco Systems, Inc.
+ *
+ * Portions based on tcm_loop_fabric_scsi.c and libfc/fc_fcp.c
+ *
+ * Copyright (c) 2007 Intel Corporation. All rights reserved.
+ * Copyright (c) 2008 Red Hat, Inc. All rights reserved.
+ * Copyright (c) 2008 Mike Christie
+ * Copyright (c) 2009 Rising Tide, Inc.
+ * Copyright (c) 2009 Linux-iSCSI.org
+ * Copyright (c) 2009 Nicholas A. Bellinger <nab@linux-iscsi.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* XXX TBD some includes may be extraneous */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <generated/utsrelease.h>
+#include <linux/utsname.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/hash.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_transport.h>
+#include <target/target_core_fabric_ops.h>
+#include <target/target_core_device.h>
+#include <target/target_core_tpg.h>
+#include <target/target_core_configfs.h>
+#include <target/target_core_base.h>
+#include <target/configfs_macros.h>
+
+#include "tcm_fc.h"
+
+/*
+ * Deliver read data back to initiator.
+ * XXX TBD handle resource problems later.
+ */
+int ft_queue_data_in(struct se_cmd *se_cmd)
+{
+ struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
+ struct se_transport_task *task;
+ struct fc_frame *fp = NULL;
+ struct fc_exch *ep;
+ struct fc_lport *lport;
+ struct se_mem *mem;
+ size_t remaining;
+ u32 f_ctl = FC_FC_EX_CTX | FC_FC_REL_OFF;
+ u32 mem_off;
+ u32 fh_off = 0;
+ u32 frame_off = 0;
+ size_t frame_len = 0;
+ size_t mem_len;
+ size_t tlen;
+ size_t off_in_page;
+ struct page *page;
+ int use_sg;
+ int error;
+ void *page_addr;
+ void *from;
+ void *to = NULL;
+
+ ep = fc_seq_exch(cmd->seq);
+ lport = ep->lp;
+ cmd->seq = lport->tt.seq_start_next(cmd->seq);
+
+ task = T_TASK(se_cmd);
+ BUG_ON(!task);
+ remaining = se_cmd->data_length;
+
+ /*
+ * Setup to use first mem list entry if any.
+ */
+ if (task->t_tasks_se_num) {
+ mem = list_first_entry(task->t_mem_list,
+ struct se_mem, se_list);
+ mem_len = mem->se_len;
+ mem_off = mem->se_off;
+ page = mem->se_page;
+ } else {
+ mem = NULL;
+ mem_len = remaining;
+ mem_off = 0;
+ page = NULL;
+ }
+
+ /* no scatter/gather in skb for odd word length due to fc_seq_send() */
+ use_sg = !(remaining % 4);
+
+ while (remaining) {
+ if (!mem_len) {
+ BUG_ON(!mem);
+ mem = list_entry(mem->se_list.next,
+ struct se_mem, se_list);
+ mem_len = min((size_t)mem->se_len, remaining);
+ mem_off = mem->se_off;
+ page = mem->se_page;
+ }
+ if (!frame_len) {
+ /*
+ * If lport's has capability of Large Send Offload LSO)
+ * , then allow 'frame_len' to be as big as 'lso_max'
+ * if indicated transfer length is >= lport->lso_max
+ */
+ frame_len = (lport->seq_offload) ? lport->lso_max :
+ cmd->sess->max_frame;
+ frame_len = min(frame_len, remaining);
+ fp = fc_frame_alloc(lport, use_sg ? 0 : frame_len);
+ if (!fp)
+ return -ENOMEM;
+ to = fc_frame_payload_get(fp, 0);
+ fh_off = frame_off;
+ frame_off += frame_len;
+ /*
+ * Setup the frame's max payload which is used by base
+ * driver to indicate HW about max frame size, so that
+ * HW can do fragmentation appropriately based on
+ * "gso_max_size" of underline netdev.
+ */
+ fr_max_payload(fp) = cmd->sess->max_frame;
+ }
+ tlen = min(mem_len, frame_len);
+
+ if (use_sg) {
+ if (!mem) {
+ BUG_ON(!task->t_task_buf);
+ page_addr = task->t_task_buf + mem_off;
+ /*
+ * In this case, offset is 'offset_in_page' of
+ * (t_task_buf + mem_off) instead of 'mem_off'.
+ */
+ off_in_page = offset_in_page(page_addr);
+ page = virt_to_page(page_addr);
+ tlen = min(tlen, PAGE_SIZE - off_in_page);
+ } else
+ off_in_page = mem_off;
+ BUG_ON(!page);
+ get_page(page);
+ skb_fill_page_desc(fp_skb(fp),
+ skb_shinfo(fp_skb(fp))->nr_frags,
+ page, off_in_page, tlen);
+ fr_len(fp) += tlen;
+ fp_skb(fp)->data_len += tlen;
+ fp_skb(fp)->truesize +=
+ PAGE_SIZE << compound_order(page);
+ } else if (mem) {
+ BUG_ON(!page);
+ from = kmap_atomic(page + (mem_off >> PAGE_SHIFT),
+ KM_SOFTIRQ0);
+ page_addr = from;
+ from += mem_off & ~PAGE_MASK;
+ tlen = min(tlen, (size_t)(PAGE_SIZE -
+ (mem_off & ~PAGE_MASK)));
+ memcpy(to, from, tlen);
+ kunmap_atomic(page_addr, KM_SOFTIRQ0);
+ to += tlen;
+ } else {
+ from = task->t_task_buf + mem_off;
+ memcpy(to, from, tlen);
+ to += tlen;
+ }
+
+ mem_off += tlen;
+ mem_len -= tlen;
+ frame_len -= tlen;
+ remaining -= tlen;
+
+ if (frame_len &&
+ (skb_shinfo(fp_skb(fp))->nr_frags < FC_FRAME_SG_LEN))
+ continue;
+ if (!remaining)
+ f_ctl |= FC_FC_END_SEQ;
+ fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid,
+ FC_TYPE_FCP, f_ctl, fh_off);
+ error = lport->tt.seq_send(lport, cmd->seq, fp);
+ if (error) {
+ /* XXX For now, initiator will retry */
+ if (printk_ratelimit())
+ printk(KERN_ERR "%s: Failed to send frame %p, "
+ "xid <0x%x>, remaining <0x%x>, "
+ "lso_max <0x%x>\n",
+ __func__, fp, ep->xid,
+ remaining, lport->lso_max);
+ }
+ }
+ return ft_queue_status(se_cmd);
+}
+
+/*
+ * Receive write data frame.
+ */
+void ft_recv_write_data(struct ft_cmd *cmd, struct fc_frame *fp)
+{
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct fc_seq *seq = cmd->seq;
+ struct fc_exch *ep;
+ struct fc_lport *lport;
+ struct se_transport_task *task;
+ struct fc_frame_header *fh;
+ struct se_mem *mem;
+ u32 mem_off;
+ u32 rel_off;
+ size_t frame_len;
+ size_t mem_len;
+ size_t tlen;
+ struct page *page;
+ void *page_addr;
+ void *from;
+ void *to;
+ u32 f_ctl;
+ void *buf;
+
+ task = T_TASK(se_cmd);
+ BUG_ON(!task);
+
+ fh = fc_frame_header_get(fp);
+ if (!(ntoh24(fh->fh_f_ctl) & FC_FC_REL_OFF))
+ goto drop;
+
+ /*
+ * Doesn't expect even single byte of payload. Payload
+ * is expected to be copied directly to user buffers
+ * due to DDP (Large Rx offload) feature, hence
+ * BUG_ON if BUF is non-NULL
+ */
+ buf = fc_frame_payload_get(fp, 1);
+ if (cmd->was_ddp_setup && buf) {
+ printk(KERN_INFO "%s: When DDP was setup, not expected to"
+ "receive frame with payload, Payload shall be"
+ "copied directly to buffer instead of coming "
+ "via. legacy receive queues\n", __func__);
+ BUG_ON(buf);
+ }
+
+ /*
+ * If ft_cmd indicated 'ddp_setup', in that case only the last frame
+ * should come with 'TSI bit being set'. If 'TSI bit is not set and if
+ * data frame appears here, means error condition. In both the cases
+ * release the DDP context (ddp_put) and in error case, as well
+ * initiate error recovery mechanism.
+ */
+ ep = fc_seq_exch(seq);
+ if (cmd->was_ddp_setup) {
+ BUG_ON(!ep);
+ lport = ep->lp;
+ BUG_ON(!lport);
+ }
+ if (cmd->was_ddp_setup && ep->xid != FC_XID_UNKNOWN) {
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ /*
+ * If TSI bit set in f_ctl, means last write data frame is
+ * received successfully where payload is posted directly
+ * to user buffer and only the last frame's header is posted
+ * in legacy receive queue
+ */
+ if (f_ctl & FC_FC_SEQ_INIT) { /* TSI bit set in FC frame */
+ cmd->write_data_len = lport->tt.ddp_done(lport,
+ ep->xid);
+ goto last_frame;
+ } else {
+ /*
+ * Updating the write_data_len may be meaningless at
+ * this point, but just in case if required in future
+ * for debugging or any other purpose
+ */
+ printk(KERN_ERR "%s: Received frame with TSI bit not"
+ " being SET, dropping the frame, "
+ "cmd->sg <%p>, cmd->sg_cnt <0x%x>\n",
+ __func__, cmd->sg, cmd->sg_cnt);
+ cmd->write_data_len = lport->tt.ddp_done(lport,
+ ep->xid);
+ lport->tt.seq_exch_abort(cmd->seq, 0);
+ goto drop;
+ }
+ }
+
+ rel_off = ntohl(fh->fh_parm_offset);
+ frame_len = fr_len(fp);
+ if (frame_len <= sizeof(*fh))
+ goto drop;
+ frame_len -= sizeof(*fh);
+ from = fc_frame_payload_get(fp, 0);
+ if (rel_off >= se_cmd->data_length)
+ goto drop;
+ if (frame_len + rel_off > se_cmd->data_length)
+ frame_len = se_cmd->data_length - rel_off;
+
+ /*
+ * Setup to use first mem list entry if any.
+ */
+ if (task->t_tasks_se_num) {
+ mem = list_first_entry(task->t_mem_list,
+ struct se_mem, se_list);
+ mem_len = mem->se_len;
+ mem_off = mem->se_off;
+ page = mem->se_page;
+ } else {
+ mem = NULL;
+ page = NULL;
+ mem_off = 0;
+ mem_len = frame_len;
+ }
+
+ while (frame_len) {
+ if (!mem_len) {
+ BUG_ON(!mem);
+ mem = list_entry(mem->se_list.next,
+ struct se_mem, se_list);
+ mem_len = mem->se_len;
+ mem_off = mem->se_off;
+ page = mem->se_page;
+ }
+ if (rel_off >= mem_len) {
+ rel_off -= mem_len;
+ mem_len = 0;
+ continue;
+ }
+ mem_off += rel_off;
+ mem_len -= rel_off;
+ rel_off = 0;
+
+ tlen = min(mem_len, frame_len);
+
+ if (mem) {
+ to = kmap_atomic(page + (mem_off >> PAGE_SHIFT),
+ KM_SOFTIRQ0);
+ page_addr = to;
+ to += mem_off & ~PAGE_MASK;
+ tlen = min(tlen, (size_t)(PAGE_SIZE -
+ (mem_off & ~PAGE_MASK)));
+ memcpy(to, from, tlen);
+ kunmap_atomic(page_addr, KM_SOFTIRQ0);
+ } else {
+ to = task->t_task_buf + mem_off;
+ memcpy(to, from, tlen);
+ }
+ from += tlen;
+ frame_len -= tlen;
+ mem_off += tlen;
+ mem_len -= tlen;
+ cmd->write_data_len += tlen;
+ }
+last_frame:
+ if (cmd->write_data_len == se_cmd->data_length)
+ transport_generic_handle_data(se_cmd);
+drop:
+ fc_frame_free(fp);
+}
diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c
new file mode 100644
index 00000000000..a3bd57f2ea3
--- /dev/null
+++ b/drivers/target/tcm_fc/tfc_sess.c
@@ -0,0 +1,541 @@
+/*
+ * Copyright (c) 2010 Cisco Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* XXX TBD some includes may be extraneous */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <generated/utsrelease.h>
+#include <linux/utsname.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/hash.h>
+#include <linux/rcupdate.h>
+#include <linux/rculist.h>
+#include <linux/kref.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/libfc.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_transport.h>
+#include <target/target_core_fabric_ops.h>
+#include <target/target_core_device.h>
+#include <target/target_core_tpg.h>
+#include <target/target_core_configfs.h>
+#include <target/target_core_base.h>
+#include <target/configfs_macros.h>
+
+#include <scsi/libfc.h>
+#include "tcm_fc.h"
+
+static void ft_sess_delete_all(struct ft_tport *);
+
+/*
+ * Lookup or allocate target local port.
+ * Caller holds ft_lport_lock.
+ */
+static struct ft_tport *ft_tport_create(struct fc_lport *lport)
+{
+ struct ft_tpg *tpg;
+ struct ft_tport *tport;
+ int i;
+
+ tport = rcu_dereference(lport->prov[FC_TYPE_FCP]);
+ if (tport && tport->tpg)
+ return tport;
+
+ tpg = ft_lport_find_tpg(lport);
+ if (!tpg)
+ return NULL;
+
+ if (tport) {
+ tport->tpg = tpg;
+ return tport;
+ }
+
+ tport = kzalloc(sizeof(*tport), GFP_KERNEL);
+ if (!tport)
+ return NULL;
+
+ tport->lport = lport;
+ tport->tpg = tpg;
+ tpg->tport = tport;
+ for (i = 0; i < FT_SESS_HASH_SIZE; i++)
+ INIT_HLIST_HEAD(&tport->hash[i]);
+
+ rcu_assign_pointer(lport->prov[FC_TYPE_FCP], tport);
+ return tport;
+}
+
+/*
+ * Free tport via RCU.
+ */
+static void ft_tport_rcu_free(struct rcu_head *rcu)
+{
+ struct ft_tport *tport = container_of(rcu, struct ft_tport, rcu);
+
+ kfree(tport);
+}
+
+/*
+ * Delete a target local port.
+ * Caller holds ft_lport_lock.
+ */
+static void ft_tport_delete(struct ft_tport *tport)
+{
+ struct fc_lport *lport;
+ struct ft_tpg *tpg;
+
+ ft_sess_delete_all(tport);
+ lport = tport->lport;
+ BUG_ON(tport != lport->prov[FC_TYPE_FCP]);
+ rcu_assign_pointer(lport->prov[FC_TYPE_FCP], NULL);
+
+ tpg = tport->tpg;
+ if (tpg) {
+ tpg->tport = NULL;
+ tport->tpg = NULL;
+ }
+ call_rcu(&tport->rcu, ft_tport_rcu_free);
+}
+
+/*
+ * Add local port.
+ * Called thru fc_lport_iterate().
+ */
+void ft_lport_add(struct fc_lport *lport, void *arg)
+{
+ mutex_lock(&ft_lport_lock);
+ ft_tport_create(lport);
+ mutex_unlock(&ft_lport_lock);
+}
+
+/*
+ * Delete local port.
+ * Called thru fc_lport_iterate().
+ */
+void ft_lport_del(struct fc_lport *lport, void *arg)
+{
+ struct ft_tport *tport;
+
+ mutex_lock(&ft_lport_lock);
+ tport = lport->prov[FC_TYPE_FCP];
+ if (tport)
+ ft_tport_delete(tport);
+ mutex_unlock(&ft_lport_lock);
+}
+
+/*
+ * Notification of local port change from libfc.
+ * Create or delete local port and associated tport.
+ */
+int ft_lport_notify(struct notifier_block *nb, unsigned long event, void *arg)
+{
+ struct fc_lport *lport = arg;
+
+ switch (event) {
+ case FC_LPORT_EV_ADD:
+ ft_lport_add(lport, NULL);
+ break;
+ case FC_LPORT_EV_DEL:
+ ft_lport_del(lport, NULL);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+/*
+ * Hash function for FC_IDs.
+ */
+static u32 ft_sess_hash(u32 port_id)
+{
+ return hash_32(port_id, FT_SESS_HASH_BITS);
+}
+
+/*
+ * Find session in local port.
+ * Sessions and hash lists are RCU-protected.
+ * A reference is taken which must be eventually freed.
+ */
+static struct ft_sess *ft_sess_get(struct fc_lport *lport, u32 port_id)
+{
+ struct ft_tport *tport;
+ struct hlist_head *head;
+ struct hlist_node *pos;
+ struct ft_sess *sess;
+
+ rcu_read_lock();
+ tport = rcu_dereference(lport->prov[FC_TYPE_FCP]);
+ if (!tport)
+ goto out;
+
+ head = &tport->hash[ft_sess_hash(port_id)];
+ hlist_for_each_entry_rcu(sess, pos, head, hash) {
+ if (sess->port_id == port_id) {
+ kref_get(&sess->kref);
+ rcu_read_unlock();
+ FT_SESS_DBG("port_id %x found %p\n", port_id, sess);
+ return sess;
+ }
+ }
+out:
+ rcu_read_unlock();
+ FT_SESS_DBG("port_id %x not found\n", port_id);
+ return NULL;
+}
+
+/*
+ * Allocate session and enter it in the hash for the local port.
+ * Caller holds ft_lport_lock.
+ */
+static struct ft_sess *ft_sess_create(struct ft_tport *tport, u32 port_id,
+ struct ft_node_acl *acl)
+{
+ struct ft_sess *sess;
+ struct hlist_head *head;
+ struct hlist_node *pos;
+
+ head = &tport->hash[ft_sess_hash(port_id)];
+ hlist_for_each_entry_rcu(sess, pos, head, hash)
+ if (sess->port_id == port_id)
+ return sess;
+
+ sess = kzalloc(sizeof(*sess), GFP_KERNEL);
+ if (!sess)
+ return NULL;
+
+ sess->se_sess = transport_init_session();
+ if (!sess->se_sess) {
+ kfree(sess);
+ return NULL;
+ }
+ sess->se_sess->se_node_acl = &acl->se_node_acl;
+ sess->tport = tport;
+ sess->port_id = port_id;
+ kref_init(&sess->kref); /* ref for table entry */
+ hlist_add_head_rcu(&sess->hash, head);
+ tport->sess_count++;
+
+ FT_SESS_DBG("port_id %x sess %p\n", port_id, sess);
+
+ transport_register_session(&tport->tpg->se_tpg, &acl->se_node_acl,
+ sess->se_sess, sess);
+ return sess;
+}
+
+/*
+ * Unhash the session.
+ * Caller holds ft_lport_lock.
+ */
+static void ft_sess_unhash(struct ft_sess *sess)
+{
+ struct ft_tport *tport = sess->tport;
+
+ hlist_del_rcu(&sess->hash);
+ BUG_ON(!tport->sess_count);
+ tport->sess_count--;
+ sess->port_id = -1;
+ sess->params = 0;
+}
+
+/*
+ * Delete session from hash.
+ * Caller holds ft_lport_lock.
+ */
+static struct ft_sess *ft_sess_delete(struct ft_tport *tport, u32 port_id)
+{
+ struct hlist_head *head;
+ struct hlist_node *pos;
+ struct ft_sess *sess;
+
+ head = &tport->hash[ft_sess_hash(port_id)];
+ hlist_for_each_entry_rcu(sess, pos, head, hash) {
+ if (sess->port_id == port_id) {
+ ft_sess_unhash(sess);
+ return sess;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Delete all sessions from tport.
+ * Caller holds ft_lport_lock.
+ */
+static void ft_sess_delete_all(struct ft_tport *tport)
+{
+ struct hlist_head *head;
+ struct hlist_node *pos;
+ struct ft_sess *sess;
+
+ for (head = tport->hash;
+ head < &tport->hash[FT_SESS_HASH_SIZE]; head++) {
+ hlist_for_each_entry_rcu(sess, pos, head, hash) {
+ ft_sess_unhash(sess);
+ transport_deregister_session_configfs(sess->se_sess);
+ ft_sess_put(sess); /* release from table */
+ }
+ }
+}
+
+/*
+ * TCM ops for sessions.
+ */
+
+/*
+ * Determine whether session is allowed to be shutdown in the current context.
+ * Returns non-zero if the session should be shutdown.
+ */
+int ft_sess_shutdown(struct se_session *se_sess)
+{
+ struct ft_sess *sess = se_sess->fabric_sess_ptr;
+
+ FT_SESS_DBG("port_id %x\n", sess->port_id);
+ return 1;
+}
+
+/*
+ * Remove session and send PRLO.
+ * This is called when the ACL is being deleted or queue depth is changing.
+ */
+void ft_sess_close(struct se_session *se_sess)
+{
+ struct ft_sess *sess = se_sess->fabric_sess_ptr;
+ struct fc_lport *lport;
+ u32 port_id;
+
+ mutex_lock(&ft_lport_lock);
+ lport = sess->tport->lport;
+ port_id = sess->port_id;
+ if (port_id == -1) {
+ mutex_lock(&ft_lport_lock);
+ return;
+ }
+ FT_SESS_DBG("port_id %x\n", port_id);
+ ft_sess_unhash(sess);
+ mutex_unlock(&ft_lport_lock);
+ transport_deregister_session_configfs(se_sess);
+ ft_sess_put(sess);
+ /* XXX Send LOGO or PRLO */
+ synchronize_rcu(); /* let transport deregister happen */
+}
+
+void ft_sess_stop(struct se_session *se_sess, int sess_sleep, int conn_sleep)
+{
+ struct ft_sess *sess = se_sess->fabric_sess_ptr;
+
+ FT_SESS_DBG("port_id %x\n", sess->port_id);
+}
+
+int ft_sess_logged_in(struct se_session *se_sess)
+{
+ struct ft_sess *sess = se_sess->fabric_sess_ptr;
+
+ return sess->port_id != -1;
+}
+
+u32 ft_sess_get_index(struct se_session *se_sess)
+{
+ struct ft_sess *sess = se_sess->fabric_sess_ptr;
+
+ return sess->port_id; /* XXX TBD probably not what is needed */
+}
+
+u32 ft_sess_get_port_name(struct se_session *se_sess,
+ unsigned char *buf, u32 len)
+{
+ struct ft_sess *sess = se_sess->fabric_sess_ptr;
+
+ return ft_format_wwn(buf, len, sess->port_name);
+}
+
+void ft_sess_set_erl0(struct se_session *se_sess)
+{
+ /* XXX TBD called when out of memory */
+}
+
+/*
+ * libfc ops involving sessions.
+ */
+
+static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len,
+ const struct fc_els_spp *rspp, struct fc_els_spp *spp)
+{
+ struct ft_tport *tport;
+ struct ft_sess *sess;
+ struct ft_node_acl *acl;
+ u32 fcp_parm;
+
+ tport = ft_tport_create(rdata->local_port);
+ if (!tport)
+ return 0; /* not a target for this local port */
+
+ acl = ft_acl_get(tport->tpg, rdata);
+ if (!acl)
+ return 0;
+
+ if (!rspp)
+ goto fill;
+
+ if (rspp->spp_flags & (FC_SPP_OPA_VAL | FC_SPP_RPA_VAL))
+ return FC_SPP_RESP_NO_PA;
+
+ /*
+ * If both target and initiator bits are off, the SPP is invalid.
+ */
+ fcp_parm = ntohl(rspp->spp_params);
+ if (!(fcp_parm & (FCP_SPPF_INIT_FCN | FCP_SPPF_TARG_FCN)))
+ return FC_SPP_RESP_INVL;
+
+ /*
+ * Create session (image pair) only if requested by
+ * EST_IMG_PAIR flag and if the requestor is an initiator.
+ */
+ if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR) {
+ spp->spp_flags |= FC_SPP_EST_IMG_PAIR;
+ if (!(fcp_parm & FCP_SPPF_INIT_FCN))
+ return FC_SPP_RESP_CONF;
+ sess = ft_sess_create(tport, rdata->ids.port_id, acl);
+ if (!sess)
+ return FC_SPP_RESP_RES;
+ if (!sess->params)
+ rdata->prli_count++;
+ sess->params = fcp_parm;
+ sess->port_name = rdata->ids.port_name;
+ sess->max_frame = rdata->maxframe_size;
+
+ /* XXX TBD - clearing actions. unit attn, see 4.10 */
+ }
+
+ /*
+ * OR in our service parameters with other provider (initiator), if any.
+ * TBD XXX - indicate RETRY capability?
+ */
+fill:
+ fcp_parm = ntohl(spp->spp_params);
+ spp->spp_params = htonl(fcp_parm | FCP_SPPF_TARG_FCN);
+ return FC_SPP_RESP_ACK;
+}
+
+/**
+ * tcm_fcp_prli() - Handle incoming or outgoing PRLI for the FCP target
+ * @rdata: remote port private
+ * @spp_len: service parameter page length
+ * @rspp: received service parameter page (NULL for outgoing PRLI)
+ * @spp: response service parameter page
+ *
+ * Returns spp response code.
+ */
+static int ft_prli(struct fc_rport_priv *rdata, u32 spp_len,
+ const struct fc_els_spp *rspp, struct fc_els_spp *spp)
+{
+ int ret;
+
+ mutex_lock(&ft_lport_lock);
+ ret = ft_prli_locked(rdata, spp_len, rspp, spp);
+ mutex_unlock(&ft_lport_lock);
+ FT_SESS_DBG("port_id %x flags %x ret %x\n",
+ rdata->ids.port_id, rspp ? rspp->spp_flags : 0, ret);
+ return ret;
+}
+
+static void ft_sess_rcu_free(struct rcu_head *rcu)
+{
+ struct ft_sess *sess = container_of(rcu, struct ft_sess, rcu);
+
+ transport_deregister_session(sess->se_sess);
+ kfree(sess);
+}
+
+static void ft_sess_free(struct kref *kref)
+{
+ struct ft_sess *sess = container_of(kref, struct ft_sess, kref);
+
+ call_rcu(&sess->rcu, ft_sess_rcu_free);
+}
+
+void ft_sess_put(struct ft_sess *sess)
+{
+ int sess_held = atomic_read(&sess->kref.refcount);
+
+ BUG_ON(!sess_held);
+ kref_put(&sess->kref, ft_sess_free);
+}
+
+static void ft_prlo(struct fc_rport_priv *rdata)
+{
+ struct ft_sess *sess;
+ struct ft_tport *tport;
+
+ mutex_lock(&ft_lport_lock);
+ tport = rcu_dereference(rdata->local_port->prov[FC_TYPE_FCP]);
+ if (!tport) {
+ mutex_unlock(&ft_lport_lock);
+ return;
+ }
+ sess = ft_sess_delete(tport, rdata->ids.port_id);
+ if (!sess) {
+ mutex_unlock(&ft_lport_lock);
+ return;
+ }
+ mutex_unlock(&ft_lport_lock);
+ transport_deregister_session_configfs(sess->se_sess);
+ ft_sess_put(sess); /* release from table */
+ rdata->prli_count--;
+ /* XXX TBD - clearing actions. unit attn, see 4.10 */
+}
+
+/*
+ * Handle incoming FCP request.
+ * Caller has verified that the frame is type FCP.
+ */
+static void ft_recv(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct ft_sess *sess;
+ u32 sid = fc_frame_sid(fp);
+
+ FT_SESS_DBG("sid %x\n", sid);
+
+ sess = ft_sess_get(lport, sid);
+ if (!sess) {
+ FT_SESS_DBG("sid %x sess lookup failed\n", sid);
+ /* TBD XXX - if FCP_CMND, send PRLO */
+ fc_frame_free(fp);
+ return;
+ }
+ ft_recv_req(sess, fp); /* must do ft_sess_put() */
+}
+
+/*
+ * Provider ops for libfc.
+ */
+struct fc4_prov ft_prov = {
+ .prli = ft_prli,
+ .prlo = ft_prlo,
+ .recv = ft_recv,
+ .module = THIS_MODULE,
+};
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index 47f8cdb207f..74273e638c0 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -1658,8 +1658,12 @@ static void gsm_queue(struct gsm_mux *gsm)
if ((gsm->control & ~PF) == UI)
gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len);
- /* generate final CRC with received FCS */
- gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs);
+ if (gsm->encoding == 0){
+ /* WARNING: gsm->received_fcs is used for gsm->encoding = 0 only.
+ In this case it contain the last piece of data
+ required to generate final CRC */
+ gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs);
+ }
if (gsm->fcs != GOOD_FCS) {
gsm->bad_fcs++;
if (debug & 4)
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 80484af781e..b1f0f83b870 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1391,6 +1391,14 @@ config SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE
help
Support for Console on the NWP serial ports.
+config SERIAL_LANTIQ
+ bool "Lantiq serial driver"
+ depends on LANTIQ
+ select SERIAL_CORE
+ select SERIAL_CORE_CONSOLE
+ help
+ Support for console and UART on Lantiq SoCs.
+
config SERIAL_QE
tristate "Freescale QUICC Engine serial port support"
depends on QUICC_ENGINE
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index fee0690ef8e..35276043d9d 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -94,3 +94,4 @@ obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o
obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o
obj-$(CONFIG_SERIAL_MSM_SMD) += msm_smd_tty.o
obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o
+obj-$(CONFIG_SERIAL_LANTIQ) += lantiq.o
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index cb36b0d4ef3..62df72d9f0a 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -382,12 +382,13 @@ static void imx_start_tx(struct uart_port *port)
static irqreturn_t imx_rtsint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
- unsigned int val = readl(sport->port.membase + USR1) & USR1_RTSS;
+ unsigned int val;
unsigned long flags;
spin_lock_irqsave(&sport->port.lock, flags);
writel(USR1_RTSD, sport->port.membase + USR1);
+ val = readl(sport->port.membase + USR1) & USR1_RTSS;
uart_handle_cts_change(&sport->port, !!val);
wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c
new file mode 100644
index 00000000000..58cf279ed87
--- /dev/null
+++ b/drivers/tty/serial/lantiq.c
@@ -0,0 +1,756 @@
+/*
+ * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright (C) 2004 Infineon IFAP DC COM CPE
+ * Copyright (C) 2007 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2007 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2010 Thomas Langer, <thomas.langer@lantiq.com>
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#include <lantiq_soc.h>
+
+#define PORT_LTQ_ASC 111
+#define MAXPORTS 2
+#define UART_DUMMY_UER_RX 1
+#define DRVNAME "ltq_asc"
+#ifdef __BIG_ENDIAN
+#define LTQ_ASC_TBUF (0x0020 + 3)
+#define LTQ_ASC_RBUF (0x0024 + 3)
+#else
+#define LTQ_ASC_TBUF 0x0020
+#define LTQ_ASC_RBUF 0x0024
+#endif
+#define LTQ_ASC_FSTAT 0x0048
+#define LTQ_ASC_WHBSTATE 0x0018
+#define LTQ_ASC_STATE 0x0014
+#define LTQ_ASC_IRNCR 0x00F8
+#define LTQ_ASC_CLC 0x0000
+#define LTQ_ASC_ID 0x0008
+#define LTQ_ASC_PISEL 0x0004
+#define LTQ_ASC_TXFCON 0x0044
+#define LTQ_ASC_RXFCON 0x0040
+#define LTQ_ASC_CON 0x0010
+#define LTQ_ASC_BG 0x0050
+#define LTQ_ASC_IRNREN 0x00F4
+
+#define ASC_IRNREN_TX 0x1
+#define ASC_IRNREN_RX 0x2
+#define ASC_IRNREN_ERR 0x4
+#define ASC_IRNREN_TX_BUF 0x8
+#define ASC_IRNCR_TIR 0x1
+#define ASC_IRNCR_RIR 0x2
+#define ASC_IRNCR_EIR 0x4
+
+#define ASCOPT_CSIZE 0x3
+#define TXFIFO_FL 1
+#define RXFIFO_FL 1
+#define ASCCLC_DISS 0x2
+#define ASCCLC_RMCMASK 0x0000FF00
+#define ASCCLC_RMCOFFSET 8
+#define ASCCON_M_8ASYNC 0x0
+#define ASCCON_M_7ASYNC 0x2
+#define ASCCON_ODD 0x00000020
+#define ASCCON_STP 0x00000080
+#define ASCCON_BRS 0x00000100
+#define ASCCON_FDE 0x00000200
+#define ASCCON_R 0x00008000
+#define ASCCON_FEN 0x00020000
+#define ASCCON_ROEN 0x00080000
+#define ASCCON_TOEN 0x00100000
+#define ASCSTATE_PE 0x00010000
+#define ASCSTATE_FE 0x00020000
+#define ASCSTATE_ROE 0x00080000
+#define ASCSTATE_ANY (ASCSTATE_ROE|ASCSTATE_PE|ASCSTATE_FE)
+#define ASCWHBSTATE_CLRREN 0x00000001
+#define ASCWHBSTATE_SETREN 0x00000002
+#define ASCWHBSTATE_CLRPE 0x00000004
+#define ASCWHBSTATE_CLRFE 0x00000008
+#define ASCWHBSTATE_CLRROE 0x00000020
+#define ASCTXFCON_TXFEN 0x0001
+#define ASCTXFCON_TXFFLU 0x0002
+#define ASCTXFCON_TXFITLMASK 0x3F00
+#define ASCTXFCON_TXFITLOFF 8
+#define ASCRXFCON_RXFEN 0x0001
+#define ASCRXFCON_RXFFLU 0x0002
+#define ASCRXFCON_RXFITLMASK 0x3F00
+#define ASCRXFCON_RXFITLOFF 8
+#define ASCFSTAT_RXFFLMASK 0x003F
+#define ASCFSTAT_TXFFLMASK 0x3F00
+#define ASCFSTAT_TXFREEMASK 0x3F000000
+#define ASCFSTAT_TXFREEOFF 24
+
+static void lqasc_tx_chars(struct uart_port *port);
+static struct ltq_uart_port *lqasc_port[MAXPORTS];
+static struct uart_driver lqasc_reg;
+static DEFINE_SPINLOCK(ltq_asc_lock);
+
+struct ltq_uart_port {
+ struct uart_port port;
+ struct clk *clk;
+ unsigned int tx_irq;
+ unsigned int rx_irq;
+ unsigned int err_irq;
+};
+
+static inline struct
+ltq_uart_port *to_ltq_uart_port(struct uart_port *port)
+{
+ return container_of(port, struct ltq_uart_port, port);
+}
+
+static void
+lqasc_stop_tx(struct uart_port *port)
+{
+ return;
+}
+
+static void
+lqasc_start_tx(struct uart_port *port)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&ltq_asc_lock, flags);
+ lqasc_tx_chars(port);
+ spin_unlock_irqrestore(&ltq_asc_lock, flags);
+ return;
+}
+
+static void
+lqasc_stop_rx(struct uart_port *port)
+{
+ ltq_w32(ASCWHBSTATE_CLRREN, port->membase + LTQ_ASC_WHBSTATE);
+}
+
+static void
+lqasc_enable_ms(struct uart_port *port)
+{
+}
+
+static int
+lqasc_rx_chars(struct uart_port *port)
+{
+ struct tty_struct *tty = tty_port_tty_get(&port->state->port);
+ unsigned int ch = 0, rsr = 0, fifocnt;
+
+ if (!tty) {
+ dev_dbg(port->dev, "%s:tty is busy now", __func__);
+ return -EBUSY;
+ }
+ fifocnt =
+ ltq_r32(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_RXFFLMASK;
+ while (fifocnt--) {
+ u8 flag = TTY_NORMAL;
+ ch = ltq_r8(port->membase + LTQ_ASC_RBUF);
+ rsr = (ltq_r32(port->membase + LTQ_ASC_STATE)
+ & ASCSTATE_ANY) | UART_DUMMY_UER_RX;
+ tty_flip_buffer_push(tty);
+ port->icount.rx++;
+
+ /*
+ * Note that the error handling code is
+ * out of the main execution path
+ */
+ if (rsr & ASCSTATE_ANY) {
+ if (rsr & ASCSTATE_PE) {
+ port->icount.parity++;
+ ltq_w32_mask(0, ASCWHBSTATE_CLRPE,
+ port->membase + LTQ_ASC_WHBSTATE);
+ } else if (rsr & ASCSTATE_FE) {
+ port->icount.frame++;
+ ltq_w32_mask(0, ASCWHBSTATE_CLRFE,
+ port->membase + LTQ_ASC_WHBSTATE);
+ }
+ if (rsr & ASCSTATE_ROE) {
+ port->icount.overrun++;
+ ltq_w32_mask(0, ASCWHBSTATE_CLRROE,
+ port->membase + LTQ_ASC_WHBSTATE);
+ }
+
+ rsr &= port->read_status_mask;
+
+ if (rsr & ASCSTATE_PE)
+ flag = TTY_PARITY;
+ else if (rsr & ASCSTATE_FE)
+ flag = TTY_FRAME;
+ }
+
+ if ((rsr & port->ignore_status_mask) == 0)
+ tty_insert_flip_char(tty, ch, flag);
+
+ if (rsr & ASCSTATE_ROE)
+ /*
+ * Overrun is special, since it's reported
+ * immediately, and doesn't affect the current
+ * character
+ */
+ tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ }
+ if (ch != 0)
+ tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
+ return 0;
+}
+
+static void
+lqasc_tx_chars(struct uart_port *port)
+{
+ struct circ_buf *xmit = &port->state->xmit;
+ if (uart_tx_stopped(port)) {
+ lqasc_stop_tx(port);
+ return;
+ }
+
+ while (((ltq_r32(port->membase + LTQ_ASC_FSTAT) &
+ ASCFSTAT_TXFREEMASK) >> ASCFSTAT_TXFREEOFF) != 0) {
+ if (port->x_char) {
+ ltq_w8(port->x_char, port->membase + LTQ_ASC_TBUF);
+ port->icount.tx++;
+ port->x_char = 0;
+ continue;
+ }
+
+ if (uart_circ_empty(xmit))
+ break;
+
+ ltq_w8(port->state->xmit.buf[port->state->xmit.tail],
+ port->membase + LTQ_ASC_TBUF);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+}
+
+static irqreturn_t
+lqasc_tx_int(int irq, void *_port)
+{
+ unsigned long flags;
+ struct uart_port *port = (struct uart_port *)_port;
+ spin_lock_irqsave(&ltq_asc_lock, flags);
+ ltq_w32(ASC_IRNCR_TIR, port->membase + LTQ_ASC_IRNCR);
+ spin_unlock_irqrestore(&ltq_asc_lock, flags);
+ lqasc_start_tx(port);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+lqasc_err_int(int irq, void *_port)
+{
+ unsigned long flags;
+ struct uart_port *port = (struct uart_port *)_port;
+ spin_lock_irqsave(&ltq_asc_lock, flags);
+ /* clear any pending interrupts */
+ ltq_w32_mask(0, ASCWHBSTATE_CLRPE | ASCWHBSTATE_CLRFE |
+ ASCWHBSTATE_CLRROE, port->membase + LTQ_ASC_WHBSTATE);
+ spin_unlock_irqrestore(&ltq_asc_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+lqasc_rx_int(int irq, void *_port)
+{
+ unsigned long flags;
+ struct uart_port *port = (struct uart_port *)_port;
+ spin_lock_irqsave(&ltq_asc_lock, flags);
+ ltq_w32(ASC_IRNCR_RIR, port->membase + LTQ_ASC_IRNCR);
+ lqasc_rx_chars(port);
+ spin_unlock_irqrestore(&ltq_asc_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static unsigned int
+lqasc_tx_empty(struct uart_port *port)
+{
+ int status;
+ status = ltq_r32(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_TXFFLMASK;
+ return status ? 0 : TIOCSER_TEMT;
+}
+
+static unsigned int
+lqasc_get_mctrl(struct uart_port *port)
+{
+ return TIOCM_CTS | TIOCM_CAR | TIOCM_DSR;
+}
+
+static void
+lqasc_set_mctrl(struct uart_port *port, u_int mctrl)
+{
+}
+
+static void
+lqasc_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+static int
+lqasc_startup(struct uart_port *port)
+{
+ struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
+ int retval;
+
+ port->uartclk = clk_get_rate(ltq_port->clk);
+
+ ltq_w32_mask(ASCCLC_DISS | ASCCLC_RMCMASK, (1 << ASCCLC_RMCOFFSET),
+ port->membase + LTQ_ASC_CLC);
+
+ ltq_w32(0, port->membase + LTQ_ASC_PISEL);
+ ltq_w32(
+ ((TXFIFO_FL << ASCTXFCON_TXFITLOFF) & ASCTXFCON_TXFITLMASK) |
+ ASCTXFCON_TXFEN | ASCTXFCON_TXFFLU,
+ port->membase + LTQ_ASC_TXFCON);
+ ltq_w32(
+ ((RXFIFO_FL << ASCRXFCON_RXFITLOFF) & ASCRXFCON_RXFITLMASK)
+ | ASCRXFCON_RXFEN | ASCRXFCON_RXFFLU,
+ port->membase + LTQ_ASC_RXFCON);
+ /* make sure other settings are written to hardware before
+ * setting enable bits
+ */
+ wmb();
+ ltq_w32_mask(0, ASCCON_M_8ASYNC | ASCCON_FEN | ASCCON_TOEN |
+ ASCCON_ROEN, port->membase + LTQ_ASC_CON);
+
+ retval = request_irq(ltq_port->tx_irq, lqasc_tx_int,
+ IRQF_DISABLED, "asc_tx", port);
+ if (retval) {
+ pr_err("failed to request lqasc_tx_int\n");
+ return retval;
+ }
+
+ retval = request_irq(ltq_port->rx_irq, lqasc_rx_int,
+ IRQF_DISABLED, "asc_rx", port);
+ if (retval) {
+ pr_err("failed to request lqasc_rx_int\n");
+ goto err1;
+ }
+
+ retval = request_irq(ltq_port->err_irq, lqasc_err_int,
+ IRQF_DISABLED, "asc_err", port);
+ if (retval) {
+ pr_err("failed to request lqasc_err_int\n");
+ goto err2;
+ }
+
+ ltq_w32(ASC_IRNREN_RX | ASC_IRNREN_ERR | ASC_IRNREN_TX,
+ port->membase + LTQ_ASC_IRNREN);
+ return 0;
+
+err2:
+ free_irq(ltq_port->rx_irq, port);
+err1:
+ free_irq(ltq_port->tx_irq, port);
+ return retval;
+}
+
+static void
+lqasc_shutdown(struct uart_port *port)
+{
+ struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
+ free_irq(ltq_port->tx_irq, port);
+ free_irq(ltq_port->rx_irq, port);
+ free_irq(ltq_port->err_irq, port);
+
+ ltq_w32(0, port->membase + LTQ_ASC_CON);
+ ltq_w32_mask(ASCRXFCON_RXFEN, ASCRXFCON_RXFFLU,
+ port->membase + LTQ_ASC_RXFCON);
+ ltq_w32_mask(ASCTXFCON_TXFEN, ASCTXFCON_TXFFLU,
+ port->membase + LTQ_ASC_TXFCON);
+}
+
+static void
+lqasc_set_termios(struct uart_port *port,
+ struct ktermios *new, struct ktermios *old)
+{
+ unsigned int cflag;
+ unsigned int iflag;
+ unsigned int divisor;
+ unsigned int baud;
+ unsigned int con = 0;
+ unsigned long flags;
+
+ cflag = new->c_cflag;
+ iflag = new->c_iflag;
+
+ switch (cflag & CSIZE) {
+ case CS7:
+ con = ASCCON_M_7ASYNC;
+ break;
+
+ case CS5:
+ case CS6:
+ default:
+ new->c_cflag &= ~ CSIZE;
+ new->c_cflag |= CS8;
+ con = ASCCON_M_8ASYNC;
+ break;
+ }
+
+ cflag &= ~CMSPAR; /* Mark/Space parity is not supported */
+
+ if (cflag & CSTOPB)
+ con |= ASCCON_STP;
+
+ if (cflag & PARENB) {
+ if (!(cflag & PARODD))
+ con &= ~ASCCON_ODD;
+ else
+ con |= ASCCON_ODD;
+ }
+
+ port->read_status_mask = ASCSTATE_ROE;
+ if (iflag & INPCK)
+ port->read_status_mask |= ASCSTATE_FE | ASCSTATE_PE;
+
+ port->ignore_status_mask = 0;
+ if (iflag & IGNPAR)
+ port->ignore_status_mask |= ASCSTATE_FE | ASCSTATE_PE;
+
+ if (iflag & IGNBRK) {
+ /*
+ * If we're ignoring parity and break indicators,
+ * ignore overruns too (for real raw support).
+ */
+ if (iflag & IGNPAR)
+ port->ignore_status_mask |= ASCSTATE_ROE;
+ }
+
+ if ((cflag & CREAD) == 0)
+ port->ignore_status_mask |= UART_DUMMY_UER_RX;
+
+ /* set error signals - framing, parity and overrun, enable receiver */
+ con |= ASCCON_FEN | ASCCON_TOEN | ASCCON_ROEN;
+
+ spin_lock_irqsave(&ltq_asc_lock, flags);
+
+ /* set up CON */
+ ltq_w32_mask(0, con, port->membase + LTQ_ASC_CON);
+
+ /* Set baud rate - take a divider of 2 into account */
+ baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
+ divisor = uart_get_divisor(port, baud);
+ divisor = divisor / 2 - 1;
+
+ /* disable the baudrate generator */
+ ltq_w32_mask(ASCCON_R, 0, port->membase + LTQ_ASC_CON);
+
+ /* make sure the fractional divider is off */
+ ltq_w32_mask(ASCCON_FDE, 0, port->membase + LTQ_ASC_CON);
+
+ /* set up to use divisor of 2 */
+ ltq_w32_mask(ASCCON_BRS, 0, port->membase + LTQ_ASC_CON);
+
+ /* now we can write the new baudrate into the register */
+ ltq_w32(divisor, port->membase + LTQ_ASC_BG);
+
+ /* turn the baudrate generator back on */
+ ltq_w32_mask(0, ASCCON_R, port->membase + LTQ_ASC_CON);
+
+ /* enable rx */
+ ltq_w32(ASCWHBSTATE_SETREN, port->membase + LTQ_ASC_WHBSTATE);
+
+ spin_unlock_irqrestore(&ltq_asc_lock, flags);
+
+ /* Don't rewrite B0 */
+ if (tty_termios_baud_rate(new))
+ tty_termios_encode_baud_rate(new, baud, baud);
+}
+
+static const char*
+lqasc_type(struct uart_port *port)
+{
+ if (port->type == PORT_LTQ_ASC)
+ return DRVNAME;
+ else
+ return NULL;
+}
+
+static void
+lqasc_release_port(struct uart_port *port)
+{
+ if (port->flags & UPF_IOREMAP) {
+ iounmap(port->membase);
+ port->membase = NULL;
+ }
+}
+
+static int
+lqasc_request_port(struct uart_port *port)
+{
+ struct platform_device *pdev = to_platform_device(port->dev);
+ struct resource *res;
+ int size;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "cannot obtain I/O memory region");
+ return -ENODEV;
+ }
+ size = resource_size(res);
+
+ res = devm_request_mem_region(&pdev->dev, res->start,
+ size, dev_name(&pdev->dev));
+ if (!res) {
+ dev_err(&pdev->dev, "cannot request I/O memory region");
+ return -EBUSY;
+ }
+
+ if (port->flags & UPF_IOREMAP) {
+ port->membase = devm_ioremap_nocache(&pdev->dev,
+ port->mapbase, size);
+ if (port->membase == NULL)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void
+lqasc_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE) {
+ port->type = PORT_LTQ_ASC;
+ lqasc_request_port(port);
+ }
+}
+
+static int
+lqasc_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ int ret = 0;
+ if (ser->type != PORT_UNKNOWN && ser->type != PORT_LTQ_ASC)
+ ret = -EINVAL;
+ if (ser->irq < 0 || ser->irq >= NR_IRQS)
+ ret = -EINVAL;
+ if (ser->baud_base < 9600)
+ ret = -EINVAL;
+ return ret;
+}
+
+static struct uart_ops lqasc_pops = {
+ .tx_empty = lqasc_tx_empty,
+ .set_mctrl = lqasc_set_mctrl,
+ .get_mctrl = lqasc_get_mctrl,
+ .stop_tx = lqasc_stop_tx,
+ .start_tx = lqasc_start_tx,
+ .stop_rx = lqasc_stop_rx,
+ .enable_ms = lqasc_enable_ms,
+ .break_ctl = lqasc_break_ctl,
+ .startup = lqasc_startup,
+ .shutdown = lqasc_shutdown,
+ .set_termios = lqasc_set_termios,
+ .type = lqasc_type,
+ .release_port = lqasc_release_port,
+ .request_port = lqasc_request_port,
+ .config_port = lqasc_config_port,
+ .verify_port = lqasc_verify_port,
+};
+
+static void
+lqasc_console_putchar(struct uart_port *port, int ch)
+{
+ int fifofree;
+
+ if (!port->membase)
+ return;
+
+ do {
+ fifofree = (ltq_r32(port->membase + LTQ_ASC_FSTAT)
+ & ASCFSTAT_TXFREEMASK) >> ASCFSTAT_TXFREEOFF;
+ } while (fifofree == 0);
+ ltq_w8(ch, port->membase + LTQ_ASC_TBUF);
+}
+
+
+static void
+lqasc_console_write(struct console *co, const char *s, u_int count)
+{
+ struct ltq_uart_port *ltq_port;
+ struct uart_port *port;
+ unsigned long flags;
+
+ if (co->index >= MAXPORTS)
+ return;
+
+ ltq_port = lqasc_port[co->index];
+ if (!ltq_port)
+ return;
+
+ port = &ltq_port->port;
+
+ spin_lock_irqsave(&ltq_asc_lock, flags);
+ uart_console_write(port, s, count, lqasc_console_putchar);
+ spin_unlock_irqrestore(&ltq_asc_lock, flags);
+}
+
+static int __init
+lqasc_console_setup(struct console *co, char *options)
+{
+ struct ltq_uart_port *ltq_port;
+ struct uart_port *port;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ if (co->index >= MAXPORTS)
+ return -ENODEV;
+
+ ltq_port = lqasc_port[co->index];
+ if (!ltq_port)
+ return -ENODEV;
+
+ port = &ltq_port->port;
+
+ port->uartclk = clk_get_rate(ltq_port->clk);
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+ return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct console lqasc_console = {
+ .name = "ttyLTQ",
+ .write = lqasc_console_write,
+ .device = uart_console_device,
+ .setup = lqasc_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &lqasc_reg,
+};
+
+static int __init
+lqasc_console_init(void)
+{
+ register_console(&lqasc_console);
+ return 0;
+}
+console_initcall(lqasc_console_init);
+
+static struct uart_driver lqasc_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = DRVNAME,
+ .dev_name = "ttyLTQ",
+ .major = 0,
+ .minor = 0,
+ .nr = MAXPORTS,
+ .cons = &lqasc_console,
+};
+
+static int __init
+lqasc_probe(struct platform_device *pdev)
+{
+ struct ltq_uart_port *ltq_port;
+ struct uart_port *port;
+ struct resource *mmres, *irqres;
+ int tx_irq, rx_irq, err_irq;
+ struct clk *clk;
+ int ret;
+
+ mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!mmres || !irqres)
+ return -ENODEV;
+
+ if (pdev->id >= MAXPORTS)
+ return -EBUSY;
+
+ if (lqasc_port[pdev->id] != NULL)
+ return -EBUSY;
+
+ clk = clk_get(&pdev->dev, "fpi");
+ if (IS_ERR(clk)) {
+ pr_err("failed to get fpi clk\n");
+ return -ENOENT;
+ }
+
+ tx_irq = platform_get_irq_byname(pdev, "tx");
+ rx_irq = platform_get_irq_byname(pdev, "rx");
+ err_irq = platform_get_irq_byname(pdev, "err");
+ if ((tx_irq < 0) | (rx_irq < 0) | (err_irq < 0))
+ return -ENODEV;
+
+ ltq_port = kzalloc(sizeof(struct ltq_uart_port), GFP_KERNEL);
+ if (!ltq_port)
+ return -ENOMEM;
+
+ port = &ltq_port->port;
+
+ port->iotype = SERIAL_IO_MEM;
+ port->flags = ASYNC_BOOT_AUTOCONF | UPF_IOREMAP;
+ port->ops = &lqasc_pops;
+ port->fifosize = 16;
+ port->type = PORT_LTQ_ASC,
+ port->line = pdev->id;
+ port->dev = &pdev->dev;
+
+ port->irq = tx_irq; /* unused, just to be backward-compatibe */
+ port->mapbase = mmres->start;
+
+ ltq_port->clk = clk;
+
+ ltq_port->tx_irq = tx_irq;
+ ltq_port->rx_irq = rx_irq;
+ ltq_port->err_irq = err_irq;
+
+ lqasc_port[pdev->id] = ltq_port;
+ platform_set_drvdata(pdev, ltq_port);
+
+ ret = uart_add_one_port(&lqasc_reg, port);
+
+ return ret;
+}
+
+static struct platform_driver lqasc_driver = {
+ .driver = {
+ .name = DRVNAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+int __init
+init_lqasc(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&lqasc_reg);
+ if (ret != 0)
+ return ret;
+
+ ret = platform_driver_probe(&lqasc_driver, lqasc_probe);
+ if (ret != 0)
+ uart_unregister_driver(&lqasc_reg);
+
+ return ret;
+}
+
+module_init(init_lqasc);
+
+MODULE_DESCRIPTION("Lantiq serial port driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c
index 0e8eec516df..c911b2419ab 100644
--- a/drivers/tty/serial/of_serial.c
+++ b/drivers/tty/serial/of_serial.c
@@ -80,14 +80,17 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
/*
* Try to register a serial port
*/
+static struct of_device_id of_platform_serial_table[];
static int __devinit of_platform_serial_probe(struct platform_device *ofdev)
{
+ const struct of_device_id *match;
struct of_serial_info *info;
struct uart_port port;
int port_type;
int ret;
- if (!ofdev->dev.of_match)
+ match = of_match_device(of_platform_serial_table, &ofdev->dev);
+ if (!match)
return -EINVAL;
if (of_find_property(ofdev->dev.of_node, "used-by-rtas", NULL))
@@ -97,7 +100,7 @@ static int __devinit of_platform_serial_probe(struct platform_device *ofdev)
if (info == NULL)
return -ENOMEM;
- port_type = (unsigned long)ofdev->dev.of_match->data;
+ port_type = (unsigned long)match->data;
ret = of_platform_serial_setup(ofdev, port_type, &port);
if (ret)
goto out;
diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c
index 51fe1795d5a..d2efe823c20 100644
--- a/drivers/uio/uio.c
+++ b/drivers/uio/uio.c
@@ -381,7 +381,13 @@ static int uio_get_minor(struct uio_device *idev)
retval = -ENOMEM;
goto exit;
}
- idev->minor = id & MAX_ID_MASK;
+ if (id < UIO_MAX_DEVICES) {
+ idev->minor = id;
+ } else {
+ dev_err(idev->dev, "too many uio devices\n");
+ retval = -EINVAL;
+ idr_remove(&uio_idr, id);
+ }
exit:
mutex_unlock(&minor_lock);
return retval;
@@ -587,14 +593,12 @@ static ssize_t uio_write(struct file *filep, const char __user *buf,
static int uio_find_mem_index(struct vm_area_struct *vma)
{
- int mi;
struct uio_device *idev = vma->vm_private_data;
- for (mi = 0; mi < MAX_UIO_MAPS; mi++) {
- if (idev->info->mem[mi].size == 0)
+ if (vma->vm_pgoff < MAX_UIO_MAPS) {
+ if (idev->info->mem[vma->vm_pgoff].size == 0)
return -1;
- if (vma->vm_pgoff == mi)
- return mi;
+ return (int)vma->vm_pgoff;
}
return -1;
}
diff --git a/drivers/uio/uio_netx.c b/drivers/uio/uio_netx.c
index 5ffdb483b01..a879fd5741f 100644
--- a/drivers/uio/uio_netx.c
+++ b/drivers/uio/uio_netx.c
@@ -18,6 +18,9 @@
#define PCI_VENDOR_ID_HILSCHER 0x15CF
#define PCI_DEVICE_ID_HILSCHER_NETX 0x0000
+#define PCI_DEVICE_ID_HILSCHER_NETPLC 0x0010
+#define PCI_SUBDEVICE_ID_NETPLC_RAM 0x0000
+#define PCI_SUBDEVICE_ID_NETPLC_FLASH 0x0001
#define PCI_SUBDEVICE_ID_NXSB_PCA 0x3235
#define PCI_SUBDEVICE_ID_NXPCA 0x3335
@@ -66,6 +69,10 @@ static int __devinit netx_pci_probe(struct pci_dev *dev,
bar = 0;
info->name = "netx";
break;
+ case PCI_DEVICE_ID_HILSCHER_NETPLC:
+ bar = 0;
+ info->name = "netplc";
+ break;
default:
bar = 2;
info->name = "netx_plx";
@@ -134,6 +141,18 @@ static struct pci_device_id netx_pci_ids[] = {
.subdevice = 0,
},
{
+ .vendor = PCI_VENDOR_ID_HILSCHER,
+ .device = PCI_DEVICE_ID_HILSCHER_NETPLC,
+ .subvendor = PCI_VENDOR_ID_HILSCHER,
+ .subdevice = PCI_SUBDEVICE_ID_NETPLC_RAM,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_HILSCHER,
+ .device = PCI_DEVICE_ID_HILSCHER_NETPLC,
+ .subvendor = PCI_VENDOR_ID_HILSCHER,
+ .subdevice = PCI_SUBDEVICE_ID_NETPLC_FLASH,
+ },
+ {
.vendor = PCI_VENDOR_ID_PLX,
.device = PCI_DEVICE_ID_PLX_9030,
.subvendor = PCI_VENDOR_ID_PLX,
diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c
index 7174d518b8a..0f424af7f10 100644
--- a/drivers/uio/uio_pdrv_genirq.c
+++ b/drivers/uio/uio_pdrv_genirq.c
@@ -189,6 +189,10 @@ static int uio_pdrv_genirq_remove(struct platform_device *pdev)
uio_unregister_device(priv->uioinfo);
pm_runtime_disable(&pdev->dev);
+
+ priv->uioinfo->handler = NULL;
+ priv->uioinfo->irqcontrol = NULL;
+
kfree(priv);
return 0;
}
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 41b6e51188e..006489d82dc 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -66,6 +66,7 @@ config USB_ARCH_HAS_EHCI
default y if ARCH_VT8500
default y if PLAT_SPEAR
default y if ARCH_MSM
+ default y if MICROBLAZE
default PCI
# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index a3d2e239965..96fdfb815f8 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -221,7 +221,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end,
break;
case USB_ENDPOINT_XFER_INT:
type = "Int.";
- if (speed == USB_SPEED_HIGH)
+ if (speed == USB_SPEED_HIGH || speed == USB_SPEED_SUPER)
interval = 1 << (desc->bInterval - 1);
else
interval = desc->bInterval;
@@ -229,7 +229,8 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end,
default: /* "can't happen" */
return start;
}
- interval *= (speed == USB_SPEED_HIGH) ? 125 : 1000;
+ interval *= (speed == USB_SPEED_HIGH ||
+ speed == USB_SPEED_SUPER) ? 125 : 1000;
if (interval % 1000)
unit = 'u';
else {
@@ -542,8 +543,9 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes,
if (level == 0) {
int max;
- /* high speed reserves 80%, full/low reserves 90% */
- if (usbdev->speed == USB_SPEED_HIGH)
+ /* super/high speed reserves 80%, full/low reserves 90% */
+ if (usbdev->speed == USB_SPEED_HIGH ||
+ usbdev->speed == USB_SPEED_SUPER)
max = 800;
else
max = FRAME_TIME_MAX_USECS_ALLOC;
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 8eed05d2383..77a7faec8d7 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1908,7 +1908,7 @@ void usb_free_streams(struct usb_interface *interface,
/* Streams only apply to bulk endpoints. */
for (i = 0; i < num_eps; i++)
- if (!usb_endpoint_xfer_bulk(&eps[i]->desc))
+ if (!eps[i] || !usb_endpoint_xfer_bulk(&eps[i]->desc))
return;
hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 8fb754916c6..93720bdc9ef 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2285,7 +2285,17 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
}
/* see 7.1.7.6 */
- status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND);
+ /* Clear PORT_POWER if it's a USB3.0 device connected to USB 3.0
+ * external hub.
+ * FIXME: this is a temporary workaround to make the system able
+ * to suspend/resume.
+ */
+ if ((hub->hdev->parent != NULL) && hub_is_superspeed(hub->hdev))
+ status = clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_POWER);
+ else
+ status = set_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_SUSPEND);
if (status) {
dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n",
port1, status);
diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c
index 9abecfddb27..0111f8a9cf7 100644
--- a/drivers/usb/gadget/f_audio.c
+++ b/drivers/usb/gadget/f_audio.c
@@ -706,6 +706,7 @@ f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
struct f_audio *audio = func_to_audio(f);
usb_free_descriptors(f->descriptors);
+ usb_free_descriptors(f->hs_descriptors);
kfree(audio);
}
diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c
index 95dd4662d6a..b3c30429015 100644
--- a/drivers/usb/gadget/f_eem.c
+++ b/drivers/usb/gadget/f_eem.c
@@ -314,6 +314,9 @@ eem_unbind(struct usb_configuration *c, struct usb_function *f)
static void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req)
{
+ struct sk_buff *skb = (struct sk_buff *)req->context;
+
+ dev_kfree_skb_any(skb);
}
/*
@@ -428,10 +431,11 @@ static int eem_unwrap(struct gether *port,
skb_trim(skb2, len);
put_unaligned_le16(BIT(15) | BIT(11) | len,
skb_push(skb2, 2));
- skb_copy_bits(skb, 0, req->buf, skb->len);
- req->length = skb->len;
+ skb_copy_bits(skb2, 0, req->buf, skb2->len);
+ req->length = skb2->len;
req->complete = eem_cmd_complete;
req->zero = 1;
+ req->context = skb2;
if (usb_ep_queue(port->in_ep, req, GFP_ATOMIC))
DBG(cdev, "echo response queue fail\n");
break;
diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c
index aee7e3c53c3..3a68e09309f 100644
--- a/drivers/usb/gadget/fsl_qe_udc.c
+++ b/drivers/usb/gadget/fsl_qe_udc.c
@@ -1148,6 +1148,12 @@ static int qe_ep_tx(struct qe_ep *ep, struct qe_frame *frame)
static int txcomplete(struct qe_ep *ep, unsigned char restart)
{
if (ep->tx_req != NULL) {
+ struct qe_req *req = ep->tx_req;
+ unsigned zlp = 0, last_len = 0;
+
+ last_len = min_t(unsigned, req->req.length - ep->sent,
+ ep->ep.maxpacket);
+
if (!restart) {
int asent = ep->last;
ep->sent += asent;
@@ -1156,9 +1162,18 @@ static int txcomplete(struct qe_ep *ep, unsigned char restart)
ep->last = 0;
}
+ /* zlp needed when req->re.zero is set */
+ if (req->req.zero) {
+ if (last_len == 0 ||
+ (req->req.length % ep->ep.maxpacket) != 0)
+ zlp = 0;
+ else
+ zlp = 1;
+ } else
+ zlp = 0;
+
/* a request already were transmitted completely */
- if ((ep->tx_req->req.length - ep->sent) <= 0) {
- ep->tx_req->req.actual = (unsigned int)ep->sent;
+ if (((ep->tx_req->req.length - ep->sent) <= 0) && !zlp) {
done(ep, ep->tx_req, 0);
ep->tx_req = NULL;
ep->last = 0;
@@ -1191,6 +1206,7 @@ static int qe_usb_senddata(struct qe_ep *ep, struct qe_frame *frame)
buf = (u8 *)ep->tx_req->req.buf + ep->sent;
if (buf && size) {
ep->last = size;
+ ep->tx_req->req.actual += size;
frame_set_data(frame, buf);
frame_set_length(frame, size);
frame_set_status(frame, FRAME_OK);
@@ -2523,15 +2539,18 @@ static void qe_udc_release(struct device *dev)
}
/* Driver probe functions */
+static const struct of_device_id qe_udc_match[];
static int __devinit qe_udc_probe(struct platform_device *ofdev)
{
+ const struct of_device_id *match;
struct device_node *np = ofdev->dev.of_node;
struct qe_ep *ep;
unsigned int ret = 0;
unsigned int i;
const void *prop;
- if (!ofdev->dev.of_match)
+ match = of_match_device(qe_udc_match, &ofdev->dev);
+ if (!match)
return -EINVAL;
prop = of_get_property(np, "mode", NULL);
@@ -2545,7 +2564,7 @@ static int __devinit qe_udc_probe(struct platform_device *ofdev)
return -ENOMEM;
}
- udc_controller->soc_type = (unsigned long)ofdev->dev.of_match->data;
+ udc_controller->soc_type = (unsigned long)match->data;
udc_controller->usb_regs = of_iomap(np, 0);
if (!udc_controller->usb_regs) {
ret = -ENOMEM;
diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c
index 48a760220ba..bf6e11c758d 100644
--- a/drivers/usb/gadget/goku_udc.c
+++ b/drivers/usb/gadget/goku_udc.c
@@ -38,6 +38,7 @@
#include <linux/device.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/prefetch.h>
#include <asm/byteorder.h>
#include <asm/io.h>
diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c
index 5408186afc3..ade40066dec 100644
--- a/drivers/usb/gadget/imx_udc.c
+++ b/drivers/usb/gadget/imx_udc.c
@@ -30,6 +30,7 @@
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/slab.h>
+#include <linux/prefetch.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index 3ed73f49cf1..a01383f71f3 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -386,8 +386,10 @@ ep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
/* halt any endpoint by doing a "wrong direction" i/o call */
if (usb_endpoint_dir_in(&data->desc)) {
- if (usb_endpoint_xfer_isoc(&data->desc))
+ if (usb_endpoint_xfer_isoc(&data->desc)) {
+ mutex_unlock(&data->lock);
return -EINVAL;
+ }
DBG (data->dev, "%s halt\n", data->name);
spin_lock_irq (&data->dev->lock);
if (likely (data->ep != NULL))
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index cb5cd422f3f..82fd2493533 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -44,6 +44,7 @@
#include <linux/usb/otg.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
+#include <linux/prefetch.h>
#include <asm/byteorder.h>
#include <asm/io.h>
diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c
index 3e4b35e50c2..68dbcc3e4cc 100644
--- a/drivers/usb/gadget/pch_udc.c
+++ b/drivers/usb/gadget/pch_udc.c
@@ -1608,7 +1608,7 @@ static int pch_udc_pcd_queue(struct usb_ep *usbep, struct usb_request *usbreq,
return -EINVAL;
if (!dev->driver || (dev->gadget.speed == USB_SPEED_UNKNOWN))
return -ESHUTDOWN;
- spin_lock_irqsave(&ep->dev->lock, iflags);
+ spin_lock_irqsave(&dev->lock, iflags);
/* map the buffer for dma */
if (usbreq->length &&
((usbreq->dma == DMA_ADDR_INVALID) || !usbreq->dma)) {
@@ -1625,8 +1625,10 @@ static int pch_udc_pcd_queue(struct usb_ep *usbep, struct usb_request *usbreq,
DMA_FROM_DEVICE);
} else {
req->buf = kzalloc(usbreq->length, GFP_ATOMIC);
- if (!req->buf)
- return -ENOMEM;
+ if (!req->buf) {
+ retval = -ENOMEM;
+ goto probe_end;
+ }
if (ep->in) {
memcpy(req->buf, usbreq->buf, usbreq->length);
req->dma = dma_map_single(&dev->pdev->dev,
diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c
index 444b60aa15e..365c02fc25f 100644
--- a/drivers/usb/gadget/pxa25x_udc.c
+++ b/drivers/usb/gadget/pxa25x_udc.c
@@ -46,6 +46,7 @@
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/io.h>
+#include <linux/prefetch.h>
#include <asm/byteorder.h>
#include <asm/dma.h>
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c
index 78a39a41547..57607696735 100644
--- a/drivers/usb/gadget/pxa27x_udc.c
+++ b/drivers/usb/gadget/pxa27x_udc.c
@@ -32,6 +32,7 @@
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/slab.h>
+#include <linux/prefetch.h>
#include <asm/byteorder.h>
#include <mach/hardware.h>
diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c
index 015118535f7..6dcc1f68fa6 100644
--- a/drivers/usb/gadget/r8a66597-udc.c
+++ b/drivers/usb/gadget/r8a66597-udc.c
@@ -1083,7 +1083,9 @@ static void irq_device_state(struct r8a66597 *r8a66597)
if (dvsq == DS_DFLT) {
/* bus reset */
+ spin_unlock(&r8a66597->lock);
r8a66597->driver->disconnect(&r8a66597->gadget);
+ spin_lock(&r8a66597->lock);
r8a66597_update_usb_speed(r8a66597);
}
if (r8a66597->old_dvsq == DS_CNFG && dvsq != DS_CNFG)
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index 7e41a95c5ce..627f3a67875 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -40,6 +40,7 @@
#include <linux/slab.h>
#include <linux/usb/ulpi.h>
#include <plat/usb.h>
+#include <linux/regulator/consumer.h>
/* EHCI Register Set */
#define EHCI_INSNREG04 (0xA0)
@@ -118,6 +119,8 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
struct ehci_hcd *omap_ehci;
int ret = -ENODEV;
int irq;
+ int i;
+ char supply[7];
if (usb_disabled())
return -ENODEV;
@@ -158,6 +161,23 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
hcd->rsrc_len = resource_size(res);
hcd->regs = regs;
+ /* get ehci regulator and enable */
+ for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) {
+ if (pdata->port_mode[i] != OMAP_EHCI_PORT_MODE_PHY) {
+ pdata->regulator[i] = NULL;
+ continue;
+ }
+ snprintf(supply, sizeof(supply), "hsusb%d", i);
+ pdata->regulator[i] = regulator_get(dev, supply);
+ if (IS_ERR(pdata->regulator[i])) {
+ pdata->regulator[i] = NULL;
+ dev_dbg(dev,
+ "failed to get ehci port%d regulator\n", i);
+ } else {
+ regulator_enable(pdata->regulator[i]);
+ }
+ }
+
ret = omap_usbhs_enable(dev);
if (ret) {
dev_err(dev, "failed to start usbhs with err %d\n", ret);
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 98ded66e8d3..42abd0f603b 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -1247,24 +1247,27 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
static void scan_async (struct ehci_hcd *ehci)
{
+ bool stopped;
struct ehci_qh *qh;
enum ehci_timer_action action = TIMER_IO_WATCHDOG;
ehci->stamp = ehci_readl(ehci, &ehci->regs->frame_index);
timer_action_done (ehci, TIMER_ASYNC_SHRINK);
rescan:
+ stopped = !HC_IS_RUNNING(ehci_to_hcd(ehci)->state);
qh = ehci->async->qh_next.qh;
if (likely (qh != NULL)) {
do {
/* clean any finished work for this qh */
- if (!list_empty (&qh->qtd_list)
- && qh->stamp != ehci->stamp) {
+ if (!list_empty(&qh->qtd_list) && (stopped ||
+ qh->stamp != ehci->stamp)) {
int temp;
/* unlinks could happen here; completion
* reporting drops the lock. rescan using
* the latest schedule, but don't rescan
- * qhs we already finished (no looping).
+ * qhs we already finished (no looping)
+ * unless the controller is stopped.
*/
qh = qh_get (qh);
qh->stamp = ehci->stamp;
@@ -1285,9 +1288,9 @@ rescan:
*/
if (list_empty(&qh->qtd_list)
&& qh->qh_state == QH_STATE_LINKED) {
- if (!ehci->reclaim
- && ((ehci->stamp - qh->stamp) & 0x1fff)
- >= (EHCI_SHRINK_FRAMES * 8))
+ if (!ehci->reclaim && (stopped ||
+ ((ehci->stamp - qh->stamp) & 0x1fff)
+ >= EHCI_SHRINK_FRAMES * 8))
start_unlink_async(ehci, qh);
else
action = TIMER_ASYNC_SHRINK;
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
index f97570a847c..9c37dad3e81 100644
--- a/drivers/usb/host/isp1362-hcd.c
+++ b/drivers/usb/host/isp1362-hcd.c
@@ -81,6 +81,7 @@
#include <linux/pm.h>
#include <linux/io.h>
#include <linux/bitmap.h>
+#include <linux/prefetch.h>
#include <asm/irq.h>
#include <asm/system.h>
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c
index f50e84ac570..7b2e69aa2e9 100644
--- a/drivers/usb/host/isp1760-hcd.c
+++ b/drivers/usb/host/isp1760-hcd.c
@@ -295,7 +295,7 @@ static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd)
}
dev_err(hcd->self.controller,
- "%s: Can not allocate %lu bytes of memory\n"
+ "%s: Cannot allocate %zu bytes of memory\n"
"Current memory map:\n",
__func__, qtd->length);
for (i = 0; i < BLOCKS; i++) {
@@ -1633,6 +1633,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
ints[i].qh = NULL;
ints[i].qtd = NULL;
+ urb->status = status;
isp1760_urb_done(hcd, urb);
if (qtd)
pe(hcd, qh, qtd);
diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c
index 17a6043c1fa..958d985f295 100644
--- a/drivers/usb/host/ohci-au1xxx.c
+++ b/drivers/usb/host/ohci-au1xxx.c
@@ -33,7 +33,7 @@
#ifdef __LITTLE_ENDIAN
#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C)
-#elif __BIG_ENDIAN
+#elif defined(__BIG_ENDIAN)
#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | \
USBH_ENABLE_BE)
#else
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index 1d586d4f7b5..9b166d70ae9 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -84,65 +84,92 @@ int usb_amd_find_chipset_info(void)
{
u8 rev = 0;
unsigned long flags;
+ struct amd_chipset_info info;
+ int ret;
spin_lock_irqsave(&amd_lock, flags);
- amd_chipset.probe_count++;
/* probe only once */
- if (amd_chipset.probe_count > 1) {
+ if (amd_chipset.probe_count > 0) {
+ amd_chipset.probe_count++;
spin_unlock_irqrestore(&amd_lock, flags);
return amd_chipset.probe_result;
}
+ memset(&info, 0, sizeof(info));
+ spin_unlock_irqrestore(&amd_lock, flags);
- amd_chipset.smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, 0x4385, NULL);
- if (amd_chipset.smbus_dev) {
- rev = amd_chipset.smbus_dev->revision;
+ info.smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, 0x4385, NULL);
+ if (info.smbus_dev) {
+ rev = info.smbus_dev->revision;
if (rev >= 0x40)
- amd_chipset.sb_type = 1;
+ info.sb_type = 1;
else if (rev >= 0x30 && rev <= 0x3b)
- amd_chipset.sb_type = 3;
+ info.sb_type = 3;
} else {
- amd_chipset.smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
- 0x780b, NULL);
- if (!amd_chipset.smbus_dev) {
- spin_unlock_irqrestore(&amd_lock, flags);
- return 0;
+ info.smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+ 0x780b, NULL);
+ if (!info.smbus_dev) {
+ ret = 0;
+ goto commit;
}
- rev = amd_chipset.smbus_dev->revision;
+
+ rev = info.smbus_dev->revision;
if (rev >= 0x11 && rev <= 0x18)
- amd_chipset.sb_type = 2;
+ info.sb_type = 2;
}
- if (amd_chipset.sb_type == 0) {
- if (amd_chipset.smbus_dev) {
- pci_dev_put(amd_chipset.smbus_dev);
- amd_chipset.smbus_dev = NULL;
+ if (info.sb_type == 0) {
+ if (info.smbus_dev) {
+ pci_dev_put(info.smbus_dev);
+ info.smbus_dev = NULL;
}
- spin_unlock_irqrestore(&amd_lock, flags);
- return 0;
+ ret = 0;
+ goto commit;
}
- amd_chipset.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x9601, NULL);
- if (amd_chipset.nb_dev) {
- amd_chipset.nb_type = 1;
+ info.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x9601, NULL);
+ if (info.nb_dev) {
+ info.nb_type = 1;
} else {
- amd_chipset.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD,
- 0x1510, NULL);
- if (amd_chipset.nb_dev) {
- amd_chipset.nb_type = 2;
- } else {
- amd_chipset.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD,
- 0x9600, NULL);
- if (amd_chipset.nb_dev)
- amd_chipset.nb_type = 3;
+ info.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x1510, NULL);
+ if (info.nb_dev) {
+ info.nb_type = 2;
+ } else {
+ info.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+ 0x9600, NULL);
+ if (info.nb_dev)
+ info.nb_type = 3;
}
}
- amd_chipset.probe_result = 1;
+ ret = info.probe_result = 1;
printk(KERN_DEBUG "QUIRK: Enable AMD PLL fix\n");
- spin_unlock_irqrestore(&amd_lock, flags);
- return amd_chipset.probe_result;
+commit:
+
+ spin_lock_irqsave(&amd_lock, flags);
+ if (amd_chipset.probe_count > 0) {
+ /* race - someone else was faster - drop devices */
+
+ /* Mark that we where here */
+ amd_chipset.probe_count++;
+ ret = amd_chipset.probe_result;
+
+ spin_unlock_irqrestore(&amd_lock, flags);
+
+ if (info.nb_dev)
+ pci_dev_put(info.nb_dev);
+ if (info.smbus_dev)
+ pci_dev_put(info.smbus_dev);
+
+ } else {
+ /* no race - commit the result */
+ info.probe_count++;
+ amd_chipset = info;
+ spin_unlock_irqrestore(&amd_lock, flags);
+ }
+
+ return ret;
}
EXPORT_SYMBOL_GPL(usb_amd_find_chipset_info);
@@ -284,6 +311,7 @@ EXPORT_SYMBOL_GPL(usb_amd_quirk_pll_enable);
void usb_amd_dev_put(void)
{
+ struct pci_dev *nb, *smbus;
unsigned long flags;
spin_lock_irqsave(&amd_lock, flags);
@@ -294,20 +322,23 @@ void usb_amd_dev_put(void)
return;
}
- if (amd_chipset.nb_dev) {
- pci_dev_put(amd_chipset.nb_dev);
- amd_chipset.nb_dev = NULL;
- }
- if (amd_chipset.smbus_dev) {
- pci_dev_put(amd_chipset.smbus_dev);
- amd_chipset.smbus_dev = NULL;
- }
+ /* save them to pci_dev_put outside of spinlock */
+ nb = amd_chipset.nb_dev;
+ smbus = amd_chipset.smbus_dev;
+
+ amd_chipset.nb_dev = NULL;
+ amd_chipset.smbus_dev = NULL;
amd_chipset.nb_type = 0;
amd_chipset.sb_type = 0;
amd_chipset.isoc_reqs = 0;
amd_chipset.probe_result = 0;
spin_unlock_irqrestore(&amd_lock, flags);
+
+ if (nb)
+ pci_dev_put(nb);
+ if (smbus)
+ pci_dev_put(smbus);
}
EXPORT_SYMBOL_GPL(usb_amd_dev_put);
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index 18b7099a812..fafccc2fd33 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -47,6 +47,7 @@
#include <linux/usb/sl811.h>
#include <linux/usb/hcd.h>
#include <linux/platform_device.h>
+#include <linux/prefetch.h>
#include <asm/io.h>
#include <asm/irq.h>
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index a78f2ebd11b..73f75d26436 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -777,7 +777,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
if (t1 != t2)
xhci_writel(xhci, t2, port_array[port_index]);
- if (DEV_HIGHSPEED(t1)) {
+ if (hcd->speed != HCD_USB3) {
/* enable remote wake up for USB 2.0 */
u32 __iomem *addr;
u32 tmp;
@@ -866,6 +866,21 @@ int xhci_bus_resume(struct usb_hcd *hcd)
temp |= PORT_LINK_STROBE | XDEV_U0;
xhci_writel(xhci, temp, port_array[port_index]);
}
+ /* wait for the port to enter U0 and report port link
+ * state change.
+ */
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ msleep(20);
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ /* Clear PLC */
+ temp = xhci_readl(xhci, port_array[port_index]);
+ if (temp & PORT_PLC) {
+ temp = xhci_port_state_to_neutral(temp);
+ temp |= PORT_PLC;
+ xhci_writel(xhci, temp, port_array[port_index]);
+ }
+
slot_id = xhci_find_slot_id_by_port(hcd,
xhci, port_index + 1);
if (slot_id)
@@ -873,7 +888,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
} else
xhci_writel(xhci, temp, port_array[port_index]);
- if (DEV_HIGHSPEED(temp)) {
+ if (hcd->speed != HCD_USB3) {
/* disable remote wake up for USB 2.0 */
u32 __iomem *addr;
u32 tmp;
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index a003e79aacd..627f3438028 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -846,7 +846,7 @@ static u32 xhci_find_real_port_number(struct xhci_hcd *xhci,
* Skip ports that don't have known speeds, or have duplicate
* Extended Capabilities port speed entries.
*/
- if (port_speed == 0 || port_speed == -1)
+ if (port_speed == 0 || port_speed == DUPLICATE_ENTRY)
continue;
/*
@@ -974,6 +974,47 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
return 0;
}
+/*
+ * Convert interval expressed as 2^(bInterval - 1) == interval into
+ * straight exponent value 2^n == interval.
+ *
+ */
+static unsigned int xhci_parse_exponent_interval(struct usb_device *udev,
+ struct usb_host_endpoint *ep)
+{
+ unsigned int interval;
+
+ interval = clamp_val(ep->desc.bInterval, 1, 16) - 1;
+ if (interval != ep->desc.bInterval - 1)
+ dev_warn(&udev->dev,
+ "ep %#x - rounding interval to %d microframes\n",
+ ep->desc.bEndpointAddress,
+ 1 << interval);
+
+ return interval;
+}
+
+/*
+ * Convert bInterval expressed in frames (in 1-255 range) to exponent of
+ * microframes, rounded down to nearest power of 2.
+ */
+static unsigned int xhci_parse_frame_interval(struct usb_device *udev,
+ struct usb_host_endpoint *ep)
+{
+ unsigned int interval;
+
+ interval = fls(8 * ep->desc.bInterval) - 1;
+ interval = clamp_val(interval, 3, 10);
+ if ((1 << interval) != 8 * ep->desc.bInterval)
+ dev_warn(&udev->dev,
+ "ep %#x - rounding interval to %d microframes, ep desc says %d microframes\n",
+ ep->desc.bEndpointAddress,
+ 1 << interval,
+ 8 * ep->desc.bInterval);
+
+ return interval;
+}
+
/* Return the polling or NAK interval.
*
* The polling interval is expressed in "microframes". If xHCI's Interval field
@@ -982,7 +1023,7 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
* The NAK interval is one NAK per 1 to 255 microframes, or no NAKs if interval
* is set to 0.
*/
-static inline unsigned int xhci_get_endpoint_interval(struct usb_device *udev,
+static unsigned int xhci_get_endpoint_interval(struct usb_device *udev,
struct usb_host_endpoint *ep)
{
unsigned int interval = 0;
@@ -991,45 +1032,38 @@ static inline unsigned int xhci_get_endpoint_interval(struct usb_device *udev,
case USB_SPEED_HIGH:
/* Max NAK rate */
if (usb_endpoint_xfer_control(&ep->desc) ||
- usb_endpoint_xfer_bulk(&ep->desc))
+ usb_endpoint_xfer_bulk(&ep->desc)) {
interval = ep->desc.bInterval;
+ break;
+ }
/* Fall through - SS and HS isoc/int have same decoding */
+
case USB_SPEED_SUPER:
if (usb_endpoint_xfer_int(&ep->desc) ||
- usb_endpoint_xfer_isoc(&ep->desc)) {
- if (ep->desc.bInterval == 0)
- interval = 0;
- else
- interval = ep->desc.bInterval - 1;
- if (interval > 15)
- interval = 15;
- if (interval != ep->desc.bInterval + 1)
- dev_warn(&udev->dev, "ep %#x - rounding interval to %d microframes\n",
- ep->desc.bEndpointAddress, 1 << interval);
+ usb_endpoint_xfer_isoc(&ep->desc)) {
+ interval = xhci_parse_exponent_interval(udev, ep);
}
break;
- /* Convert bInterval (in 1-255 frames) to microframes and round down to
- * nearest power of 2.
- */
+
case USB_SPEED_FULL:
+ if (usb_endpoint_xfer_int(&ep->desc)) {
+ interval = xhci_parse_exponent_interval(udev, ep);
+ break;
+ }
+ /*
+ * Fall through for isochronous endpoint interval decoding
+ * since it uses the same rules as low speed interrupt
+ * endpoints.
+ */
+
case USB_SPEED_LOW:
if (usb_endpoint_xfer_int(&ep->desc) ||
- usb_endpoint_xfer_isoc(&ep->desc)) {
- interval = fls(8*ep->desc.bInterval) - 1;
- if (interval > 10)
- interval = 10;
- if (interval < 3)
- interval = 3;
- if ((1 << interval) != 8*ep->desc.bInterval)
- dev_warn(&udev->dev,
- "ep %#x - rounding interval"
- " to %d microframes, "
- "ep desc says %d microframes\n",
- ep->desc.bEndpointAddress,
- 1 << interval,
- 8*ep->desc.bInterval);
+ usb_endpoint_xfer_isoc(&ep->desc)) {
+
+ interval = xhci_parse_frame_interval(udev, ep);
}
break;
+
default:
BUG();
}
@@ -1041,7 +1075,7 @@ static inline unsigned int xhci_get_endpoint_interval(struct usb_device *udev,
* transaction opportunities per microframe", but that goes in the Max Burst
* endpoint context field.
*/
-static inline u32 xhci_get_endpoint_mult(struct usb_device *udev,
+static u32 xhci_get_endpoint_mult(struct usb_device *udev,
struct usb_host_endpoint *ep)
{
if (udev->speed != USB_SPEED_SUPER ||
@@ -1050,7 +1084,7 @@ static inline u32 xhci_get_endpoint_mult(struct usb_device *udev,
return ep->ss_ep_comp.bmAttributes;
}
-static inline u32 xhci_get_endpoint_type(struct usb_device *udev,
+static u32 xhci_get_endpoint_type(struct usb_device *udev,
struct usb_host_endpoint *ep)
{
int in;
@@ -1084,7 +1118,7 @@ static inline u32 xhci_get_endpoint_type(struct usb_device *udev,
* Basically, this is the maxpacket size, multiplied by the burst size
* and mult size.
*/
-static inline u32 xhci_get_max_esit_payload(struct xhci_hcd *xhci,
+static u32 xhci_get_max_esit_payload(struct xhci_hcd *xhci,
struct usb_device *udev,
struct usb_host_endpoint *ep)
{
@@ -1727,12 +1761,12 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
* found a similar duplicate.
*/
if (xhci->port_array[i] != major_revision &&
- xhci->port_array[i] != (u8) -1) {
+ xhci->port_array[i] != DUPLICATE_ENTRY) {
if (xhci->port_array[i] == 0x03)
xhci->num_usb3_ports--;
else
xhci->num_usb2_ports--;
- xhci->port_array[i] = (u8) -1;
+ xhci->port_array[i] = DUPLICATE_ENTRY;
}
/* FIXME: Should we disable the port? */
continue;
@@ -1831,7 +1865,7 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
for (i = 0; i < num_ports; i++) {
if (xhci->port_array[i] == 0x03 ||
xhci->port_array[i] == 0 ||
- xhci->port_array[i] == -1)
+ xhci->port_array[i] == DUPLICATE_ENTRY)
continue;
xhci->usb2_ports[port_index] =
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index ceea9f33491..a10494c2f3c 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -114,6 +114,10 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
if (pdev->vendor == PCI_VENDOR_ID_NEC)
xhci->quirks |= XHCI_NEC_HOST;
+ /* AMD PLL quirk */
+ if (pdev->vendor == PCI_VENDOR_ID_AMD && usb_amd_find_chipset_info())
+ xhci->quirks |= XHCI_AMD_PLL_FIX;
+
/* Make sure the HC is halted. */
retval = xhci_halt(xhci);
if (retval)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index cfc1ad92473..7437386a9a5 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -93,7 +93,7 @@ dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg,
/* Does this link TRB point to the first segment in a ring,
* or was the previous TRB the last TRB on the last segment in the ERST?
*/
-static inline bool last_trb_on_last_seg(struct xhci_hcd *xhci, struct xhci_ring *ring,
+static bool last_trb_on_last_seg(struct xhci_hcd *xhci, struct xhci_ring *ring,
struct xhci_segment *seg, union xhci_trb *trb)
{
if (ring == xhci->event_ring)
@@ -107,7 +107,7 @@ static inline bool last_trb_on_last_seg(struct xhci_hcd *xhci, struct xhci_ring
* segment? I.e. would the updated event TRB pointer step off the end of the
* event seg?
*/
-static inline int last_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
+static int last_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
struct xhci_segment *seg, union xhci_trb *trb)
{
if (ring == xhci->event_ring)
@@ -116,7 +116,7 @@ static inline int last_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
return (trb->link.control & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK);
}
-static inline int enqueue_is_link_trb(struct xhci_ring *ring)
+static int enqueue_is_link_trb(struct xhci_ring *ring)
{
struct xhci_link_trb *link = &ring->enqueue->link;
return ((link->control & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK));
@@ -592,7 +592,7 @@ void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci,
ep->ep_state |= SET_DEQ_PENDING;
}
-static inline void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci,
+static void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci,
struct xhci_virt_ep *ep)
{
ep->ep_state &= ~EP_HALT_PENDING;
@@ -619,6 +619,13 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
/* Only giveback urb when this is the last td in urb */
if (urb_priv->td_cnt == urb_priv->length) {
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs--;
+ if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) {
+ if (xhci->quirks & XHCI_AMD_PLL_FIX)
+ usb_amd_quirk_pll_enable();
+ }
+ }
usb_hcd_unlink_urb_from_ep(hcd, urb);
xhci_dbg(xhci, "Giveback %s URB %p\n", adjective, urb);
@@ -1209,7 +1216,7 @@ static unsigned int find_faked_portnum_from_hw_portnum(struct usb_hcd *hcd,
* Skip ports that don't have known speeds, or have duplicate
* Extended Capabilities port speed entries.
*/
- if (port_speed == 0 || port_speed == -1)
+ if (port_speed == 0 || port_speed == DUPLICATE_ENTRY)
continue;
/*
@@ -1235,6 +1242,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
u8 major_revision;
struct xhci_bus_state *bus_state;
u32 __iomem **port_array;
+ bool bogus_port_status = false;
/* Port status change events always have a successful completion code */
if (GET_COMP_CODE(event->generic.field[2]) != COMP_SUCCESS) {
@@ -1247,6 +1255,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
max_ports = HCS_MAX_PORTS(xhci->hcs_params1);
if ((port_id <= 0) || (port_id > max_ports)) {
xhci_warn(xhci, "Invalid port id %d\n", port_id);
+ bogus_port_status = true;
goto cleanup;
}
@@ -1258,12 +1267,14 @@ static void handle_port_status(struct xhci_hcd *xhci,
xhci_warn(xhci, "Event for port %u not in "
"Extended Capabilities, ignoring.\n",
port_id);
+ bogus_port_status = true;
goto cleanup;
}
- if (major_revision == (u8) -1) {
+ if (major_revision == DUPLICATE_ENTRY) {
xhci_warn(xhci, "Event for port %u duplicated in"
"Extended Capabilities, ignoring.\n",
port_id);
+ bogus_port_status = true;
goto cleanup;
}
@@ -1335,6 +1346,13 @@ cleanup:
/* Update event ring dequeue pointer before dropping the lock */
inc_deq(xhci, xhci->event_ring, true);
+ /* Don't make the USB core poll the roothub if we got a bad port status
+ * change event. Besides, at that point we can't tell which roothub
+ * (USB 2.0 or USB 3.0) to kick.
+ */
+ if (bogus_port_status)
+ return;
+
spin_unlock(&xhci->lock);
/* Pass this up to the core */
usb_hcd_poll_rh_status(hcd);
@@ -1554,8 +1572,17 @@ td_cleanup:
urb_priv->td_cnt++;
/* Giveback the urb when all the tds are completed */
- if (urb_priv->td_cnt == urb_priv->length)
+ if (urb_priv->td_cnt == urb_priv->length) {
ret = 1;
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs--;
+ if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs
+ == 0) {
+ if (xhci->quirks & XHCI_AMD_PLL_FIX)
+ usb_amd_quirk_pll_enable();
+ }
+ }
+ }
}
return ret;
@@ -1675,71 +1702,52 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
struct urb_priv *urb_priv;
int idx;
int len = 0;
- int skip_td = 0;
union xhci_trb *cur_trb;
struct xhci_segment *cur_seg;
+ struct usb_iso_packet_descriptor *frame;
u32 trb_comp_code;
+ bool skip_td = false;
ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
trb_comp_code = GET_COMP_CODE(event->transfer_len);
urb_priv = td->urb->hcpriv;
idx = urb_priv->td_cnt;
+ frame = &td->urb->iso_frame_desc[idx];
- if (ep->skip) {
- /* The transfer is partly done */
- *status = -EXDEV;
- td->urb->iso_frame_desc[idx].status = -EXDEV;
- } else {
- /* handle completion code */
- switch (trb_comp_code) {
- case COMP_SUCCESS:
- td->urb->iso_frame_desc[idx].status = 0;
- xhci_dbg(xhci, "Successful isoc transfer!\n");
- break;
- case COMP_SHORT_TX:
- if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
- td->urb->iso_frame_desc[idx].status =
- -EREMOTEIO;
- else
- td->urb->iso_frame_desc[idx].status = 0;
- break;
- case COMP_BW_OVER:
- td->urb->iso_frame_desc[idx].status = -ECOMM;
- skip_td = 1;
- break;
- case COMP_BUFF_OVER:
- case COMP_BABBLE:
- td->urb->iso_frame_desc[idx].status = -EOVERFLOW;
- skip_td = 1;
- break;
- case COMP_STALL:
- td->urb->iso_frame_desc[idx].status = -EPROTO;
- skip_td = 1;
- break;
- case COMP_STOP:
- case COMP_STOP_INVAL:
- break;
- default:
- td->urb->iso_frame_desc[idx].status = -1;
- break;
- }
- }
-
- /* calc actual length */
- if (ep->skip) {
- td->urb->iso_frame_desc[idx].actual_length = 0;
- /* Update ring dequeue pointer */
- while (ep_ring->dequeue != td->last_trb)
- inc_deq(xhci, ep_ring, false);
- inc_deq(xhci, ep_ring, false);
- return finish_td(xhci, td, event_trb, event, ep, status, true);
+ /* handle completion code */
+ switch (trb_comp_code) {
+ case COMP_SUCCESS:
+ frame->status = 0;
+ xhci_dbg(xhci, "Successful isoc transfer!\n");
+ break;
+ case COMP_SHORT_TX:
+ frame->status = td->urb->transfer_flags & URB_SHORT_NOT_OK ?
+ -EREMOTEIO : 0;
+ break;
+ case COMP_BW_OVER:
+ frame->status = -ECOMM;
+ skip_td = true;
+ break;
+ case COMP_BUFF_OVER:
+ case COMP_BABBLE:
+ frame->status = -EOVERFLOW;
+ skip_td = true;
+ break;
+ case COMP_STALL:
+ frame->status = -EPROTO;
+ skip_td = true;
+ break;
+ case COMP_STOP:
+ case COMP_STOP_INVAL:
+ break;
+ default:
+ frame->status = -1;
+ break;
}
- if (trb_comp_code == COMP_SUCCESS || skip_td == 1) {
- td->urb->iso_frame_desc[idx].actual_length =
- td->urb->iso_frame_desc[idx].length;
- td->urb->actual_length +=
- td->urb->iso_frame_desc[idx].length;
+ if (trb_comp_code == COMP_SUCCESS || skip_td) {
+ frame->actual_length = frame->length;
+ td->urb->actual_length += frame->length;
} else {
for (cur_trb = ep_ring->dequeue,
cur_seg = ep_ring->deq_seg; cur_trb != event_trb;
@@ -1755,7 +1763,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
TRB_LEN(event->transfer_len);
if (trb_comp_code != COMP_STOP_INVAL) {
- td->urb->iso_frame_desc[idx].actual_length = len;
+ frame->actual_length = len;
td->urb->actual_length += len;
}
}
@@ -1766,6 +1774,35 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
return finish_td(xhci, td, event_trb, event, ep, status, false);
}
+static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
+ struct xhci_transfer_event *event,
+ struct xhci_virt_ep *ep, int *status)
+{
+ struct xhci_ring *ep_ring;
+ struct urb_priv *urb_priv;
+ struct usb_iso_packet_descriptor *frame;
+ int idx;
+
+ ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
+ urb_priv = td->urb->hcpriv;
+ idx = urb_priv->td_cnt;
+ frame = &td->urb->iso_frame_desc[idx];
+
+ /* The transfer is partly done */
+ *status = -EXDEV;
+ frame->status = -EXDEV;
+
+ /* calc actual length */
+ frame->actual_length = 0;
+
+ /* Update ring dequeue pointer */
+ while (ep_ring->dequeue != td->last_trb)
+ inc_deq(xhci, ep_ring, false);
+ inc_deq(xhci, ep_ring, false);
+
+ return finish_td(xhci, td, NULL, event, ep, status, true);
+}
+
/*
* Process bulk and interrupt tds, update urb status and actual_length.
*/
@@ -2024,36 +2061,42 @@ static int handle_tx_event(struct xhci_hcd *xhci,
}
td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list);
+
/* Is this a TRB in the currently executing TD? */
event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue,
td->last_trb, event_dma);
- if (event_seg && ep->skip) {
+ if (!event_seg) {
+ if (!ep->skip ||
+ !usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
+ /* HC is busted, give up! */
+ xhci_err(xhci,
+ "ERROR Transfer event TRB DMA ptr not "
+ "part of current TD\n");
+ return -ESHUTDOWN;
+ }
+
+ ret = skip_isoc_td(xhci, td, event, ep, &status);
+ goto cleanup;
+ }
+
+ if (ep->skip) {
xhci_dbg(xhci, "Found td. Clear skip flag.\n");
ep->skip = false;
}
- if (!event_seg &&
- (!ep->skip || !usb_endpoint_xfer_isoc(&td->urb->ep->desc))) {
- /* HC is busted, give up! */
- xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not "
- "part of current TD\n");
- return -ESHUTDOWN;
- }
- if (event_seg) {
- event_trb = &event_seg->trbs[(event_dma -
- event_seg->dma) / sizeof(*event_trb)];
- /*
- * No-op TRB should not trigger interrupts.
- * If event_trb is a no-op TRB, it means the
- * corresponding TD has been cancelled. Just ignore
- * the TD.
- */
- if ((event_trb->generic.field[3] & TRB_TYPE_BITMASK)
- == TRB_TYPE(TRB_TR_NOOP)) {
- xhci_dbg(xhci, "event_trb is a no-op TRB. "
- "Skip it\n");
- goto cleanup;
- }
+ event_trb = &event_seg->trbs[(event_dma - event_seg->dma) /
+ sizeof(*event_trb)];
+ /*
+ * No-op TRB should not trigger interrupts.
+ * If event_trb is a no-op TRB, it means the
+ * corresponding TD has been cancelled. Just ignore
+ * the TD.
+ */
+ if ((event_trb->generic.field[3] & TRB_TYPE_BITMASK)
+ == TRB_TYPE(TRB_TR_NOOP)) {
+ xhci_dbg(xhci,
+ "event_trb is a no-op TRB. Skip it\n");
+ goto cleanup;
}
/* Now update the urb's actual_length and give back to
@@ -3126,6 +3169,12 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
}
}
+ if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) {
+ if (xhci->quirks & XHCI_AMD_PLL_FIX)
+ usb_amd_quirk_pll_disable();
+ }
+ xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs++;
+
giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,
start_cycle, start_trb);
return 0;
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 196e0181b2e..81b976e4588 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -550,6 +550,9 @@ void xhci_stop(struct usb_hcd *hcd)
del_timer_sync(&xhci->event_ring_timer);
#endif
+ if (xhci->quirks & XHCI_AMD_PLL_FIX)
+ usb_amd_dev_put();
+
xhci_dbg(xhci, "// Disabling event ring interrupts\n");
temp = xhci_readl(xhci, &xhci->op_regs->status);
xhci_writel(xhci, temp & ~STS_EINT, &xhci->op_regs->status);
@@ -771,7 +774,9 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
/* If restore operation fails, re-initialize the HC during resume */
if ((temp & STS_SRE) || hibernated) {
- usb_root_hub_lost_power(hcd->self.root_hub);
+ /* Let the USB core know _both_ roothubs lost power. */
+ usb_root_hub_lost_power(xhci->main_hcd->self.root_hub);
+ usb_root_hub_lost_power(xhci->shared_hcd->self.root_hub);
xhci_dbg(xhci, "Stop HCD\n");
xhci_halt(xhci);
@@ -2386,10 +2391,18 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
/* Everything but endpoint 0 is disabled, so free or cache the rings. */
last_freed_endpoint = 1;
for (i = 1; i < 31; ++i) {
- if (!virt_dev->eps[i].ring)
- continue;
- xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
- last_freed_endpoint = i;
+ struct xhci_virt_ep *ep = &virt_dev->eps[i];
+
+ if (ep->ep_state & EP_HAS_STREAMS) {
+ xhci_free_stream_info(xhci, ep->stream_info);
+ ep->stream_info = NULL;
+ ep->ep_state &= ~EP_HAS_STREAMS;
+ }
+
+ if (ep->ring) {
+ xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
+ last_freed_endpoint = i;
+ }
}
xhci_dbg(xhci, "Output context after successful reset device cmd:\n");
xhci_dbg_ctx(xhci, virt_dev->out_ctx, last_freed_endpoint);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 07e263063e3..ba1be6b7cc6 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -30,6 +30,7 @@
/* Code sharing between pci-quirks and xhci hcd */
#include "xhci-ext-caps.h"
+#include "pci-quirks.h"
/* xHCI PCI Configuration Registers */
#define XHCI_SBRN_OFFSET (0x60)
@@ -232,7 +233,7 @@ struct xhci_op_regs {
* notification type that matches a bit set in this bit field.
*/
#define DEV_NOTE_MASK (0xffff)
-#define ENABLE_DEV_NOTE(x) (1 << x)
+#define ENABLE_DEV_NOTE(x) (1 << (x))
/* Most of the device notification types should only be used for debug.
* SW does need to pay attention to function wake notifications.
*/
@@ -348,6 +349,9 @@ struct xhci_op_regs {
/* Initiate a warm port reset - complete when PORT_WRC is '1' */
#define PORT_WR (1 << 31)
+/* We mark duplicate entries with -1 */
+#define DUPLICATE_ENTRY ((u8)(-1))
+
/* Port Power Management Status and Control - port_power_base bitmasks */
/* Inactivity timer value for transitions into U1, in microseconds.
* Timeout can be up to 127us. 0xFF means an infinite timeout.
@@ -601,11 +605,11 @@ struct xhci_ep_ctx {
#define EP_STATE_STOPPED 3
#define EP_STATE_ERROR 4
/* Mult - Max number of burtst within an interval, in EP companion desc. */
-#define EP_MULT(p) ((p & 0x3) << 8)
+#define EP_MULT(p) (((p) & 0x3) << 8)
/* bits 10:14 are Max Primary Streams */
/* bit 15 is Linear Stream Array */
/* Interval - period between requests to an endpoint - 125u increments. */
-#define EP_INTERVAL(p) ((p & 0xff) << 16)
+#define EP_INTERVAL(p) (((p) & 0xff) << 16)
#define EP_INTERVAL_TO_UFRAMES(p) (1 << (((p) >> 16) & 0xff))
#define EP_MAXPSTREAMS_MASK (0x1f << 10)
#define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK)
@@ -1276,6 +1280,7 @@ struct xhci_hcd {
#define XHCI_LINK_TRB_QUIRK (1 << 0)
#define XHCI_RESET_EP_QUIRK (1 << 1)
#define XHCI_NEC_HOST (1 << 2)
+#define XHCI_AMD_PLL_FIX (1 << 3)
/* There are two roothubs to keep track of bus suspend info for */
struct xhci_bus_state bus_state[2];
/* Is each xHCI roothub port a USB 3.0, USB 2.0, or USB 1.1 port? */
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index 4cbb7e4b368..74073b363c3 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -14,7 +14,7 @@ config USB_MUSB_HDRC
select TWL4030_USB if MACH_OMAP_3430SDP
select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA
select USB_OTG_UTILS
- tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
+ bool 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
help
Say Y here if your system has a dual role high speed USB
controller based on the Mentor Graphics silicon IP. Then
@@ -30,8 +30,8 @@ config USB_MUSB_HDRC
If you do not know what this is, please say N.
- To compile this driver as a module, choose M here; the
- module will be called "musb-hdrc".
+# To compile this driver as a module, choose M here; the
+# module will be called "musb-hdrc".
choice
prompt "Platform Glue Layer"
diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c
index 52312e8af21..8e2a1ff8a35 100644
--- a/drivers/usb/musb/blackfin.c
+++ b/drivers/usb/musb/blackfin.c
@@ -21,6 +21,7 @@
#include <asm/cacheflush.h>
#include "musb_core.h"
+#include "musbhsdma.h"
#include "blackfin.h"
struct bfin_glue {
@@ -332,6 +333,27 @@ static int bfin_musb_set_mode(struct musb *musb, u8 musb_mode)
return -EIO;
}
+static int bfin_musb_adjust_channel_params(struct dma_channel *channel,
+ u16 packet_sz, u8 *mode,
+ dma_addr_t *dma_addr, u32 *len)
+{
+ struct musb_dma_channel *musb_channel = channel->private_data;
+
+ /*
+ * Anomaly 05000450 might cause data corruption when using DMA
+ * MODE 1 transmits with short packet. So to work around this,
+ * we truncate all MODE 1 transfers down to a multiple of the
+ * max packet size, and then do the last short packet transfer
+ * (if there is any) using MODE 0.
+ */
+ if (ANOMALY_05000450) {
+ if (musb_channel->transmit && *mode == 1)
+ *len = *len - (*len % packet_sz);
+ }
+
+ return 0;
+}
+
static void bfin_musb_reg_init(struct musb *musb)
{
if (ANOMALY_05000346) {
@@ -430,6 +452,8 @@ static const struct musb_platform_ops bfin_ops = {
.vbus_status = bfin_musb_vbus_status,
.set_vbus = bfin_musb_set_vbus,
+
+ .adjust_channel_params = bfin_musb_adjust_channel_params,
};
static u64 bfin_dmamask = DMA_BIT_MASK(32);
diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c
index de55a3c3259..ab434fbd8c3 100644
--- a/drivers/usb/musb/cppi_dma.c
+++ b/drivers/usb/musb/cppi_dma.c
@@ -597,12 +597,12 @@ cppi_next_tx_segment(struct musb *musb, struct cppi_channel *tx)
length = min(n_bds * maxpacket, length);
}
- DBG(4, "TX DMA%d, pktSz %d %s bds %d dma 0x%x len %u\n",
+ DBG(4, "TX DMA%d, pktSz %d %s bds %d dma 0x%llx len %u\n",
tx->index,
maxpacket,
rndis ? "rndis" : "transparent",
n_bds,
- addr, length);
+ (unsigned long long)addr, length);
cppi_rndis_update(tx, 0, musb->ctrl_base, rndis);
@@ -820,7 +820,7 @@ cppi_next_rx_segment(struct musb *musb, struct cppi_channel *rx, int onepacket)
length = min(n_bds * maxpacket, length);
DBG(4, "RX DMA%d seg, maxp %d %s bds %d (cnt %d) "
- "dma 0x%x len %u %u/%u\n",
+ "dma 0x%llx len %u %u/%u\n",
rx->index, maxpacket,
onepacket
? (is_rndis ? "rndis" : "onepacket")
@@ -829,7 +829,8 @@ cppi_next_rx_segment(struct musb *musb, struct cppi_channel *rx, int onepacket)
musb_readl(tibase,
DAVINCI_RXCPPI_BUFCNT0_REG + (rx->index * 4))
& 0xffff,
- addr, length, rx->channel.actual_len, rx->buf_len);
+ (unsigned long long)addr, length,
+ rx->channel.actual_len, rx->buf_len);
/* only queue one segment at a time, since the hardware prevents
* correct queue shutdown after unexpected short packets
@@ -1039,9 +1040,9 @@ static bool cppi_rx_scan(struct cppi *cppi, unsigned ch)
if (!completed && (bd->hw_options & CPPI_OWN_SET))
break;
- DBG(5, "C/RXBD %08x: nxt %08x buf %08x "
+ DBG(5, "C/RXBD %llx: nxt %08x buf %08x "
"off.len %08x opt.len %08x (%d)\n",
- bd->dma, bd->hw_next, bd->hw_bufp,
+ (unsigned long long)bd->dma, bd->hw_next, bd->hw_bufp,
bd->hw_off_len, bd->hw_options,
rx->channel.actual_len);
@@ -1111,11 +1112,12 @@ static bool cppi_rx_scan(struct cppi *cppi, unsigned ch)
musb_ep_select(cppi->mregs, rx->index + 1);
csr = musb_readw(regs, MUSB_RXCSR);
if (csr & MUSB_RXCSR_DMAENAB) {
- DBG(4, "list%d %p/%p, last %08x%s, csr %04x\n",
+ DBG(4, "list%d %p/%p, last %llx%s, csr %04x\n",
rx->index,
rx->head, rx->tail,
rx->last_processed
- ? rx->last_processed->dma
+ ? (unsigned long long)
+ rx->last_processed->dma
: 0,
completed ? ", completed" : "",
csr);
@@ -1167,8 +1169,11 @@ irqreturn_t cppi_interrupt(int irq, void *dev_id)
tx = musb_readl(tibase, DAVINCI_TXCPPI_MASKED_REG);
rx = musb_readl(tibase, DAVINCI_RXCPPI_MASKED_REG);
- if (!tx && !rx)
+ if (!tx && !rx) {
+ if (cppi->irq)
+ spin_unlock_irqrestore(&musb->lock, flags);
return IRQ_NONE;
+ }
DBG(4, "CPPI IRQ Tx%x Rx%x\n", tx, rx);
@@ -1199,7 +1204,7 @@ irqreturn_t cppi_interrupt(int irq, void *dev_id)
*/
if (NULL == bd) {
DBG(1, "null BD\n");
- tx_ram->tx_complete = 0;
+ musb_writel(&tx_ram->tx_complete, 0, 0);
continue;
}
@@ -1452,7 +1457,7 @@ static int cppi_channel_abort(struct dma_channel *channel)
* compare mode by writing 1 to the tx_complete register.
*/
cppi_reset_tx(tx_ram, 1);
- cppi_ch->head = 0;
+ cppi_ch->head = NULL;
musb_writel(&tx_ram->tx_complete, 0, 1);
cppi_dump_tx(5, cppi_ch, " (done teardown)");
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 630ae7f3cd4..f10ff00ca09 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -1030,6 +1030,7 @@ static void musb_shutdown(struct platform_device *pdev)
struct musb *musb = dev_to_musb(&pdev->dev);
unsigned long flags;
+ pm_runtime_get_sync(musb->controller);
spin_lock_irqsave(&musb->lock, flags);
musb_platform_disable(musb);
musb_generic_disable(musb);
@@ -1040,6 +1041,7 @@ static void musb_shutdown(struct platform_device *pdev)
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
musb_platform_exit(musb);
+ pm_runtime_put(musb->controller);
/* FIXME power down */
}
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 4bd9e2145ee..0e053b58796 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -261,6 +261,7 @@ enum musb_g_ep0_state {
* @try_ilde: tries to idle the IP
* @vbus_status: returns vbus status if possible
* @set_vbus: forces vbus status
+ * @channel_program: pre check for standard dma channel_program func
*/
struct musb_platform_ops {
int (*init)(struct musb *musb);
@@ -274,6 +275,10 @@ struct musb_platform_ops {
int (*vbus_status)(struct musb *musb);
void (*set_vbus)(struct musb *musb, int on);
+
+ int (*adjust_channel_params)(struct dma_channel *channel,
+ u16 packet_sz, u8 *mode,
+ dma_addr_t *dma_addr, u32 *len);
};
/*
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 98519c5d8b5..f47c20197c6 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -535,7 +535,7 @@ void musb_g_tx(struct musb *musb, u8 epnum)
is_dma = 1;
csr |= MUSB_TXCSR_P_WZC_BITS;
csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_P_UNDERRUN |
- MUSB_TXCSR_TXPKTRDY);
+ MUSB_TXCSR_TXPKTRDY | MUSB_TXCSR_AUTOSET);
musb_writew(epio, MUSB_TXCSR, csr);
/* Ensure writebuffer is empty. */
csr = musb_readw(epio, MUSB_TXCSR);
@@ -1296,7 +1296,7 @@ static int musb_gadget_dequeue(struct usb_ep *ep, struct usb_request *request)
}
/* if the hardware doesn't have the request, easy ... */
- if (musb_ep->req_list.next != &request->list || musb_ep->busy)
+ if (musb_ep->req_list.next != &req->list || musb_ep->busy)
musb_g_giveback(musb_ep, request, -ECONNRESET);
/* ... else abort the dma transfer ... */
@@ -1887,11 +1887,9 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
otg_set_vbus(musb->xceiv, 1);
hcd->self.uses_pio_for_control = 1;
-
- if (musb->xceiv->last_event == USB_EVENT_NONE)
- pm_runtime_put(musb->controller);
-
}
+ if (musb->xceiv->last_event == USB_EVENT_NONE)
+ pm_runtime_put(musb->controller);
return 0;
diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c
index 0144a2d481f..d281792db05 100644
--- a/drivers/usb/musb/musbhsdma.c
+++ b/drivers/usb/musb/musbhsdma.c
@@ -169,6 +169,14 @@ static int dma_channel_program(struct dma_channel *channel,
BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN ||
channel->status == MUSB_DMA_STATUS_BUSY);
+ /* Let targets check/tweak the arguments */
+ if (musb->ops->adjust_channel_params) {
+ int ret = musb->ops->adjust_channel_params(channel,
+ packet_sz, &mode, &dma_addr, &len);
+ if (ret)
+ return ret;
+ }
+
/*
* The DMA engine in RTL1.8 and above cannot handle
* DMA addresses that are not aligned to a 4 byte boundary.
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index 25cb8b0003b..e9e60b6e058 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -259,9 +259,10 @@ static int musb_otg_notifications(struct notifier_block *nb,
case USB_EVENT_VBUS:
DBG(4, "VBUS Connect\n");
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
if (musb->gadget_driver)
pm_runtime_get_sync(musb->controller);
-
+#endif
otg_init(musb->xceiv);
break;
@@ -269,7 +270,7 @@ static int musb_otg_notifications(struct notifier_block *nb,
DBG(4, "VBUS Disconnect\n");
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
- if (is_otg_enabled(musb))
+ if (is_otg_enabled(musb) || is_peripheral_enabled(musb))
if (musb->gadget_driver)
#endif
{
diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c
index d6384e4aeef..f7e04bf34a1 100644
--- a/drivers/usb/musb/ux500.c
+++ b/drivers/usb/musb/ux500.c
@@ -93,6 +93,8 @@ static int __init ux500_probe(struct platform_device *pdev)
}
musb->dev.parent = &pdev->dev;
+ musb->dev.dma_mask = pdev->dev.dma_mask;
+ musb->dev.coherent_dma_mask = pdev->dev.coherent_dma_mask;
glue->dev = &pdev->dev;
glue->musb = musb;
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index a973c7a29d6..4de6ef0ae52 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -151,6 +151,8 @@ static struct ftdi_sio_quirk ftdi_stmclite_quirk = {
* /sys/bus/usb/ftdi_sio/new_id, then send patch/report!
*/
static struct usb_device_id id_table_combined [] = {
+ { USB_DEVICE(FTDI_VID, FTDI_CTI_MINI_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_CTI_NANO_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_AMC232_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CANUSB_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CANDAPTER_PID) },
@@ -525,6 +527,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_8_PID) },
{ USB_DEVICE(IDTECH_VID, IDTECH_IDT1221U_PID) },
{ USB_DEVICE(OCT_VID, OCT_US101_PID) },
+ { USB_DEVICE(OCT_VID, OCT_DK201_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_HE_TIRA1_PID),
.driver_info = (kernel_ulong_t)&ftdi_HE_TIRA1_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID),
@@ -787,6 +790,8 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(FTDI_VID, HAMEG_HO820_PID) },
+ { USB_DEVICE(FTDI_VID, HAMEG_HO720_PID) },
+ { USB_DEVICE(FTDI_VID, HAMEG_HO730_PID) },
{ USB_DEVICE(FTDI_VID, HAMEG_HO870_PID) },
{ USB_DEVICE(FTDI_VID, MJSG_GENERIC_PID) },
{ USB_DEVICE(FTDI_VID, MJSG_SR_RADIO_PID) },
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index c543e55bafb..efffc23723b 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -300,6 +300,8 @@
* Hameg HO820 and HO870 interface (using VID 0x0403)
*/
#define HAMEG_HO820_PID 0xed74
+#define HAMEG_HO730_PID 0xed73
+#define HAMEG_HO720_PID 0xed72
#define HAMEG_HO870_PID 0xed71
/*
@@ -572,6 +574,7 @@
/* Note: OCT US101 is also rebadged as Dick Smith Electronics (NZ) XH6381 */
/* Also rebadged as Dick Smith Electronics (Aus) XH6451 */
/* Also rebadged as SIIG Inc. model US2308 hardware version 1 */
+#define OCT_DK201_PID 0x0103 /* OCT DK201 USB docking station */
#define OCT_US101_PID 0x0421 /* OCT US101 USB to RS-232 */
/*
@@ -1141,3 +1144,12 @@
#define QIHARDWARE_VID 0x20B7
#define MILKYMISTONE_JTAGSERIAL_PID 0x0713
+/*
+ * CTI GmbH RS485 Converter http://www.cti-lean.com/
+ */
+/* USB-485-Mini*/
+#define FTDI_CTI_MINI_PID 0xF608
+/* USB-Nano-485*/
+#define FTDI_CTI_NANO_PID 0xF60B
+
+
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 75c7f456eed..d77ff043589 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -407,6 +407,10 @@ static void option_instat_callback(struct urb *urb);
/* ONDA MT825UP HSDPA 14.2 modem */
#define ONDA_MT825UP 0x000b
+/* Samsung products */
+#define SAMSUNG_VENDOR_ID 0x04e8
+#define SAMSUNG_PRODUCT_GT_B3730 0x6889
+
/* some devices interfaces need special handling due to a number of reasons */
enum option_blacklist_reason {
OPTION_BLACKLIST_NONE = 0,
@@ -968,6 +972,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD100) },
{ USB_DEVICE(CELOT_VENDOR_ID, CELOT_PRODUCT_CT680M) }, /* CT-650 CDMA 450 1xEVDO modem */
{ USB_DEVICE(ONDA_VENDOR_ID, ONDA_MT825UP) }, /* ONDA MT825UP modem */
+ { USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_GT_B3730, USB_CLASS_CDC_DATA, 0x00, 0x00) }, /* Samsung GT-B3730/GT-B3710 LTE USB modem.*/
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, option_ids);
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 8858201eb1d..54a9dab1f33 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -111,7 +111,7 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
ifnum = intf->desc.bInterfaceNumber;
dbg("This Interface = %d", ifnum);
- data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private),
+ data = kzalloc(sizeof(struct usb_wwan_intf_private),
GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -134,8 +134,10 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) {
dbg("QDL port found");
- if (serial->interface->num_altsetting == 1)
- return 0;
+ if (serial->interface->num_altsetting == 1) {
+ retval = 0; /* Success */
+ break;
+ }
retval = usb_set_interface(serial->dev, ifnum, 1);
if (retval < 0) {
@@ -145,7 +147,6 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
retval = -ENODEV;
kfree(data);
}
- return retval;
}
break;
@@ -166,6 +167,7 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
"Could not set interface, error %d\n",
retval);
retval = -ENODEV;
+ kfree(data);
}
} else if (ifnum == 2) {
dbg("Modem port found");
@@ -177,7 +179,6 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
retval = -ENODEV;
kfree(data);
}
- return retval;
} else if (ifnum==3) {
/*
* NMEA (serial line 9600 8N1)
@@ -191,6 +192,7 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
"Could not set interface, error %d\n",
retval);
retval = -ENODEV;
+ kfree(data);
}
}
break;
@@ -199,12 +201,27 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
dev_err(&serial->dev->dev,
"unknown number of interfaces: %d\n", nintf);
kfree(data);
- return -ENODEV;
+ retval = -ENODEV;
}
+ /* Set serial->private if not returning -ENODEV */
+ if (retval != -ENODEV)
+ usb_set_serial_data(serial, data);
return retval;
}
+static void qc_release(struct usb_serial *serial)
+{
+ struct usb_wwan_intf_private *priv = usb_get_serial_data(serial);
+
+ dbg("%s", __func__);
+
+ /* Call usb_wwan release & free the private data allocated in qcprobe */
+ usb_wwan_release(serial);
+ usb_set_serial_data(serial, NULL);
+ kfree(priv);
+}
+
static struct usb_serial_driver qcdevice = {
.driver = {
.owner = THIS_MODULE,
@@ -222,7 +239,7 @@ static struct usb_serial_driver qcdevice = {
.chars_in_buffer = usb_wwan_chars_in_buffer,
.attach = usb_wwan_startup,
.disconnect = usb_wwan_disconnect,
- .release = usb_wwan_release,
+ .release = qc_release,
#ifdef CONFIG_PM
.suspend = usb_wwan_suspend,
.resume = usb_wwan_resume,
diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c
index 09e52ba47dd..ffc4193e950 100644
--- a/drivers/usb/storage/isd200.c
+++ b/drivers/usb/storage/isd200.c
@@ -499,7 +499,6 @@ static int isd200_action( struct us_data *us, int action,
memset(&ata, 0, sizeof(ata));
srb->cmnd = info->cmnd;
srb->device = &srb_dev;
- ++srb->serial_number;
ata.generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
ata.generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 2ab29124163..7aa4eea930f 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -4,7 +4,7 @@
* Author: Michael S. Tsirkin <mst@redhat.com>
*
* Inspiration, some code, and most witty comments come from
- * Documentation/lguest/lguest.c, by Rusty Russell
+ * Documentation/virtual/lguest/lguest.c, by Rusty Russell
*
* This work is licensed under the terms of the GNU GPL, version 2.
*
diff --git a/drivers/video/acornfb.c b/drivers/video/acornfb.c
index 82acb8dc4aa..6183a57eb69 100644
--- a/drivers/video/acornfb.c
+++ b/drivers/video/acornfb.c
@@ -66,7 +66,7 @@
* have. Allow 1% either way on the nominal for TVs.
*/
#define NR_MONTYPES 6
-static struct fb_monspecs monspecs[NR_MONTYPES] __initdata = {
+static struct fb_monspecs monspecs[NR_MONTYPES] __devinitdata = {
{ /* TV */
.hfmin = 15469,
.hfmax = 15781,
@@ -873,7 +873,7 @@ static struct fb_ops acornfb_ops = {
/*
* Everything after here is initialisation!!!
*/
-static struct fb_videomode modedb[] __initdata = {
+static struct fb_videomode modedb[] __devinitdata = {
{ /* 320x256 @ 50Hz */
NULL, 50, 320, 256, 125000, 92, 62, 35, 19, 38, 2,
FB_SYNC_COMP_HIGH_ACT,
@@ -925,8 +925,7 @@ static struct fb_videomode modedb[] __initdata = {
}
};
-static struct fb_videomode __initdata
-acornfb_default_mode = {
+static struct fb_videomode acornfb_default_mode __devinitdata = {
.name = NULL,
.refresh = 60,
.xres = 640,
@@ -942,7 +941,7 @@ acornfb_default_mode = {
.vmode = FB_VMODE_NONINTERLACED
};
-static void __init acornfb_init_fbinfo(void)
+static void __devinit acornfb_init_fbinfo(void)
{
static int first = 1;
@@ -1018,8 +1017,7 @@ static void __init acornfb_init_fbinfo(void)
* size can optionally be followed by 'M' or 'K' for
* MB or KB respectively.
*/
-static void __init
-acornfb_parse_mon(char *opt)
+static void __devinit acornfb_parse_mon(char *opt)
{
char *p = opt;
@@ -1066,8 +1064,7 @@ bad:
current_par.montype = -1;
}
-static void __init
-acornfb_parse_montype(char *opt)
+static void __devinit acornfb_parse_montype(char *opt)
{
current_par.montype = -2;
@@ -1108,8 +1105,7 @@ acornfb_parse_montype(char *opt)
}
}
-static void __init
-acornfb_parse_dram(char *opt)
+static void __devinit acornfb_parse_dram(char *opt)
{
unsigned int size;
@@ -1134,15 +1130,14 @@ acornfb_parse_dram(char *opt)
static struct options {
char *name;
void (*parse)(char *opt);
-} opt_table[] __initdata = {
+} opt_table[] __devinitdata = {
{ "mon", acornfb_parse_mon },
{ "montype", acornfb_parse_montype },
{ "dram", acornfb_parse_dram },
{ NULL, NULL }
};
-int __init
-acornfb_setup(char *options)
+static int __devinit acornfb_setup(char *options)
{
struct options *optp;
char *opt;
@@ -1179,8 +1174,7 @@ acornfb_setup(char *options)
* Detect type of monitor connected
* For now, we just assume SVGA
*/
-static int __init
-acornfb_detect_monitortype(void)
+static int __devinit acornfb_detect_monitortype(void)
{
return 4;
}
diff --git a/drivers/video/atafb.c b/drivers/video/atafb.c
index 5b2b5ef4edb..64e41f5448c 100644
--- a/drivers/video/atafb.c
+++ b/drivers/video/atafb.c
@@ -3117,7 +3117,7 @@ int __init atafb_init(void)
atafb_ops.fb_setcolreg = &falcon_setcolreg;
error = request_irq(IRQ_AUTO_4, falcon_vbl_switcher,
IRQ_TYPE_PRIO,
- "framebuffer/modeswitch",
+ "framebuffer:modeswitch",
falcon_vbl_switcher);
if (error)
return error;
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index e0c2284924b..5aac00eb183 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -42,9 +42,34 @@
#define FBPIXMAPSIZE (1024 * 8)
+static DEFINE_MUTEX(registration_lock);
struct fb_info *registered_fb[FB_MAX] __read_mostly;
int num_registered_fb __read_mostly;
+static struct fb_info *get_fb_info(unsigned int idx)
+{
+ struct fb_info *fb_info;
+
+ if (idx >= FB_MAX)
+ return ERR_PTR(-ENODEV);
+
+ mutex_lock(&registration_lock);
+ fb_info = registered_fb[idx];
+ if (fb_info)
+ atomic_inc(&fb_info->count);
+ mutex_unlock(&registration_lock);
+
+ return fb_info;
+}
+
+static void put_fb_info(struct fb_info *fb_info)
+{
+ if (!atomic_dec_and_test(&fb_info->count))
+ return;
+ if (fb_info->fbops->fb_destroy)
+ fb_info->fbops->fb_destroy(fb_info);
+}
+
int lock_fb_info(struct fb_info *info)
{
mutex_lock(&info->lock);
@@ -647,6 +672,7 @@ int fb_show_logo(struct fb_info *info, int rotate) { return 0; }
static void *fb_seq_start(struct seq_file *m, loff_t *pos)
{
+ mutex_lock(&registration_lock);
return (*pos < FB_MAX) ? pos : NULL;
}
@@ -658,6 +684,7 @@ static void *fb_seq_next(struct seq_file *m, void *v, loff_t *pos)
static void fb_seq_stop(struct seq_file *m, void *v)
{
+ mutex_unlock(&registration_lock);
}
static int fb_seq_show(struct seq_file *m, void *v)
@@ -690,13 +717,30 @@ static const struct file_operations fb_proc_fops = {
.release = seq_release,
};
-static ssize_t
-fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+/*
+ * We hold a reference to the fb_info in file->private_data,
+ * but if the current registered fb has changed, we don't
+ * actually want to use it.
+ *
+ * So look up the fb_info using the inode minor number,
+ * and just verify it against the reference we have.
+ */
+static struct fb_info *file_fb_info(struct file *file)
{
- unsigned long p = *ppos;
struct inode *inode = file->f_path.dentry->d_inode;
int fbidx = iminor(inode);
struct fb_info *info = registered_fb[fbidx];
+
+ if (info != file->private_data)
+ info = NULL;
+ return info;
+}
+
+static ssize_t
+fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ unsigned long p = *ppos;
+ struct fb_info *info = file_fb_info(file);
u8 *buffer, *dst;
u8 __iomem *src;
int c, cnt = 0, err = 0;
@@ -761,9 +805,7 @@ static ssize_t
fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
unsigned long p = *ppos;
- struct inode *inode = file->f_path.dentry->d_inode;
- int fbidx = iminor(inode);
- struct fb_info *info = registered_fb[fbidx];
+ struct fb_info *info = file_fb_info(file);
u8 *buffer, *src;
u8 __iomem *dst;
int c, cnt = 0, err = 0;
@@ -1141,10 +1183,10 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- struct inode *inode = file->f_path.dentry->d_inode;
- int fbidx = iminor(inode);
- struct fb_info *info = registered_fb[fbidx];
+ struct fb_info *info = file_fb_info(file);
+ if (!info)
+ return -ENODEV;
return do_fb_ioctl(info, cmd, arg);
}
@@ -1265,12 +1307,13 @@ static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd,
static long fb_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
- struct inode *inode = file->f_path.dentry->d_inode;
- int fbidx = iminor(inode);
- struct fb_info *info = registered_fb[fbidx];
- struct fb_ops *fb = info->fbops;
+ struct fb_info *info = file_fb_info(file);
+ struct fb_ops *fb;
long ret = -ENOIOCTLCMD;
+ if (!info)
+ return -ENODEV;
+ fb = info->fbops;
switch(cmd) {
case FBIOGET_VSCREENINFO:
case FBIOPUT_VSCREENINFO:
@@ -1303,16 +1346,18 @@ static long fb_compat_ioctl(struct file *file, unsigned int cmd,
static int
fb_mmap(struct file *file, struct vm_area_struct * vma)
{
- int fbidx = iminor(file->f_path.dentry->d_inode);
- struct fb_info *info = registered_fb[fbidx];
- struct fb_ops *fb = info->fbops;
+ struct fb_info *info = file_fb_info(file);
+ struct fb_ops *fb;
unsigned long off;
unsigned long start;
u32 len;
+ if (!info)
+ return -ENODEV;
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
return -EINVAL;
off = vma->vm_pgoff << PAGE_SHIFT;
+ fb = info->fbops;
if (!fb)
return -ENODEV;
mutex_lock(&info->mm_lock);
@@ -1361,14 +1406,16 @@ __releases(&info->lock)
struct fb_info *info;
int res = 0;
- if (fbidx >= FB_MAX)
- return -ENODEV;
- info = registered_fb[fbidx];
- if (!info)
+ info = get_fb_info(fbidx);
+ if (!info) {
request_module("fb%d", fbidx);
- info = registered_fb[fbidx];
- if (!info)
- return -ENODEV;
+ info = get_fb_info(fbidx);
+ if (!info)
+ return -ENODEV;
+ }
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+
mutex_lock(&info->lock);
if (!try_module_get(info->fbops->owner)) {
res = -ENODEV;
@@ -1386,6 +1433,8 @@ __releases(&info->lock)
#endif
out:
mutex_unlock(&info->lock);
+ if (res)
+ put_fb_info(info);
return res;
}
@@ -1401,6 +1450,7 @@ __releases(&info->lock)
info->fbops->fb_release(info,1);
module_put(info->fbops->owner);
mutex_unlock(&info->lock);
+ put_fb_info(info);
return 0;
}
@@ -1487,8 +1537,10 @@ static bool fb_do_apertures_overlap(struct apertures_struct *gena,
return false;
}
+static int do_unregister_framebuffer(struct fb_info *fb_info);
+
#define VGA_FB_PHYS 0xA0000
-void remove_conflicting_framebuffers(struct apertures_struct *a,
+static void do_remove_conflicting_framebuffers(struct apertures_struct *a,
const char *name, bool primary)
{
int i;
@@ -1510,43 +1562,32 @@ void remove_conflicting_framebuffers(struct apertures_struct *a,
printk(KERN_INFO "fb: conflicting fb hw usage "
"%s vs %s - removing generic driver\n",
name, registered_fb[i]->fix.id);
- unregister_framebuffer(registered_fb[i]);
+ do_unregister_framebuffer(registered_fb[i]);
}
}
}
-EXPORT_SYMBOL(remove_conflicting_framebuffers);
-/**
- * register_framebuffer - registers a frame buffer device
- * @fb_info: frame buffer info structure
- *
- * Registers a frame buffer device @fb_info.
- *
- * Returns negative errno on error, or zero for success.
- *
- */
-
-int
-register_framebuffer(struct fb_info *fb_info)
+static int do_register_framebuffer(struct fb_info *fb_info)
{
int i;
struct fb_event event;
struct fb_videomode mode;
- if (num_registered_fb == FB_MAX)
- return -ENXIO;
-
if (fb_check_foreignness(fb_info))
return -ENOSYS;
- remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,
+ do_remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,
fb_is_primary_device(fb_info));
+ if (num_registered_fb == FB_MAX)
+ return -ENXIO;
+
num_registered_fb++;
for (i = 0 ; i < FB_MAX; i++)
if (!registered_fb[i])
break;
fb_info->node = i;
+ atomic_set(&fb_info->count, 1);
mutex_init(&fb_info->lock);
mutex_init(&fb_info->mm_lock);
@@ -1592,36 +1633,14 @@ register_framebuffer(struct fb_info *fb_info)
return 0;
}
-
-/**
- * unregister_framebuffer - releases a frame buffer device
- * @fb_info: frame buffer info structure
- *
- * Unregisters a frame buffer device @fb_info.
- *
- * Returns negative errno on error, or zero for success.
- *
- * This function will also notify the framebuffer console
- * to release the driver.
- *
- * This is meant to be called within a driver's module_exit()
- * function. If this is called outside module_exit(), ensure
- * that the driver implements fb_open() and fb_release() to
- * check that no processes are using the device.
- */
-
-int
-unregister_framebuffer(struct fb_info *fb_info)
+static int do_unregister_framebuffer(struct fb_info *fb_info)
{
struct fb_event event;
int i, ret = 0;
i = fb_info->node;
- if (!registered_fb[i]) {
- ret = -EINVAL;
- goto done;
- }
-
+ if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info)
+ return -EINVAL;
if (!lock_fb_info(fb_info))
return -ENODEV;
@@ -1629,16 +1648,14 @@ unregister_framebuffer(struct fb_info *fb_info)
ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event);
unlock_fb_info(fb_info);
- if (ret) {
- ret = -EINVAL;
- goto done;
- }
+ if (ret)
+ return -EINVAL;
if (fb_info->pixmap.addr &&
(fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
kfree(fb_info->pixmap.addr);
fb_destroy_modelist(&fb_info->modelist);
- registered_fb[i]=NULL;
+ registered_fb[i] = NULL;
num_registered_fb--;
fb_cleanup_device(fb_info);
device_destroy(fb_class, MKDEV(FB_MAJOR, i));
@@ -1646,9 +1663,65 @@ unregister_framebuffer(struct fb_info *fb_info)
fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
/* this may free fb info */
- if (fb_info->fbops->fb_destroy)
- fb_info->fbops->fb_destroy(fb_info);
-done:
+ put_fb_info(fb_info);
+ return 0;
+}
+
+void remove_conflicting_framebuffers(struct apertures_struct *a,
+ const char *name, bool primary)
+{
+ mutex_lock(&registration_lock);
+ do_remove_conflicting_framebuffers(a, name, primary);
+ mutex_unlock(&registration_lock);
+}
+EXPORT_SYMBOL(remove_conflicting_framebuffers);
+
+/**
+ * register_framebuffer - registers a frame buffer device
+ * @fb_info: frame buffer info structure
+ *
+ * Registers a frame buffer device @fb_info.
+ *
+ * Returns negative errno on error, or zero for success.
+ *
+ */
+int
+register_framebuffer(struct fb_info *fb_info)
+{
+ int ret;
+
+ mutex_lock(&registration_lock);
+ ret = do_register_framebuffer(fb_info);
+ mutex_unlock(&registration_lock);
+
+ return ret;
+}
+
+/**
+ * unregister_framebuffer - releases a frame buffer device
+ * @fb_info: frame buffer info structure
+ *
+ * Unregisters a frame buffer device @fb_info.
+ *
+ * Returns negative errno on error, or zero for success.
+ *
+ * This function will also notify the framebuffer console
+ * to release the driver.
+ *
+ * This is meant to be called within a driver's module_exit()
+ * function. If this is called outside module_exit(), ensure
+ * that the driver implements fb_open() and fb_release() to
+ * check that no processes are using the device.
+ */
+int
+unregister_framebuffer(struct fb_info *fb_info)
+{
+ int ret;
+
+ mutex_lock(&registration_lock);
+ ret = do_unregister_framebuffer(fb_info);
+ mutex_unlock(&registration_lock);
+
return ret;
}
diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c
index a2e5b5100ab..0f4e8c942f9 100644
--- a/drivers/video/pxafb.c
+++ b/drivers/video/pxafb.c
@@ -1648,7 +1648,9 @@ pxafb_freq_transition(struct notifier_block *nb, unsigned long val, void *data)
switch (val) {
case CPUFREQ_PRECHANGE:
- if (!fbi->overlay[0].usage && !fbi->overlay[1].usage)
+#ifdef CONFIG_FB_PXA_OVERLAY
+ if (!(fbi->overlay[0].usage || fbi->overlay[1].usage))
+#endif
set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE);
break;
diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c
index 68041d9dc26..695066b5b2e 100644
--- a/drivers/video/udlfb.c
+++ b/drivers/video/udlfb.c
@@ -27,6 +27,7 @@
#include <linux/fb.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
+#include <linux/prefetch.h>
#include <linux/delay.h>
#include <video/udlfb.h>
#include "edid.h"
diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c
index 4fb5b2bf234..4bcc8b82640 100644
--- a/drivers/virtio/virtio_pci.c
+++ b/drivers/virtio/virtio_pci.c
@@ -590,15 +590,10 @@ static struct virtio_config_ops virtio_pci_config_ops = {
static void virtio_pci_release_dev(struct device *_d)
{
- struct virtio_device *dev = container_of(_d, struct virtio_device, dev);
+ struct virtio_device *dev = container_of(_d, struct virtio_device,
+ dev);
struct virtio_pci_device *vp_dev = to_vp_device(dev);
- struct pci_dev *pci_dev = vp_dev->pci_dev;
- vp_del_vqs(dev);
- pci_set_drvdata(pci_dev, NULL);
- pci_iounmap(pci_dev, vp_dev->ioaddr);
- pci_release_regions(pci_dev);
- pci_disable_device(pci_dev);
kfree(vp_dev);
}
@@ -681,6 +676,12 @@ static void __devexit virtio_pci_remove(struct pci_dev *pci_dev)
struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
unregister_virtio_device(&vp_dev->vdev);
+
+ vp_del_vqs(&vp_dev->vdev);
+ pci_set_drvdata(pci_dev, NULL);
+ pci_iounmap(pci_dev, vp_dev->ioaddr);
+ pci_release_regions(pci_dev);
+ pci_disable_device(pci_dev);
}
#ifdef CONFIG_PM
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index cc2f73e0347..b0043fb26a4 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -371,6 +371,7 @@ void *virtqueue_detach_unused_buf(struct virtqueue *_vq)
/* detach_buf clears data, so grab it now. */
buf = vq->data[i];
detach_buf(vq, i);
+ vq->vring.avail->idx--;
END_USE(vq);
return buf;
}
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 1b0f98bc51b..022f9eb0b7b 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -990,6 +990,12 @@ config BCM63XX_WDT
To compile this driver as a loadable module, choose M here.
The module will be called bcm63xx_wdt.
+config LANTIQ_WDT
+ tristate "Lantiq SoC watchdog"
+ depends on LANTIQ
+ help
+ Hardware driver for the Lantiq SoC Watchdog Timer.
+
# PARISC Architecture
# POWERPC Architecture
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 3f8608b922a..ed26f7094e4 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -123,6 +123,7 @@ obj-$(CONFIG_AR7_WDT) += ar7_wdt.o
obj-$(CONFIG_TXX9_WDT) += txx9wdt.o
obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o
octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o
+obj-$(CONFIG_LANTIQ_WDT) += lantiq_wdt.o
# PARISC Architecture
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c
index 35a0d12dad7..5fd020da7c5 100644
--- a/drivers/watchdog/iTCO_wdt.c
+++ b/drivers/watchdog/iTCO_wdt.c
@@ -35,6 +35,7 @@
* document number 324645-001, 324646-001: Cougar Point (CPT)
* document number TBD : Patsburg (PBG)
* document number TBD : DH89xxCC
+ * document number TBD : Panther Point
*/
/*
@@ -153,6 +154,38 @@ enum iTCO_chipsets {
TCO_PBG1, /* Patsburg */
TCO_PBG2, /* Patsburg */
TCO_DH89XXCC, /* DH89xxCC */
+ TCO_PPT0, /* Panther Point */
+ TCO_PPT1, /* Panther Point */
+ TCO_PPT2, /* Panther Point */
+ TCO_PPT3, /* Panther Point */
+ TCO_PPT4, /* Panther Point */
+ TCO_PPT5, /* Panther Point */
+ TCO_PPT6, /* Panther Point */
+ TCO_PPT7, /* Panther Point */
+ TCO_PPT8, /* Panther Point */
+ TCO_PPT9, /* Panther Point */
+ TCO_PPT10, /* Panther Point */
+ TCO_PPT11, /* Panther Point */
+ TCO_PPT12, /* Panther Point */
+ TCO_PPT13, /* Panther Point */
+ TCO_PPT14, /* Panther Point */
+ TCO_PPT15, /* Panther Point */
+ TCO_PPT16, /* Panther Point */
+ TCO_PPT17, /* Panther Point */
+ TCO_PPT18, /* Panther Point */
+ TCO_PPT19, /* Panther Point */
+ TCO_PPT20, /* Panther Point */
+ TCO_PPT21, /* Panther Point */
+ TCO_PPT22, /* Panther Point */
+ TCO_PPT23, /* Panther Point */
+ TCO_PPT24, /* Panther Point */
+ TCO_PPT25, /* Panther Point */
+ TCO_PPT26, /* Panther Point */
+ TCO_PPT27, /* Panther Point */
+ TCO_PPT28, /* Panther Point */
+ TCO_PPT29, /* Panther Point */
+ TCO_PPT30, /* Panther Point */
+ TCO_PPT31, /* Panther Point */
};
static struct {
@@ -244,6 +277,38 @@ static struct {
{"Patsburg", 2},
{"Patsburg", 2},
{"DH89xxCC", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
+ {"Panther Point", 2},
{NULL, 0}
};
@@ -363,6 +428,38 @@ static DEFINE_PCI_DEVICE_TABLE(iTCO_wdt_pci_tbl) = {
{ ITCO_PCI_DEVICE(0x1d40, TCO_PBG1)},
{ ITCO_PCI_DEVICE(0x1d41, TCO_PBG2)},
{ ITCO_PCI_DEVICE(0x2310, TCO_DH89XXCC)},
+ { ITCO_PCI_DEVICE(0x1e40, TCO_PPT0)},
+ { ITCO_PCI_DEVICE(0x1e41, TCO_PPT1)},
+ { ITCO_PCI_DEVICE(0x1e42, TCO_PPT2)},
+ { ITCO_PCI_DEVICE(0x1e43, TCO_PPT3)},
+ { ITCO_PCI_DEVICE(0x1e44, TCO_PPT4)},
+ { ITCO_PCI_DEVICE(0x1e45, TCO_PPT5)},
+ { ITCO_PCI_DEVICE(0x1e46, TCO_PPT6)},
+ { ITCO_PCI_DEVICE(0x1e47, TCO_PPT7)},
+ { ITCO_PCI_DEVICE(0x1e48, TCO_PPT8)},
+ { ITCO_PCI_DEVICE(0x1e49, TCO_PPT9)},
+ { ITCO_PCI_DEVICE(0x1e4a, TCO_PPT10)},
+ { ITCO_PCI_DEVICE(0x1e4b, TCO_PPT11)},
+ { ITCO_PCI_DEVICE(0x1e4c, TCO_PPT12)},
+ { ITCO_PCI_DEVICE(0x1e4d, TCO_PPT13)},
+ { ITCO_PCI_DEVICE(0x1e4e, TCO_PPT14)},
+ { ITCO_PCI_DEVICE(0x1e4f, TCO_PPT15)},
+ { ITCO_PCI_DEVICE(0x1e50, TCO_PPT16)},
+ { ITCO_PCI_DEVICE(0x1e51, TCO_PPT17)},
+ { ITCO_PCI_DEVICE(0x1e52, TCO_PPT18)},
+ { ITCO_PCI_DEVICE(0x1e53, TCO_PPT19)},
+ { ITCO_PCI_DEVICE(0x1e54, TCO_PPT20)},
+ { ITCO_PCI_DEVICE(0x1e55, TCO_PPT21)},
+ { ITCO_PCI_DEVICE(0x1e56, TCO_PPT22)},
+ { ITCO_PCI_DEVICE(0x1e57, TCO_PPT23)},
+ { ITCO_PCI_DEVICE(0x1e58, TCO_PPT24)},
+ { ITCO_PCI_DEVICE(0x1e59, TCO_PPT25)},
+ { ITCO_PCI_DEVICE(0x1e5a, TCO_PPT26)},
+ { ITCO_PCI_DEVICE(0x1e5b, TCO_PPT27)},
+ { ITCO_PCI_DEVICE(0x1e5c, TCO_PPT28)},
+ { ITCO_PCI_DEVICE(0x1e5d, TCO_PPT29)},
+ { ITCO_PCI_DEVICE(0x1e5e, TCO_PPT30)},
+ { ITCO_PCI_DEVICE(0x1e5f, TCO_PPT31)},
{ 0, }, /* End of list */
};
MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl);
diff --git a/drivers/watchdog/lantiq_wdt.c b/drivers/watchdog/lantiq_wdt.c
new file mode 100644
index 00000000000..7d82adac1cb
--- /dev/null
+++ b/drivers/watchdog/lantiq_wdt.c
@@ -0,0 +1,261 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
+ * Based on EP93xx wdt driver
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <lantiq.h>
+
+/* Section 3.4 of the datasheet
+ * The password sequence protects the WDT control register from unintended
+ * write actions, which might cause malfunction of the WDT.
+ *
+ * essentially the following two magic passwords need to be written to allow
+ * IO access to the WDT core
+ */
+#define LTQ_WDT_PW1 0x00BE0000
+#define LTQ_WDT_PW2 0x00DC0000
+
+#define LTQ_WDT_CR 0x0 /* watchdog control register */
+#define LTQ_WDT_SR 0x8 /* watchdog status register */
+
+#define LTQ_WDT_SR_EN (0x1 << 31) /* enable bit */
+#define LTQ_WDT_SR_PWD (0x3 << 26) /* turn on power */
+#define LTQ_WDT_SR_CLKDIV (0x3 << 24) /* turn on clock and set */
+ /* divider to 0x40000 */
+#define LTQ_WDT_DIVIDER 0x40000
+#define LTQ_MAX_TIMEOUT ((1 << 16) - 1) /* the reload field is 16 bit */
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+
+static void __iomem *ltq_wdt_membase;
+static unsigned long ltq_io_region_clk_rate;
+
+static unsigned long ltq_wdt_bootstatus;
+static unsigned long ltq_wdt_in_use;
+static int ltq_wdt_timeout = 30;
+static int ltq_wdt_ok_to_close;
+
+static void
+ltq_wdt_enable(void)
+{
+ ltq_wdt_timeout = ltq_wdt_timeout *
+ (ltq_io_region_clk_rate / LTQ_WDT_DIVIDER) + 0x1000;
+ if (ltq_wdt_timeout > LTQ_MAX_TIMEOUT)
+ ltq_wdt_timeout = LTQ_MAX_TIMEOUT;
+
+ /* write the first password magic */
+ ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR);
+ /* write the second magic plus the configuration and new timeout */
+ ltq_w32(LTQ_WDT_SR_EN | LTQ_WDT_SR_PWD | LTQ_WDT_SR_CLKDIV |
+ LTQ_WDT_PW2 | ltq_wdt_timeout, ltq_wdt_membase + LTQ_WDT_CR);
+}
+
+static void
+ltq_wdt_disable(void)
+{
+ /* write the first password magic */
+ ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR);
+ /* write the second password magic with no config
+ * this turns the watchdog off
+ */
+ ltq_w32(LTQ_WDT_PW2, ltq_wdt_membase + LTQ_WDT_CR);
+}
+
+static ssize_t
+ltq_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ ltq_wdt_ok_to_close = 0;
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ ltq_wdt_ok_to_close = 1;
+ else
+ ltq_wdt_ok_to_close = 0;
+ }
+ }
+ ltq_wdt_enable();
+ }
+
+ return len;
+}
+
+static struct watchdog_info ident = {
+ .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+ WDIOF_CARDRESET,
+ .identity = "ltq_wdt",
+};
+
+static long
+ltq_wdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = -ENOTTY;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info __user *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(ltq_wdt_bootstatus, (int __user *)arg);
+ break;
+
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, (int __user *)arg);
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(ltq_wdt_timeout, (int __user *)arg);
+ if (!ret)
+ ltq_wdt_enable();
+ /* intentional drop through */
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(ltq_wdt_timeout, (int __user *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ ltq_wdt_enable();
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+static int
+ltq_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &ltq_wdt_in_use))
+ return -EBUSY;
+ ltq_wdt_in_use = 1;
+ ltq_wdt_enable();
+
+ return nonseekable_open(inode, file);
+}
+
+static int
+ltq_wdt_release(struct inode *inode, struct file *file)
+{
+ if (ltq_wdt_ok_to_close)
+ ltq_wdt_disable();
+ else
+ pr_err("ltq_wdt: watchdog closed without warning\n");
+ ltq_wdt_ok_to_close = 0;
+ clear_bit(0, &ltq_wdt_in_use);
+
+ return 0;
+}
+
+static const struct file_operations ltq_wdt_fops = {
+ .owner = THIS_MODULE,
+ .write = ltq_wdt_write,
+ .unlocked_ioctl = ltq_wdt_ioctl,
+ .open = ltq_wdt_open,
+ .release = ltq_wdt_release,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice ltq_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ltq_wdt_fops,
+};
+
+static int __init
+ltq_wdt_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct clk *clk;
+
+ if (!res) {
+ dev_err(&pdev->dev, "cannot obtain I/O memory region");
+ return -ENOENT;
+ }
+ res = devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), dev_name(&pdev->dev));
+ if (!res) {
+ dev_err(&pdev->dev, "cannot request I/O memory region");
+ return -EBUSY;
+ }
+ ltq_wdt_membase = devm_ioremap_nocache(&pdev->dev, res->start,
+ resource_size(res));
+ if (!ltq_wdt_membase) {
+ dev_err(&pdev->dev, "cannot remap I/O memory region\n");
+ return -ENOMEM;
+ }
+
+ /* we do not need to enable the clock as it is always running */
+ clk = clk_get(&pdev->dev, "io");
+ WARN_ON(!clk);
+ ltq_io_region_clk_rate = clk_get_rate(clk);
+ clk_put(clk);
+
+ if (ltq_reset_cause() == LTQ_RST_CAUSE_WDTRST)
+ ltq_wdt_bootstatus = WDIOF_CARDRESET;
+
+ return misc_register(&ltq_wdt_miscdev);
+}
+
+static int __devexit
+ltq_wdt_remove(struct platform_device *pdev)
+{
+ misc_deregister(&ltq_wdt_miscdev);
+
+ if (ltq_wdt_membase)
+ iounmap(ltq_wdt_membase);
+
+ return 0;
+}
+
+
+static struct platform_driver ltq_wdt_driver = {
+ .remove = __devexit_p(ltq_wdt_remove),
+ .driver = {
+ .name = "ltq_wdt",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init
+init_ltq_wdt(void)
+{
+ return platform_driver_probe(&ltq_wdt_driver, ltq_wdt_probe);
+}
+
+static void __exit
+exit_ltq_wdt(void)
+{
+ return platform_driver_unregister(&ltq_wdt_driver);
+}
+
+module_init(init_ltq_wdt);
+module_exit(exit_ltq_wdt);
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
+
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
+MODULE_DESCRIPTION("Lantiq SoC Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c
index 528bceb220f..eed5436ffb5 100644
--- a/drivers/watchdog/mpc8xxx_wdt.c
+++ b/drivers/watchdog/mpc8xxx_wdt.c
@@ -185,17 +185,20 @@ static struct miscdevice mpc8xxx_wdt_miscdev = {
.fops = &mpc8xxx_wdt_fops,
};
+static const struct of_device_id mpc8xxx_wdt_match[];
static int __devinit mpc8xxx_wdt_probe(struct platform_device *ofdev)
{
int ret;
+ const struct of_device_id *match;
struct device_node *np = ofdev->dev.of_node;
struct mpc8xxx_wdt_type *wdt_type;
u32 freq = fsl_get_sys_freq();
bool enabled;
- if (!ofdev->dev.of_match)
+ match = of_match_device(mpc8xxx_wdt_match, &ofdev->dev);
+ if (!match)
return -EINVAL;
- wdt_type = ofdev->dev.of_match->data;
+ wdt_type = match->data;
if (!freq || freq == -1)
return -EINVAL;
diff --git a/drivers/watchdog/mtx-1_wdt.c b/drivers/watchdog/mtx-1_wdt.c
index 5ec5ac1f787..1479dc4d612 100644
--- a/drivers/watchdog/mtx-1_wdt.c
+++ b/drivers/watchdog/mtx-1_wdt.c
@@ -66,6 +66,7 @@ static struct {
int default_ticks;
unsigned long inuse;
unsigned gpio;
+ int gstate;
} mtx1_wdt_device;
static void mtx1_wdt_trigger(unsigned long unused)
@@ -75,13 +76,13 @@ static void mtx1_wdt_trigger(unsigned long unused)
spin_lock(&mtx1_wdt_device.lock);
if (mtx1_wdt_device.running)
ticks--;
- /*
- * toggle GPIO2_15
- */
- tmp = au_readl(GPIO2_DIR);
- tmp = (tmp & ~(1 << mtx1_wdt_device.gpio)) |
- ((~tmp) & (1 << mtx1_wdt_device.gpio));
- au_writel(tmp, GPIO2_DIR);
+
+ /* toggle wdt gpio */
+ mtx1_wdt_device.gstate = ~mtx1_wdt_device.gstate;
+ if (mtx1_wdt_device.gstate)
+ gpio_direction_output(mtx1_wdt_device.gpio, 1);
+ else
+ gpio_direction_input(mtx1_wdt_device.gpio);
if (mtx1_wdt_device.queue && ticks)
mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL);
@@ -103,7 +104,8 @@ static void mtx1_wdt_start(void)
spin_lock_irqsave(&mtx1_wdt_device.lock, flags);
if (!mtx1_wdt_device.queue) {
mtx1_wdt_device.queue = 1;
- gpio_set_value(mtx1_wdt_device.gpio, 1);
+ mtx1_wdt_device.gstate = 1;
+ gpio_direction_output(mtx1_wdt_device.gpio, 1);
mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL);
}
mtx1_wdt_device.running++;
@@ -117,7 +119,8 @@ static int mtx1_wdt_stop(void)
spin_lock_irqsave(&mtx1_wdt_device.lock, flags);
if (mtx1_wdt_device.queue) {
mtx1_wdt_device.queue = 0;
- gpio_set_value(mtx1_wdt_device.gpio, 0);
+ mtx1_wdt_device.gstate = 0;
+ gpio_direction_output(mtx1_wdt_device.gpio, 0);
}
ticks = mtx1_wdt_device.default_ticks;
spin_unlock_irqrestore(&mtx1_wdt_device.lock, flags);
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index f420f1ff7f1..4781f806701 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -4,21 +4,21 @@ obj-y += xenbus/
nostackp := $(call cc-option, -fno-stack-protector)
CFLAGS_features.o := $(nostackp)
-obj-$(CONFIG_BLOCK) += biomerge.o
-obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o
-obj-$(CONFIG_XEN_XENCOMM) += xencomm.o
-obj-$(CONFIG_XEN_BALLOON) += xen-balloon.o
-obj-$(CONFIG_XEN_DEV_EVTCHN) += xen-evtchn.o
-obj-$(CONFIG_XEN_GNTDEV) += xen-gntdev.o
+obj-$(CONFIG_BLOCK) += biomerge.o
+obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o
+obj-$(CONFIG_XEN_XENCOMM) += xencomm.o
+obj-$(CONFIG_XEN_BALLOON) += xen-balloon.o
+obj-$(CONFIG_XEN_DEV_EVTCHN) += xen-evtchn.o
+obj-$(CONFIG_XEN_GNTDEV) += xen-gntdev.o
obj-$(CONFIG_XEN_GRANT_DEV_ALLOC) += xen-gntalloc.o
-obj-$(CONFIG_XENFS) += xenfs/
+obj-$(CONFIG_XENFS) += xenfs/
obj-$(CONFIG_XEN_SYS_HYPERVISOR) += sys-hypervisor.o
-obj-$(CONFIG_XEN_PLATFORM_PCI) += xen-platform-pci.o
-obj-$(CONFIG_SWIOTLB_XEN) += swiotlb-xen.o
-obj-$(CONFIG_XEN_DOM0) += pci.o
+obj-$(CONFIG_XEN_PLATFORM_PCI) += xen-platform-pci.o
+obj-$(CONFIG_SWIOTLB_XEN) += swiotlb-xen.o
+obj-$(CONFIG_XEN_DOM0) += pci.o
-xen-evtchn-y := evtchn.o
+xen-evtchn-y := evtchn.o
xen-gntdev-y := gntdev.o
xen-gntalloc-y := gntalloc.o
-xen-platform-pci-y := platform-pci.o
+xen-platform-pci-y := platform-pci.o
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c
index 043af8ad6b6..f54290baa3d 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -114,7 +114,6 @@ static void __balloon_append(struct page *page)
if (PageHighMem(page)) {
list_add_tail(&page->lru, &ballooned_pages);
balloon_stats.balloon_high++;
- dec_totalhigh_pages();
} else {
list_add(&page->lru, &ballooned_pages);
balloon_stats.balloon_low++;
@@ -124,6 +123,8 @@ static void __balloon_append(struct page *page)
static void balloon_append(struct page *page)
{
__balloon_append(page);
+ if (PageHighMem(page))
+ dec_totalhigh_pages();
totalram_pages--;
}
@@ -193,7 +194,7 @@ static enum bp_state update_schedule(enum bp_state state)
return BP_EAGAIN;
}
-static unsigned long current_target(void)
+static long current_credit(void)
{
unsigned long target = balloon_stats.target_pages;
@@ -202,7 +203,7 @@ static unsigned long current_target(void)
balloon_stats.balloon_low +
balloon_stats.balloon_high);
- return target;
+ return target - balloon_stats.current_pages;
}
static enum bp_state increase_reservation(unsigned long nr_pages)
@@ -246,7 +247,7 @@ static enum bp_state increase_reservation(unsigned long nr_pages)
set_phys_to_machine(pfn, frame_list[i]);
/* Link back into the page tables if not highmem. */
- if (!xen_hvm_domain() && pfn < max_low_pfn) {
+ if (xen_pv_domain() && !PageHighMem(page)) {
int ret;
ret = HYPERVISOR_update_va_mapping(
(unsigned long)__va(pfn << PAGE_SHIFT),
@@ -293,7 +294,7 @@ static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp)
scrub_page(page);
- if (!xen_hvm_domain() && !PageHighMem(page)) {
+ if (xen_pv_domain() && !PageHighMem(page)) {
ret = HYPERVISOR_update_va_mapping(
(unsigned long)__va(pfn << PAGE_SHIFT),
__pte_ma(0), 0);
@@ -337,7 +338,7 @@ static void balloon_process(struct work_struct *work)
mutex_lock(&balloon_mutex);
do {
- credit = current_target() - balloon_stats.current_pages;
+ credit = current_credit();
if (credit > 0)
state = increase_reservation(credit);
@@ -420,7 +421,7 @@ void free_xenballooned_pages(int nr_pages, struct page** pages)
}
/* The balloon may be too large now. Shrink it if needed. */
- if (current_target() != balloon_stats.current_pages)
+ if (current_credit())
schedule_delayed_work(&balloon_worker, 0);
mutex_unlock(&balloon_mutex);
@@ -429,7 +430,7 @@ EXPORT_SYMBOL(free_xenballooned_pages);
static int __init balloon_init(void)
{
- unsigned long pfn, nr_pages, extra_pfn_end;
+ unsigned long pfn, extra_pfn_end;
struct page *page;
if (!xen_domain())
@@ -437,11 +438,7 @@ static int __init balloon_init(void)
pr_info("xen/balloon: Initialising balloon driver.\n");
- if (xen_pv_domain())
- nr_pages = xen_start_info->nr_pages;
- else
- nr_pages = max_pfn;
- balloon_stats.current_pages = min(nr_pages, max_pfn);
+ balloon_stats.current_pages = xen_pv_domain() ? min(xen_start_info->nr_pages, max_pfn) : max_pfn;
balloon_stats.target_pages = balloon_stats.current_pages;
balloon_stats.balloon_low = 0;
balloon_stats.balloon_high = 0;
@@ -466,7 +463,7 @@ static int __init balloon_init(void)
pfn < extra_pfn_end;
pfn++) {
page = pfn_to_page(pfn);
- /* totalram_pages doesn't include the boot-time
+ /* totalram_pages and totalhigh_pages do not include the boot-time
balloon extension, so don't subtract from it. */
__balloon_append(page);
}
diff --git a/drivers/xen/events.c b/drivers/xen/events.c
index 42d6c930cc8..3ff822b4814 100644
--- a/drivers/xen/events.c
+++ b/drivers/xen/events.c
@@ -101,6 +101,7 @@ struct irq_info
unsigned short gsi;
unsigned char vector;
unsigned char flags;
+ uint16_t domid;
} pirq;
} u;
};
@@ -118,6 +119,8 @@ static DEFINE_PER_CPU(unsigned long [NR_EVENT_CHANNELS/BITS_PER_LONG],
static struct irq_chip xen_dynamic_chip;
static struct irq_chip xen_percpu_chip;
static struct irq_chip xen_pirq_chip;
+static void enable_dynirq(struct irq_data *data);
+static void disable_dynirq(struct irq_data *data);
/* Get info for IRQ */
static struct irq_info *info_for_irq(unsigned irq)
@@ -184,6 +187,7 @@ static void xen_irq_info_pirq_init(unsigned irq,
unsigned short pirq,
unsigned short gsi,
unsigned short vector,
+ uint16_t domid,
unsigned char flags)
{
struct irq_info *info = info_for_irq(irq);
@@ -193,6 +197,7 @@ static void xen_irq_info_pirq_init(unsigned irq,
info->u.pirq.pirq = pirq;
info->u.pirq.gsi = gsi;
info->u.pirq.vector = vector;
+ info->u.pirq.domid = domid;
info->u.pirq.flags = flags;
}
@@ -473,16 +478,6 @@ static void xen_free_irq(unsigned irq)
irq_free_desc(irq);
}
-static void pirq_unmask_notify(int irq)
-{
- struct physdev_eoi eoi = { .irq = pirq_from_irq(irq) };
-
- if (unlikely(pirq_needs_eoi(irq))) {
- int rc = HYPERVISOR_physdev_op(PHYSDEVOP_eoi, &eoi);
- WARN_ON(rc);
- }
-}
-
static void pirq_query_unmask(int irq)
{
struct physdev_irq_status_query irq_status;
@@ -506,6 +501,29 @@ static bool probing_irq(int irq)
return desc && desc->action == NULL;
}
+static void eoi_pirq(struct irq_data *data)
+{
+ int evtchn = evtchn_from_irq(data->irq);
+ struct physdev_eoi eoi = { .irq = pirq_from_irq(data->irq) };
+ int rc = 0;
+
+ irq_move_irq(data);
+
+ if (VALID_EVTCHN(evtchn))
+ clear_evtchn(evtchn);
+
+ if (pirq_needs_eoi(data->irq)) {
+ rc = HYPERVISOR_physdev_op(PHYSDEVOP_eoi, &eoi);
+ WARN_ON(rc);
+ }
+}
+
+static void mask_ack_pirq(struct irq_data *data)
+{
+ disable_dynirq(data);
+ eoi_pirq(data);
+}
+
static unsigned int __startup_pirq(unsigned int irq)
{
struct evtchn_bind_pirq bind_pirq;
@@ -539,7 +557,7 @@ static unsigned int __startup_pirq(unsigned int irq)
out:
unmask_evtchn(evtchn);
- pirq_unmask_notify(irq);
+ eoi_pirq(irq_get_irq_data(irq));
return 0;
}
@@ -579,18 +597,7 @@ static void enable_pirq(struct irq_data *data)
static void disable_pirq(struct irq_data *data)
{
-}
-
-static void ack_pirq(struct irq_data *data)
-{
- int evtchn = evtchn_from_irq(data->irq);
-
- irq_move_irq(data);
-
- if (VALID_EVTCHN(evtchn)) {
- mask_evtchn(evtchn);
- clear_evtchn(evtchn);
- }
+ disable_dynirq(data);
}
static int find_irq_by_gsi(unsigned gsi)
@@ -639,9 +646,6 @@ int xen_bind_pirq_gsi_to_irq(unsigned gsi,
if (irq < 0)
goto out;
- irq_set_chip_and_handler_name(irq, &xen_pirq_chip, handle_level_irq,
- name);
-
irq_op.irq = irq;
irq_op.vector = 0;
@@ -655,9 +659,35 @@ int xen_bind_pirq_gsi_to_irq(unsigned gsi,
goto out;
}
- xen_irq_info_pirq_init(irq, 0, pirq, gsi, irq_op.vector,
+ xen_irq_info_pirq_init(irq, 0, pirq, gsi, irq_op.vector, DOMID_SELF,
shareable ? PIRQ_SHAREABLE : 0);
+ pirq_query_unmask(irq);
+ /* We try to use the handler with the appropriate semantic for the
+ * type of interrupt: if the interrupt doesn't need an eoi
+ * (pirq_needs_eoi returns false), we treat it like an edge
+ * triggered interrupt so we use handle_edge_irq.
+ * As a matter of fact this only happens when the corresponding
+ * physical interrupt is edge triggered or an msi.
+ *
+ * On the other hand if the interrupt needs an eoi (pirq_needs_eoi
+ * returns true) we treat it like a level triggered interrupt so we
+ * use handle_fasteoi_irq like the native code does for this kind of
+ * interrupts.
+ * Depending on the Xen version, pirq_needs_eoi might return true
+ * not only for level triggered interrupts but for edge triggered
+ * interrupts too. In any case Xen always honors the eoi mechanism,
+ * not injecting any more pirqs of the same kind if the first one
+ * hasn't received an eoi yet. Therefore using the fasteoi handler
+ * is the right choice either way.
+ */
+ if (pirq_needs_eoi(irq))
+ irq_set_chip_and_handler_name(irq, &xen_pirq_chip,
+ handle_fasteoi_irq, name);
+ else
+ irq_set_chip_and_handler_name(irq, &xen_pirq_chip,
+ handle_edge_irq, name);
+
out:
spin_unlock(&irq_mapping_update_lock);
@@ -680,7 +710,8 @@ int xen_allocate_pirq_msi(struct pci_dev *dev, struct msi_desc *msidesc)
}
int xen_bind_pirq_msi_to_irq(struct pci_dev *dev, struct msi_desc *msidesc,
- int pirq, int vector, const char *name)
+ int pirq, int vector, const char *name,
+ domid_t domid)
{
int irq, ret;
@@ -690,10 +721,10 @@ int xen_bind_pirq_msi_to_irq(struct pci_dev *dev, struct msi_desc *msidesc,
if (irq == -1)
goto out;
- irq_set_chip_and_handler_name(irq, &xen_pirq_chip, handle_level_irq,
- name);
+ irq_set_chip_and_handler_name(irq, &xen_pirq_chip, handle_edge_irq,
+ name);
- xen_irq_info_pirq_init(irq, 0, pirq, 0, vector, 0);
+ xen_irq_info_pirq_init(irq, 0, pirq, 0, vector, domid, 0);
ret = irq_set_msi_desc(irq, msidesc);
if (ret < 0)
goto error_irq;
@@ -722,9 +753,16 @@ int xen_destroy_irq(int irq)
if (xen_initial_domain()) {
unmap_irq.pirq = info->u.pirq.pirq;
- unmap_irq.domid = DOMID_SELF;
+ unmap_irq.domid = info->u.pirq.domid;
rc = HYPERVISOR_physdev_op(PHYSDEVOP_unmap_pirq, &unmap_irq);
- if (rc) {
+ /* If another domain quits without making the pci_disable_msix
+ * call, the Xen hypervisor takes care of freeing the PIRQs
+ * (free_domain_pirqs).
+ */
+ if ((rc == -ESRCH && info->u.pirq.domid != DOMID_SELF))
+ printk(KERN_INFO "domain %d does not have %d anymore\n",
+ info->u.pirq.domid, info->u.pirq.pirq);
+ else if (rc) {
printk(KERN_WARNING "unmap irq failed %d\n", rc);
goto out;
}
@@ -759,6 +797,12 @@ out:
return irq;
}
+
+int xen_pirq_from_irq(unsigned irq)
+{
+ return pirq_from_irq(irq);
+}
+EXPORT_SYMBOL_GPL(xen_pirq_from_irq);
int bind_evtchn_to_irq(unsigned int evtchn)
{
int irq;
@@ -773,7 +817,7 @@ int bind_evtchn_to_irq(unsigned int evtchn)
goto out;
irq_set_chip_and_handler_name(irq, &xen_dynamic_chip,
- handle_fasteoi_irq, "event");
+ handle_edge_irq, "event");
xen_irq_info_evtchn_init(irq, evtchn);
}
@@ -912,8 +956,7 @@ int bind_evtchn_to_irqhandler(unsigned int evtchn,
unsigned long irqflags,
const char *devname, void *dev_id)
{
- unsigned int irq;
- int retval;
+ int irq, retval;
irq = bind_evtchn_to_irq(evtchn);
if (irq < 0)
@@ -955,8 +998,7 @@ int bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu,
irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
{
- unsigned int irq;
- int retval;
+ int irq, retval;
irq = bind_virq_to_irq(virq, cpu);
if (irq < 0)
@@ -1181,9 +1223,6 @@ static void __xen_evtchn_do_upcall(void)
port = (word_idx * BITS_PER_LONG) + bit_idx;
irq = evtchn_to_irq[port];
- mask_evtchn(port);
- clear_evtchn(port);
-
if (irq != -1) {
desc = irq_to_desc(irq);
if (desc)
@@ -1339,10 +1378,16 @@ static void ack_dynirq(struct irq_data *data)
{
int evtchn = evtchn_from_irq(data->irq);
- irq_move_masked_irq(data);
+ irq_move_irq(data);
if (VALID_EVTCHN(evtchn))
- unmask_evtchn(evtchn);
+ clear_evtchn(evtchn);
+}
+
+static void mask_ack_dynirq(struct irq_data *data)
+{
+ disable_dynirq(data);
+ ack_dynirq(data);
}
static int retrigger_dynirq(struct irq_data *data)
@@ -1504,6 +1549,18 @@ void xen_poll_irq(int irq)
xen_poll_irq_timeout(irq, 0 /* no timeout */);
}
+/* Check whether the IRQ line is shared with other guests. */
+int xen_test_irq_shared(int irq)
+{
+ struct irq_info *info = info_for_irq(irq);
+ struct physdev_irq_status_query irq_status = { .irq = info->u.pirq.pirq };
+
+ if (HYPERVISOR_physdev_op(PHYSDEVOP_irq_status_query, &irq_status))
+ return 0;
+ return !(irq_status.flags & XENIRQSTAT_shared);
+}
+EXPORT_SYMBOL_GPL(xen_test_irq_shared);
+
void xen_irq_resume(void)
{
unsigned int cpu, evtchn;
@@ -1537,7 +1594,9 @@ static struct irq_chip xen_dynamic_chip __read_mostly = {
.irq_mask = disable_dynirq,
.irq_unmask = enable_dynirq,
- .irq_eoi = ack_dynirq,
+ .irq_ack = ack_dynirq,
+ .irq_mask_ack = mask_ack_dynirq,
+
.irq_set_affinity = set_affinity_irq,
.irq_retrigger = retrigger_dynirq,
};
@@ -1547,14 +1606,15 @@ static struct irq_chip xen_pirq_chip __read_mostly = {
.irq_startup = startup_pirq,
.irq_shutdown = shutdown_pirq,
-
.irq_enable = enable_pirq,
- .irq_unmask = enable_pirq,
-
.irq_disable = disable_pirq,
- .irq_mask = disable_pirq,
- .irq_ack = ack_pirq,
+ .irq_mask = disable_dynirq,
+ .irq_unmask = enable_dynirq,
+
+ .irq_ack = eoi_pirq,
+ .irq_eoi = eoi_pirq,
+ .irq_mask_ack = mask_ack_pirq,
.irq_set_affinity = set_affinity_irq,
diff --git a/drivers/xen/gntalloc.c b/drivers/xen/gntalloc.c
index a7ffdfe19fc..f6832f46aea 100644
--- a/drivers/xen/gntalloc.c
+++ b/drivers/xen/gntalloc.c
@@ -427,6 +427,17 @@ static long gntalloc_ioctl(struct file *filp, unsigned int cmd,
return 0;
}
+static void gntalloc_vma_open(struct vm_area_struct *vma)
+{
+ struct gntalloc_gref *gref = vma->vm_private_data;
+ if (!gref)
+ return;
+
+ spin_lock(&gref_lock);
+ gref->users++;
+ spin_unlock(&gref_lock);
+}
+
static void gntalloc_vma_close(struct vm_area_struct *vma)
{
struct gntalloc_gref *gref = vma->vm_private_data;
@@ -441,6 +452,7 @@ static void gntalloc_vma_close(struct vm_area_struct *vma)
}
static struct vm_operations_struct gntalloc_vmops = {
+ .open = gntalloc_vma_open,
.close = gntalloc_vma_close,
};
@@ -471,8 +483,6 @@ static int gntalloc_mmap(struct file *filp, struct vm_area_struct *vma)
vma->vm_private_data = gref;
vma->vm_flags |= VM_RESERVED;
- vma->vm_flags |= VM_DONTCOPY;
- vma->vm_flags |= VM_PFNMAP | VM_PFN_AT_MMAP;
vma->vm_ops = &gntalloc_vmops;
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index b0f9e8fb005..f914b26cf0c 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -330,17 +330,26 @@ static int unmap_grant_pages(struct grant_map *map, int offset, int pages)
/* ------------------------------------------------------------------ */
+static void gntdev_vma_open(struct vm_area_struct *vma)
+{
+ struct grant_map *map = vma->vm_private_data;
+
+ pr_debug("gntdev_vma_open %p\n", vma);
+ atomic_inc(&map->users);
+}
+
static void gntdev_vma_close(struct vm_area_struct *vma)
{
struct grant_map *map = vma->vm_private_data;
- pr_debug("close %p\n", vma);
+ pr_debug("gntdev_vma_close %p\n", vma);
map->vma = NULL;
vma->vm_private_data = NULL;
gntdev_put_map(map);
}
static struct vm_operations_struct gntdev_vmops = {
+ .open = gntdev_vma_open,
.close = gntdev_vma_close,
};
@@ -652,7 +661,10 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma)
vma->vm_ops = &gntdev_vmops;
- vma->vm_flags |= VM_RESERVED|VM_DONTCOPY|VM_DONTEXPAND|VM_PFNMAP;
+ vma->vm_flags |= VM_RESERVED|VM_DONTEXPAND;
+
+ if (use_ptemod)
+ vma->vm_flags |= VM_DONTCOPY|VM_PFNMAP;
vma->vm_private_data = map;
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index 3745a318def..fd725cde6ad 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -466,13 +466,30 @@ int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops,
if (map_ops[i].status)
continue;
- /* m2p override only supported for GNTMAP_contains_pte mappings */
- if (!(map_ops[i].flags & GNTMAP_contains_pte))
- continue;
- pte = (pte_t *) (mfn_to_virt(PFN_DOWN(map_ops[i].host_addr)) +
+ if (map_ops[i].flags & GNTMAP_contains_pte) {
+ pte = (pte_t *) (mfn_to_virt(PFN_DOWN(map_ops[i].host_addr)) +
(map_ops[i].host_addr & ~PAGE_MASK));
- mfn = pte_mfn(*pte);
- ret = m2p_add_override(mfn, pages[i]);
+ mfn = pte_mfn(*pte);
+ } else {
+ /* If you really wanted to do this:
+ * mfn = PFN_DOWN(map_ops[i].dev_bus_addr);
+ *
+ * The reason we do not implement it is b/c on the
+ * unmap path (gnttab_unmap_refs) we have no means of
+ * checking whether the page is !GNTMAP_contains_pte.
+ *
+ * That is without some extra data-structure to carry
+ * the struct page, bool clear_pte, and list_head next
+ * tuples and deal with allocation/delallocation, etc.
+ *
+ * The users of this API set the GNTMAP_contains_pte
+ * flag so lets just return not supported until it
+ * becomes neccessary to implement.
+ */
+ return -EOPNOTSUPP;
+ }
+ ret = m2p_add_override(mfn, pages[i],
+ map_ops[i].flags & GNTMAP_contains_pte);
if (ret)
return ret;
}
@@ -494,7 +511,7 @@ int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
return ret;
for (i = 0; i < count; i++) {
- ret = m2p_remove_override(pages[i]);
+ ret = m2p_remove_override(pages[i], true /* clear the PTE */);
if (ret)
return ret;
}
diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c
index 95143dd6904..0b5366b5be2 100644
--- a/drivers/xen/manage.c
+++ b/drivers/xen/manage.c
@@ -8,6 +8,7 @@
#include <linux/sysrq.h>
#include <linux/stop_machine.h>
#include <linux/freezer.h>
+#include <linux/syscore_ops.h>
#include <xen/xen.h>
#include <xen/xenbus.h>
@@ -61,7 +62,7 @@ static void xen_post_suspend(int cancelled)
xen_mm_unpin_all();
}
-#ifdef CONFIG_HIBERNATION
+#ifdef CONFIG_HIBERNATE_CALLBACKS
static int xen_suspend(void *data)
{
struct suspend_info *si = data;
@@ -69,9 +70,9 @@ static int xen_suspend(void *data)
BUG_ON(!irqs_disabled());
- err = sysdev_suspend(PMSG_FREEZE);
+ err = syscore_suspend();
if (err) {
- printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
+ printk(KERN_ERR "xen_suspend: system core suspend failed: %d\n",
err);
return err;
}
@@ -95,7 +96,7 @@ static int xen_suspend(void *data)
xen_timer_resume();
}
- sysdev_resume();
+ syscore_resume();
return 0;
}
@@ -173,7 +174,7 @@ out:
#endif
shutting_down = SHUTDOWN_INVALID;
}
-#endif /* CONFIG_HIBERNATION */
+#endif /* CONFIG_HIBERNATE_CALLBACKS */
struct shutdown_handler {
const char *command;
@@ -202,7 +203,7 @@ static void shutdown_handler(struct xenbus_watch *watch,
{ "poweroff", do_poweroff },
{ "halt", do_poweroff },
{ "reboot", do_reboot },
-#ifdef CONFIG_HIBERNATION
+#ifdef CONFIG_HIBERNATE_CALLBACKS
{ "suspend", do_suspend },
#endif
{NULL, NULL},
diff --git a/drivers/xen/sys-hypervisor.c b/drivers/xen/sys-hypervisor.c
index 60f1827a32c..1e0fe01eb67 100644
--- a/drivers/xen/sys-hypervisor.c
+++ b/drivers/xen/sys-hypervisor.c
@@ -215,7 +215,7 @@ static struct attribute_group xen_compilation_group = {
.attrs = xen_compile_attrs,
};
-int __init static xen_compilation_init(void)
+static int __init xen_compilation_init(void)
{
return sysfs_create_group(hypervisor_kobj, &xen_compilation_group);
}