summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/Kconfig9
-rw-r--r--drivers/acpi/acpica/dsopcode.c2
-rw-r--r--drivers/acpi/osl.c202
-rw-r--r--drivers/acpi/pci_bind.c12
-rw-r--r--drivers/acpi/pci_irq.c17
-rw-r--r--drivers/acpi/pci_root.c165
-rw-r--r--drivers/ata/ahci_platform.c46
-rw-r--r--drivers/ata/ata_piix.c444
-rw-r--r--drivers/ata/libahci.c2
-rw-r--r--drivers/ata/libata-acpi.c4
-rw-r--r--drivers/ata/libata-core.c31
-rw-r--r--drivers/ata/libata-eh.c1
-rw-r--r--drivers/ata/libata-scsi.c6
-rw-r--r--drivers/ata/pata_arasan_cf.c7
-rw-r--r--drivers/ata/pata_cmd64x.c6
-rw-r--r--drivers/ata/pata_cs5536.c32
-rw-r--r--drivers/ata/pata_ep93xx.c6
-rw-r--r--drivers/ata/pata_imx.c2
-rw-r--r--drivers/ata/pata_ixp4xx_cf.c11
-rw-r--r--drivers/ata/pata_mpc52xx.c17
-rw-r--r--drivers/ata/pata_octeon_cf.c423
-rw-r--r--drivers/ata/pata_of_platform.c8
-rw-r--r--drivers/ata/pata_palmld.c4
-rw-r--r--drivers/ata/pata_platform.c24
-rw-r--r--drivers/ata/pata_rdc.c3
-rw-r--r--[-rwxr-xr-x]drivers/ata/sata_dwc_460ex.c1
-rw-r--r--drivers/ata/sata_highbank.c14
-rw-r--r--drivers/ata/sata_inic162x.c2
-rw-r--r--drivers/ata/sata_promise.c15
-rw-r--r--drivers/ata/sata_sil24.c4
-rw-r--r--drivers/ata/sata_sx4.c14
-rw-r--r--drivers/base/core.c21
-rw-r--r--drivers/base/dma-mapping.c4
-rw-r--r--drivers/base/node.c8
-rw-r--r--drivers/bcma/Kconfig9
-rw-r--r--drivers/bcma/Makefile1
-rw-r--r--drivers/bcma/bcma_private.h10
-rw-r--r--drivers/bcma/driver_chipcommon.c81
-rw-r--r--drivers/bcma/driver_gpio.c98
-rw-r--r--drivers/bcma/main.c5
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c2
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c2
-rw-r--r--drivers/char/ppdev.c6
-rw-r--r--drivers/clk/Kconfig2
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/mvebu/Kconfig8
-rw-r--r--drivers/clk/mvebu/Makefile3
-rw-r--r--drivers/clk/mvebu/clk-core.c675
-rw-r--r--drivers/clk/mvebu/clk-core.h18
-rw-r--r--drivers/clk/mvebu/clk-cpu.c186
-rw-r--r--drivers/clk/mvebu/clk-cpu.h22
-rw-r--r--drivers/clk/mvebu/clk-gating-ctrl.c249
-rw-r--r--drivers/clk/mvebu/clk-gating-ctrl.h22
-rw-r--r--drivers/clk/mvebu/clk.c27
-rw-r--r--drivers/clk/spear/spear1310_clock.c1
-rw-r--r--drivers/clocksource/time-armada-370-xp.c11
-rw-r--r--drivers/crypto/Kconfig1
-rw-r--r--drivers/crypto/picoxcell_crypto.c1
-rw-r--r--drivers/crypto/s5p-sss.c2
-rw-r--r--drivers/crypto/talitos.c3
-rw-r--r--drivers/crypto/tegra-aes.c10
-rw-r--r--drivers/dma/mv_xor.c429
-rw-r--r--drivers/dma/mv_xor.h36
-rw-r--r--drivers/edac/Kconfig33
-rw-r--r--drivers/edac/Makefile5
-rw-r--r--drivers/edac/octeon_edac-l2c.c208
-rw-r--r--drivers/edac/octeon_edac-lmc.c186
-rw-r--r--drivers/edac/octeon_edac-pc.c143
-rw-r--r--drivers/edac/octeon_edac-pci.c111
-rw-r--r--drivers/eisa/eisa.ids4
-rw-r--r--drivers/firewire/init_ohci1394_dma.c4
-rw-r--r--drivers/firewire/net.c15
-rw-r--r--drivers/firewire/ohci.c2
-rw-r--r--drivers/firewire/sbp2.c2
-rw-r--r--drivers/firmware/efivars.c512
-rw-r--r--drivers/gpio/Kconfig2
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c2
-rw-r--r--drivers/gpu/drm/radeon/ni.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_ring.c4
-rw-r--r--drivers/gpu/drm/radeon/si.c6
-rw-r--r--drivers/gpu/vga/Kconfig2
-rw-r--r--drivers/hid/Kconfig11
-rw-r--r--drivers/hid/Makefile6
-rw-r--r--drivers/hid/hid-apple.c3
-rw-r--r--drivers/hid/hid-core.c61
-rw-r--r--drivers/hid/hid-icade.c259
-rw-r--r--drivers/hid/hid-ids.h19
-rw-r--r--drivers/hid/hid-input.c97
-rw-r--r--drivers/hid/hid-multitouch.c109
-rw-r--r--drivers/hid/hid-picolcd_cir.c2
-rw-r--r--drivers/hid/hid-roccat-isku.c44
-rw-r--r--drivers/hid/hid-roccat-isku.h78
-rw-r--r--drivers/hid/hid-roccat-koneplus.c348
-rw-r--r--drivers/hid/hid-roccat-koneplus.h101
-rw-r--r--drivers/hid/hid-roccat-kovaplus.c237
-rw-r--r--drivers/hid/hid-roccat-kovaplus.h16
-rw-r--r--drivers/hid/hid-roccat-lua.c227
-rw-r--r--drivers/hid/hid-roccat-lua.h29
-rw-r--r--drivers/hid/hid-roccat-pyra.c342
-rw-r--r--drivers/hid/hid-roccat-pyra.h24
-rw-r--r--drivers/hid/hid-roccat-savu.c4
-rw-r--r--drivers/hid/hid-sensor-hub.c36
-rw-r--r--drivers/hid/hidraw.c16
-rw-r--r--drivers/hid/i2c-hid/Kconfig18
-rw-r--r--drivers/hid/i2c-hid/Makefile5
-rw-r--r--drivers/hid/i2c-hid/i2c-hid.c979
-rw-r--r--drivers/hid/usbhid/hid-quirks.c3
-rw-r--r--drivers/hid/usbhid/hiddev.c10
-rw-r--r--drivers/i2c/busses/i2c-nuc900.c2
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c4
-rw-r--r--drivers/infiniband/core/cma.c9
-rw-r--r--drivers/infiniband/hw/amso1100/c2_ae.c1
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_cm.c6
-rw-r--r--drivers/infiniband/hw/cxgb4/cm.c6
-rw-r--r--drivers/infiniband/hw/ipath/ipath_init_chip.c10
-rw-r--r--drivers/infiniband/hw/mlx4/cm.c4
-rw-r--r--drivers/infiniband/hw/mlx4/cq.c34
-rw-r--r--drivers/infiniband/hw/mlx4/main.c27
-rw-r--r--drivers/infiniband/hw/mlx4/mlx4_ib.h1
-rw-r--r--drivers/infiniband/hw/mlx4/user.h12
-rw-r--r--drivers/infiniband/hw/nes/nes.h1
-rw-r--r--drivers/infiniband/hw/nes/nes_cm.c32
-rw-r--r--drivers/infiniband/hw/nes/nes_hw.c9
-rw-r--r--drivers/infiniband/hw/nes/nes_mgt.c42
-rw-r--r--drivers/infiniband/hw/nes/nes_nic.c13
-rw-r--r--drivers/infiniband/hw/nes/nes_verbs.c9
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c314
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.h11
-rw-r--r--drivers/input/input-mt.c2
-rw-r--r--drivers/input/misc/Kconfig10
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/arizona-haptics.c255
-rw-r--r--drivers/irqchip/Makefile7
-rw-r--r--drivers/irqchip/spear-shirq.c316
-rw-r--r--drivers/isdn/hisax/amd7930_fn.c2
-rw-r--r--drivers/isdn/hisax/isar.c2
-rw-r--r--drivers/leds/led-class.c2
-rw-r--r--drivers/leds/led-triggers.c25
-rw-r--r--drivers/leds/leds-88pm860x.c9
-rw-r--r--drivers/leds/leds-adp5520.c4
-rw-r--r--drivers/leds/leds-bd2802.c10
-rw-r--r--drivers/leds/leds-clevo-mail.c11
-rw-r--r--drivers/leds/leds-cobalt-qube.c11
-rw-r--r--drivers/leds/leds-cobalt-raq.c11
-rw-r--r--drivers/leds/leds-da903x.c10
-rw-r--r--drivers/leds/leds-fsg.c15
-rw-r--r--drivers/leds/leds-gpio.c37
-rw-r--r--drivers/leds/leds-lm355x.c4
-rw-r--r--drivers/leds/leds-lm3642.c12
-rw-r--r--drivers/leds/leds-lp3944.c2
-rw-r--r--drivers/leds/leds-lp5521.c13
-rw-r--r--drivers/leds/leds-lp5523.c24
-rw-r--r--drivers/leds/leds-lt3593.c20
-rw-r--r--drivers/leds/leds-net48xx.c2
-rw-r--r--drivers/leds/leds-netxbig.c2
-rw-r--r--drivers/leds/leds-ns2.c36
-rw-r--r--drivers/leds/leds-pca955x.c2
-rw-r--r--drivers/leds/leds-pwm.c2
-rw-r--r--drivers/leds/leds-rb532.c2
-rw-r--r--drivers/leds/leds-renesas-tpu.c25
-rw-r--r--drivers/leds/leds-ss4200.c2
-rw-r--r--drivers/leds/leds-wm8350.c4
-rw-r--r--drivers/leds/leds-wrap.c2
-rw-r--r--drivers/leds/ledtrig-backlight.c4
-rw-r--r--drivers/leds/ledtrig-gpio.c2
-rw-r--r--drivers/md/md.c2
-rw-r--r--drivers/md/persistent-data/dm-block-manager.c4
-rw-r--r--drivers/md/persistent-data/dm-btree.h2
-rw-r--r--drivers/md/raid5.c2
-rw-r--r--drivers/media/common/Kconfig7
-rw-r--r--drivers/media/common/b2c2/Kconfig5
-rw-r--r--drivers/media/common/siano/Kconfig18
-rw-r--r--drivers/media/common/siano/Makefile6
-rw-r--r--drivers/media/common/siano/smscoreapi.c2
-rw-r--r--drivers/media/common/siano/smsir.c2
-rw-r--r--drivers/media/common/siano/smsir.h9
-rw-r--r--drivers/media/dvb-core/dmxdev.c2
-rw-r--r--drivers/media/dvb-core/dmxdev.h1
-rw-r--r--drivers/media/dvb-core/dvb-usb-ids.h1
-rw-r--r--drivers/media/dvb-core/dvb_frontend.c10
-rw-r--r--drivers/media/dvb-frontends/cx22700.c4
-rw-r--r--drivers/media/dvb-frontends/cx24123.c2
-rw-r--r--drivers/media/dvb-frontends/dib9000.h2
-rw-r--r--drivers/media/dvb-frontends/drxd_hard.c8
-rw-r--r--drivers/media/dvb-frontends/drxk_hard.c26
-rw-r--r--drivers/media/dvb-frontends/drxk_hard.h6
-rw-r--r--drivers/media/dvb-frontends/ds3000.c15
-rw-r--r--drivers/media/dvb-frontends/l64781.c4
-rw-r--r--drivers/media/dvb-frontends/mt312.c4
-rw-r--r--drivers/media/dvb-frontends/rtl2830.c6
-rw-r--r--drivers/media/dvb-frontends/rtl2832.c6
-rw-r--r--drivers/media/dvb-frontends/stb0899_drv.c2
-rw-r--r--drivers/media/dvb-frontends/stv0367.c19
-rw-r--r--drivers/media/dvb-frontends/tda10071.c6
-rw-r--r--drivers/media/dvb-frontends/tda18271c2dd.c1
-rw-r--r--drivers/media/firewire/firedtv.h1
-rw-r--r--drivers/media/i2c/adp1653.c4
-rw-r--r--drivers/media/i2c/adv7183.c13
-rw-r--r--drivers/media/i2c/adv7604.c16
-rw-r--r--drivers/media/i2c/cx25840/cx25840-core.c2
-rw-r--r--drivers/media/i2c/ir-kbd-i2c.c14
-rw-r--r--drivers/media/i2c/s5k4ecgx.c2
-rw-r--r--drivers/media/i2c/smiapp-pll.c219
-rw-r--r--drivers/media/i2c/smiapp-pll.h61
-rw-r--r--drivers/media/i2c/smiapp/smiapp-core.c74
-rw-r--r--drivers/media/i2c/smiapp/smiapp-limits.c2
-rw-r--r--drivers/media/i2c/smiapp/smiapp-limits.h2
-rw-r--r--drivers/media/i2c/smiapp/smiapp-quirk.c2
-rw-r--r--drivers/media/i2c/smiapp/smiapp-quirk.h2
-rw-r--r--drivers/media/i2c/smiapp/smiapp-reg-defs.h2
-rw-r--r--drivers/media/i2c/smiapp/smiapp-reg.h2
-rw-r--r--drivers/media/i2c/smiapp/smiapp-regs.c2
-rw-r--r--drivers/media/i2c/smiapp/smiapp-regs.h2
-rw-r--r--drivers/media/i2c/smiapp/smiapp.h2
-rw-r--r--drivers/media/i2c/soc_camera/mt9v022.c88
-rw-r--r--drivers/media/i2c/soc_camera/ov2640.c55
-rw-r--r--drivers/media/i2c/vs6624.c13
-rw-r--r--drivers/media/mmc/siano/Kconfig3
-rw-r--r--drivers/media/pci/bt8xx/bttv-driver.c7
-rw-r--r--drivers/media/pci/cx18/cx18-alsa-main.c2
-rw-r--r--drivers/media/pci/cx18/cx18-alsa-pcm.c1
-rw-r--r--drivers/media/pci/cx18/cx18-i2c.c2
-rw-r--r--drivers/media/pci/cx18/cx18-streams.c2
-rw-r--r--drivers/media/pci/cx23885/altera-ci.c45
-rw-r--r--drivers/media/pci/cx23885/cimax2.c17
-rw-r--r--drivers/media/pci/cx23885/cx23885-alsa.c6
-rw-r--r--drivers/media/pci/cx23885/cx23885-av.c1
-rw-r--r--drivers/media/pci/cx23885/cx23885-cards.c2
-rw-r--r--drivers/media/pci/cx23885/cx23885-core.c8
-rw-r--r--drivers/media/pci/cx23885/cx23885-dvb.c2
-rw-r--r--drivers/media/pci/cx23885/cx23885-f300.c1
-rw-r--r--drivers/media/pci/cx23885/cx23885-input.c7
-rw-r--r--drivers/media/pci/cx23885/cx23885-input.h2
-rw-r--r--drivers/media/pci/cx23885/cx23885-ioctl.c2
-rw-r--r--drivers/media/pci/cx23885/cx23885-ir.c1
-rw-r--r--drivers/media/pci/cx23885/cx23888-ir.c1
-rw-r--r--drivers/media/pci/cx23885/netup-init.c1
-rw-r--r--drivers/media/pci/cx25821/cx25821-audio-upstream.c44
-rw-r--r--drivers/media/pci/cx25821/cx25821-biffuncs.h6
-rw-r--r--drivers/media/pci/cx25821/cx25821-i2c.c4
-rw-r--r--drivers/media/pci/cx25821/cx25821-video-upstream-ch2.c54
-rw-r--r--drivers/media/pci/cx25821/cx25821-video-upstream.c47
-rw-r--r--drivers/media/pci/cx25821/cx25821-video.c8
-rw-r--r--drivers/media/pci/cx88/cx88-alsa.c14
-rw-r--r--drivers/media/pci/cx88/cx88-blackbird.c7
-rw-r--r--drivers/media/pci/cx88/cx88-core.c12
-rw-r--r--drivers/media/pci/cx88/cx88-input.c8
-rw-r--r--drivers/media/pci/cx88/cx88-mpeg.c18
-rw-r--r--drivers/media/pci/cx88/cx88.h4
-rw-r--r--drivers/media/pci/dm1105/dm1105.c8
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-main.c2
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-pcm.c6
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-pcm.h4
-rw-r--r--drivers/media/pci/ivtv/ivtv-firmware.c2
-rw-r--r--drivers/media/pci/ivtv/ivtv-i2c.c8
-rw-r--r--drivers/media/pci/ivtv/ivtv-ioctl.c4
-rw-r--r--drivers/media/pci/mantis/mantis_input.c5
-rw-r--r--drivers/media/pci/mantis/mantis_uart.c2
-rw-r--r--drivers/media/pci/mantis/mantis_vp1033.c6
-rw-r--r--drivers/media/pci/meye/meye.c2
-rw-r--r--drivers/media/pci/ngene/ngene-cards.c4
-rw-r--r--drivers/media/pci/ngene/ngene-core.c7
-rw-r--r--drivers/media/pci/saa7134/saa7134-core.c3
-rw-r--r--drivers/media/pci/saa7134/saa7134-input.c2
-rw-r--r--drivers/media/pci/saa7134/saa7134-video.c2
-rw-r--r--drivers/media/pci/saa7164/saa7164-api.c26
-rw-r--r--drivers/media/pci/saa7164/saa7164-bus.c6
-rw-r--r--drivers/media/pci/saa7164/saa7164-cmd.c16
-rw-r--r--drivers/media/pci/saa7164/saa7164-core.c4
-rw-r--r--drivers/media/pci/saa7164/saa7164-encoder.c15
-rw-r--r--drivers/media/pci/saa7164/saa7164-fw.c8
-rw-r--r--drivers/media/pci/saa7164/saa7164-vbi.c6
-rw-r--r--drivers/media/pci/ttpci/av7110.h1
-rw-r--r--drivers/media/pci/ttpci/budget-av.c4
-rw-r--r--drivers/media/platform/Kconfig12
-rw-r--r--drivers/media/platform/Makefile1
-rw-r--r--drivers/media/platform/blackfin/bfin_capture.c14
-rw-r--r--drivers/media/platform/coda.c4
-rw-r--r--drivers/media/platform/davinci/Kconfig2
-rw-r--r--drivers/media/platform/davinci/dm355_ccdc.c8
-rw-r--r--drivers/media/platform/davinci/dm644x_ccdc.c16
-rw-r--r--drivers/media/platform/davinci/isif.c5
-rw-r--r--drivers/media/platform/davinci/vpbe.c10
-rw-r--r--drivers/media/platform/davinci/vpbe_display.c303
-rw-r--r--drivers/media/platform/davinci/vpbe_osd.c9
-rw-r--r--drivers/media/platform/davinci/vpif.c8
-rw-r--r--drivers/media/platform/davinci/vpif_capture.c34
-rw-r--r--drivers/media/platform/davinci/vpif_display.c28
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-core.c2
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-m2m.c2
-rw-r--r--drivers/media/platform/fsl-viu.c6
-rw-r--r--drivers/media/platform/m2m-deinterlace.c24
-rw-r--r--drivers/media/platform/mem2mem_testdev.c4
-rw-r--r--drivers/media/platform/mx2_emmaprp.c16
-rw-r--r--drivers/media/platform/omap/omap_vout.c57
-rw-r--r--drivers/media/platform/omap/omap_voutlib.c38
-rw-r--r--drivers/media/platform/omap/omap_voutlib.h3
-rw-r--r--drivers/media/platform/omap3isp/isp.c83
-rw-r--r--drivers/media/platform/omap3isp/isp.h5
-rw-r--r--drivers/media/platform/omap3isp/ispcsi2.c6
-rw-r--r--drivers/media/platform/omap3isp/ispcsiphy.c227
-rw-r--r--drivers/media/platform/omap3isp/ispcsiphy.h10
-rw-r--r--drivers/media/platform/omap3isp/isphist.c8
-rw-r--r--drivers/media/platform/omap3isp/isppreview.c41
-rw-r--r--drivers/media/platform/omap3isp/ispreg.h99
-rw-r--r--drivers/media/platform/omap3isp/ispstat.c5
-rw-r--r--drivers/media/platform/omap3isp/ispstat.h2
-rw-r--r--drivers/media/platform/omap3isp/ispvideo.c3
-rw-r--r--drivers/media/platform/s3c-camif/Makefile5
-rw-r--r--drivers/media/platform/s3c-camif/camif-capture.c1672
-rw-r--r--drivers/media/platform/s3c-camif/camif-core.c662
-rw-r--r--drivers/media/platform/s3c-camif/camif-core.h393
-rw-r--r--drivers/media/platform/s3c-camif/camif-regs.c606
-rw-r--r--drivers/media/platform/s3c-camif/camif-regs.h269
-rw-r--r--drivers/media/platform/s5p-fimc/fimc-capture.c11
-rw-r--r--drivers/media/platform/s5p-fimc/fimc-m2m.c16
-rw-r--r--drivers/media/platform/s5p-fimc/fimc-mdevice.c4
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_dec.c14
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_enc.c16
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_pm.c2
-rw-r--r--drivers/media/platform/s5p-tv/mixer_video.c13
-rw-r--r--drivers/media/platform/soc_camera/Kconfig1
-rw-r--r--drivers/media/platform/soc_camera/soc_camera.c14
-rw-r--r--drivers/media/platform/vivi.c8
-rw-r--r--drivers/media/radio/radio-aimslab.c2
-rw-r--r--drivers/media/radio/radio-cadet.c3
-rw-r--r--drivers/media/radio/radio-isa.c10
-rw-r--r--drivers/media/radio/radio-sf16fmi.c2
-rw-r--r--drivers/media/radio/radio-tea5764.c4
-rw-r--r--drivers/media/radio/si4713-i2c.c2
-rw-r--r--drivers/media/radio/wl128x/fmdrv.h2
-rw-r--r--drivers/media/radio/wl128x/fmdrv_common.c2
-rw-r--r--drivers/media/radio/wl128x/fmdrv_rx.c2
-rw-r--r--drivers/media/rc/ati_remote.c2
-rw-r--r--drivers/media/rc/ene_ir.c33
-rw-r--r--drivers/media/rc/fintek-cir.c6
-rw-r--r--drivers/media/rc/gpio-ir-recv.c2
-rw-r--r--drivers/media/rc/iguanair.c2
-rw-r--r--drivers/media/rc/imon.c40
-rw-r--r--drivers/media/rc/ir-jvc-decoder.c4
-rw-r--r--drivers/media/rc/ir-lirc-codec.c4
-rw-r--r--drivers/media/rc/ir-mce_kbd-decoder.c4
-rw-r--r--drivers/media/rc/ir-nec-decoder.c4
-rw-r--r--drivers/media/rc/ir-rc5-decoder.c14
-rw-r--r--drivers/media/rc/ir-rc5-sz-decoder.c6
-rw-r--r--drivers/media/rc/ir-rc6-decoder.c8
-rw-r--r--drivers/media/rc/ir-rx51.c13
-rw-r--r--drivers/media/rc/ir-sanyo-decoder.c4
-rw-r--r--drivers/media/rc/ir-sony-decoder.c17
-rw-r--r--drivers/media/rc/ite-cir.c6
-rw-r--r--drivers/media/rc/keymaps/rc-imon-mce.c2
-rw-r--r--drivers/media/rc/keymaps/rc-rc6-mce.c2
-rw-r--r--drivers/media/rc/mceusb.c2
-rw-r--r--drivers/media/rc/nuvoton-cir.c13
-rw-r--r--drivers/media/rc/nuvoton-cir.h1
-rw-r--r--drivers/media/rc/rc-loopback.c2
-rw-r--r--drivers/media/rc/rc-main.c73
-rw-r--r--drivers/media/rc/redrat3.c2
-rw-r--r--drivers/media/rc/streamzap.c2
-rw-r--r--drivers/media/rc/ttusbir.c2
-rw-r--r--drivers/media/rc/winbond-cir.c113
-rw-r--r--drivers/media/tuners/fc2580.c61
-rw-r--r--drivers/media/tuners/max2165.c2
-rw-r--r--drivers/media/tuners/tua9001.c2
-rw-r--r--drivers/media/tuners/xc4000.c2
-rw-r--r--drivers/media/usb/au0828/au0828-cards.c2
-rw-r--r--drivers/media/usb/au0828/au0828-dvb.c5
-rw-r--r--drivers/media/usb/au0828/au0828-video.c16
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-avcore.c9
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-cards.c8
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-i2c.c4
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-input.c2
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9015.c2
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9035.c4
-rw-r--r--drivers/media/usb/dvb-usb-v2/anysee.c4
-rw-r--r--drivers/media/usb/dvb-usb-v2/az6007.c2
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb.h2
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb_core.c14
-rw-r--r--drivers/media/usb/dvb-usb-v2/it913x.c12
-rw-r--r--drivers/media/usb/dvb-usb-v2/lmedm04.c4
-rw-r--r--drivers/media/usb/dvb-usb-v2/rtl28xxu.c6
-rw-r--r--drivers/media/usb/dvb-usb-v2/usb_urb.c8
-rw-r--r--drivers/media/usb/dvb-usb/az6027.c11
-rw-r--r--drivers/media/usb/dvb-usb/dib0700.h2
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_core.c16
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_devices.c146
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb.h2
-rw-r--r--drivers/media/usb/dvb-usb/pctv452e.c4
-rw-r--r--drivers/media/usb/dvb-usb/technisat-usb2.c2
-rw-r--r--drivers/media/usb/dvb-usb/ttusb2.c2
-rw-r--r--drivers/media/usb/dvb-usb/vp702x.c8
-rw-r--r--drivers/media/usb/em28xx/em28xx-cards.c15
-rw-r--r--drivers/media/usb/em28xx/em28xx-dvb.c84
-rw-r--r--drivers/media/usb/em28xx/em28xx-input.c16
-rw-r--r--drivers/media/usb/em28xx/em28xx.h1
-rw-r--r--drivers/media/usb/gspca/gspca.c3
-rw-r--r--drivers/media/usb/gspca/gspca.h2
-rw-r--r--drivers/media/usb/gspca/jeilinj.c6
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_s5k4aa.c6
-rw-r--r--drivers/media/usb/gspca/pac7302.c62
-rw-r--r--drivers/media/usb/gspca/sonixb.c1
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-core.c2
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-i2c.c2
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-hdw.c6
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c4
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-v4l2.c4
-rw-r--r--drivers/media/usb/pwc/pwc-ctrl.c2
-rw-r--r--drivers/media/usb/pwc/pwc-if.c8
-rw-r--r--drivers/media/usb/s2255/s2255drv.c2
-rw-r--r--drivers/media/usb/siano/Kconfig3
-rw-r--r--drivers/media/usb/sn9c102/sn9c102_core.c2
-rw-r--r--drivers/media/usb/stk1160/stk1160-i2c.c2
-rw-r--r--drivers/media/usb/stk1160/stk1160-video.c23
-rw-r--r--drivers/media/usb/stk1160/stk1160.h5
-rw-r--r--drivers/media/usb/stkwebcam/stk-webcam.c5
-rw-r--r--drivers/media/usb/tlg2300/pd-dvb.c1
-rw-r--r--drivers/media/usb/tlg2300/pd-video.c4
-rw-r--r--drivers/media/usb/tm6000/tm6000-input.c20
-rw-r--r--drivers/media/usb/tm6000/tm6000-video.c1
-rw-r--r--drivers/media/usb/usbvision/usbvision.h2
-rw-r--r--drivers/media/usb/uvc/uvc_ctrl.c29
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c10
-rw-r--r--drivers/media/usb/uvc/uvc_entity.c2
-rw-r--r--drivers/media/usb/uvc/uvc_queue.c2
-rw-r--r--drivers/media/usb/uvc/uvc_v4l2.c89
-rw-r--r--drivers/media/usb/uvc/uvc_video.c1
-rw-r--r--drivers/media/usb/uvc/uvcvideo.h8
-rw-r--r--drivers/media/usb/zr364xx/zr364xx.c3
-rw-r--r--drivers/media/v4l2-core/Kconfig3
-rw-r--r--drivers/media/v4l2-core/v4l2-common.c3
-rw-r--r--drivers/media/v4l2-core/v4l2-compat-ioctl32.c19
-rw-r--r--drivers/media/v4l2-core/v4l2-dev.c1
-rw-r--r--drivers/media/v4l2-core/v4l2-event.c2
-rw-r--r--drivers/media/v4l2-core/v4l2-fh.c2
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c11
-rw-r--r--drivers/media/v4l2-core/v4l2-mem2mem.c19
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c22
-rw-r--r--drivers/media/v4l2-core/videobuf-core.c4
-rw-r--r--drivers/media/v4l2-core/videobuf2-core.c300
-rw-r--r--drivers/media/v4l2-core/videobuf2-dma-contig.c700
-rw-r--r--drivers/media/v4l2-core/videobuf2-memops.c40
-rw-r--r--drivers/media/v4l2-core/videobuf2-vmalloc.c56
-rw-r--r--drivers/message/i2o/README.ioctl12
-rw-r--r--drivers/message/i2o/i2o_block.c2
-rw-r--r--drivers/message/i2o/i2o_config.c2
-rw-r--r--drivers/mfd/Kconfig1
-rw-r--r--drivers/mfd/ab8500-core.c20
-rw-r--r--drivers/mfd/arizona-core.c2
-rw-r--r--drivers/mfd/max8997.c73
-rw-r--r--drivers/mfd/tps6586x.c76
-rw-r--r--drivers/mfd/wm5102-tables.c3
-rw-r--r--drivers/mfd/wm8994-core.c35
-rw-r--r--drivers/misc/atmel-ssc.c135
-rw-r--r--drivers/mmc/host/Kconfig2
-rw-r--r--drivers/mtd/maps/plat-ram.c2
-rw-r--r--drivers/mtd/nand/s3c2410.c2
-rw-r--r--drivers/net/bonding/bonding.h2
-rw-r--r--drivers/net/ethernet/8390/ax88796.c2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/t3_hw.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c10
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/sge.c6
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_hw.c20
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_msg.h1
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_regs.h36
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h41
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/sge.c8
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_ethtool.c2
-rw-r--r--drivers/net/ethernet/marvell/Kconfig24
-rw-r--r--drivers/net/ethernet/marvell/Makefile2
-rw-r--r--drivers/net/ethernet/marvell/mvmdio.c228
-rw-r--r--drivers/net/ethernet/marvell/mvneta.c2848
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/cmd.c11
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_cq.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_netdev.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_rx.c5
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_tx.c5
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/eq.c38
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/fw.c30
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/fw.h1
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/main.c38
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4_en.h1
-rw-r--r--drivers/net/ethernet/micrel/ksz884x.c2
-rw-r--r--drivers/net/ethernet/myricom/myri10ge/myri10ge_mcp_gen_header.h2
-rw-r--r--drivers/net/sungem_phy.c8
-rw-r--r--drivers/net/tun.c7
-rw-r--r--drivers/net/wimax/i2400m/debugfs.c1
-rw-r--r--drivers/net/wireless/ath/ath9k/pci.c8
-rw-r--r--drivers/net/wireless/atmel.c2
-rw-r--r--drivers/net/wireless/ipw2x00/ipw2100.h2
-rw-r--r--drivers/net/wireless/iwlegacy/4965.h4
-rw-r--r--drivers/net/wireless/iwlegacy/common.c10
-rw-r--r--drivers/net/wireless/iwlegacy/common.h12
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/trans.c8
-rw-r--r--drivers/net/wireless/zd1211rw/zd_chip.c2
-rw-r--r--drivers/of/Kconfig2
-rw-r--r--drivers/of/base.c2
-rw-r--r--drivers/pci/bus.c5
-rw-r--r--drivers/pci/hotplug/Kconfig11
-rw-r--r--drivers/pci/hotplug/Makefile1
-rw-r--r--drivers/pci/hotplug/s390_pci_hpc.c252
-rw-r--r--drivers/pci/ioapic.c2
-rw-r--r--drivers/pci/iov.c87
-rw-r--r--drivers/pci/irq.c10
-rw-r--r--drivers/pci/msi.c6
-rw-r--r--drivers/pci/pci-driver.c73
-rw-r--r--drivers/pci/pci-stub.c2
-rw-r--r--drivers/pci/pci-sysfs.c172
-rw-r--r--drivers/pci/pci.c48
-rw-r--r--drivers/pci/pci.h8
-rw-r--r--drivers/pci/pcie/aer/aerdrv.h5
-rw-r--r--drivers/pci/pcie/aer/aerdrv_core.c22
-rw-r--r--drivers/pci/pcie/aspm.c18
-rw-r--r--drivers/pci/pcie/portdrv_core.c3
-rw-r--r--drivers/pci/probe.c42
-rw-r--r--drivers/pci/quirks.c39
-rw-r--r--drivers/pci/remove.c36
-rw-r--r--drivers/pci/rom.c11
-rw-r--r--drivers/pci/setup-bus.c22
-rw-r--r--drivers/pci/xen-pcifront.c5
-rw-r--r--drivers/power/Kconfig24
-rw-r--r--drivers/power/Makefile4
-rw-r--r--drivers/power/ab8500_bmdata.c521
-rw-r--r--drivers/power/ab8500_btemp.c77
-rw-r--r--drivers/power/ab8500_charger.c84
-rw-r--r--drivers/power/ab8500_fg.c82
-rw-r--r--drivers/power/abx500_chargalg.c56
-rw-r--r--drivers/power/bq2415x_charger.c1670
-rw-r--r--drivers/power/bq27x00_battery.c8
-rw-r--r--drivers/power/ds2782_battery.c4
-rw-r--r--drivers/power/generic-adc-battery.c5
-rw-r--r--drivers/power/jz4740-battery.c45
-rw-r--r--drivers/power/lp8788-charger.c75
-rw-r--r--drivers/power/max17042_battery.c3
-rw-r--r--drivers/power/max8925_power.c51
-rw-r--r--drivers/power/power_supply_core.c96
-rw-r--r--drivers/power/power_supply_sysfs.c2
-rw-r--r--drivers/power/rx51_battery.c251
-rw-r--r--drivers/power/twl4030_charger.c12
-rw-r--r--drivers/regulator/88pm8607.c6
-rw-r--r--drivers/regulator/Kconfig54
-rw-r--r--drivers/regulator/Makefile6
-rw-r--r--drivers/regulator/aat2870-regulator.c4
-rw-r--r--drivers/regulator/ab3100.c6
-rw-r--r--drivers/regulator/ab8500.c12
-rw-r--r--drivers/regulator/ad5398.c6
-rw-r--r--drivers/regulator/anatop-regulator.c34
-rw-r--r--drivers/regulator/arizona-ldo1.c136
-rw-r--r--drivers/regulator/arizona-micsupp.c8
-rw-r--r--drivers/regulator/as3711-regulator.c369
-rw-r--r--drivers/regulator/core.c42
-rw-r--r--drivers/regulator/da903x.c6
-rw-r--r--drivers/regulator/da9052-regulator.c16
-rw-r--r--drivers/regulator/da9055-regulator.c641
-rw-r--r--drivers/regulator/db8500-prcmu.c6
-rw-r--r--drivers/regulator/dbx500-prcmu.c4
-rw-r--r--drivers/regulator/dummy.c2
-rw-r--r--drivers/regulator/fan53555.c6
-rw-r--r--drivers/regulator/fixed.c6
-rw-r--r--drivers/regulator/gpio-regulator.c112
-rw-r--r--drivers/regulator/isl6271a-regulator.c6
-rw-r--r--drivers/regulator/lp3971.c8
-rw-r--r--drivers/regulator/lp3972.c8
-rw-r--r--drivers/regulator/lp872x.c4
-rw-r--r--drivers/regulator/lp8788-buck.c24
-rw-r--r--drivers/regulator/lp8788-ldo.c25
-rw-r--r--drivers/regulator/max1586.c50
-rw-r--r--drivers/regulator/max77686.c170
-rw-r--r--drivers/regulator/max8649.c6
-rw-r--r--drivers/regulator/max8660.c6
-rw-r--r--drivers/regulator/max8907-regulator.c6
-rw-r--r--drivers/regulator/max8925-regulator.c78
-rw-r--r--drivers/regulator/max8952.c6
-rw-r--r--drivers/regulator/max8973-regulator.c505
-rw-r--r--drivers/regulator/max8997.c187
-rw-r--r--drivers/regulator/max8998.c6
-rw-r--r--drivers/regulator/mc13783-regulator.c6
-rw-r--r--drivers/regulator/mc13892-regulator.c6
-rw-r--r--drivers/regulator/mc13xxx-regulator-core.c4
-rw-r--r--drivers/regulator/palmas-regulator.c166
-rw-r--r--drivers/regulator/pcap-regulator.c6
-rw-r--r--drivers/regulator/pcf50633-regulator.c182
-rw-r--r--drivers/regulator/rc5t583-regulator.c6
-rw-r--r--drivers/regulator/s2mps11.c16
-rw-r--r--drivers/regulator/s5m8767.c46
-rw-r--r--drivers/regulator/tps51632-regulator.c342
-rw-r--r--drivers/regulator/tps6105x-regulator.c6
-rw-r--r--drivers/regulator/tps62360-regulator.c8
-rw-r--r--drivers/regulator/tps65023-regulator.c6
-rw-r--r--drivers/regulator/tps6507x-regulator.c6
-rw-r--r--drivers/regulator/tps65090-regulator.c254
-rw-r--r--drivers/regulator/tps65217-regulator.c6
-rw-r--r--drivers/regulator/tps6524x-regulator.c4
-rw-r--r--drivers/regulator/tps6586x-regulator.c189
-rw-r--r--drivers/regulator/tps65910-regulator.c13
-rw-r--r--drivers/regulator/tps65912-regulator.c6
-rw-r--r--drivers/regulator/tps80031-regulator.c788
-rw-r--r--drivers/regulator/twl-regulator.c6
-rw-r--r--drivers/regulator/vexpress.c147
-rw-r--r--drivers/regulator/virtual.c6
-rw-r--r--drivers/regulator/wm831x-dcdc.c31
-rw-r--r--drivers/regulator/wm831x-isink.c6
-rw-r--r--drivers/regulator/wm831x-ldo.c18
-rw-r--r--drivers/regulator/wm8400-regulator.c6
-rw-r--r--drivers/regulator/wm8994-regulator.c6
-rw-r--r--drivers/remoteproc/remoteproc_elf_loader.c4
-rw-r--r--drivers/rtc/rtc-isl1208.c2
-rw-r--r--drivers/rtc/rtc-s3c.c2
-rw-r--r--drivers/s390/block/dasd.c97
-rw-r--r--drivers/s390/block/dasd_devmap.c36
-rw-r--r--drivers/s390/block/dasd_eckd.c92
-rw-r--r--drivers/s390/block/dasd_fba.c23
-rw-r--r--drivers/s390/block/dasd_int.h2
-rw-r--r--drivers/s390/block/dasd_ioctl.c11
-rw-r--r--drivers/s390/char/sclp.h3
-rw-r--r--drivers/s390/char/sclp_cmd.c81
-rw-r--r--drivers/s390/cio/ccwgroup.c26
-rw-r--r--drivers/s390/cio/chsc.c156
-rw-r--r--drivers/s390/cio/device.c11
-rw-r--r--drivers/s390/cio/device.h2
-rw-r--r--drivers/s390/cio/device_ops.c17
-rw-r--r--drivers/s390/cio/device_pgid.c10
-rw-r--r--drivers/s390/cio/qdio_main.c52
-rw-r--r--drivers/s390/cio/qdio_setup.c9
-rw-r--r--drivers/s390/cio/qdio_thinint.c2
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype50.c68
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype50.h2
-rw-r--r--drivers/scsi/Kconfig2
-rw-r--r--drivers/scsi/Makefile2
-rw-r--r--drivers/scsi/aacraid/aachba.c87
-rw-r--r--drivers/scsi/aacraid/aacraid.h2
-rw-r--r--drivers/scsi/be2iscsi/be.h7
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.c236
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.h93
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.c124
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.h2
-rw-r--r--drivers/scsi/be2iscsi/be_main.c1059
-rw-r--r--drivers/scsi/be2iscsi/be_main.h152
-rw-r--r--drivers/scsi/be2iscsi/be_mgmt.c424
-rw-r--r--drivers/scsi/be2iscsi/be_mgmt.h23
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_hwi.c2
-rw-r--r--drivers/scsi/bnx2i/bnx2i.h2
-rw-r--r--drivers/scsi/bnx2i/bnx2i_init.c43
-rw-r--r--drivers/scsi/bnx2i/bnx2i_iscsi.c2
-rw-r--r--drivers/scsi/csiostor/Kconfig19
-rw-r--r--drivers/scsi/csiostor/Makefile11
-rw-r--r--drivers/scsi/csiostor/csio_attr.c796
-rw-r--r--drivers/scsi/csiostor/csio_defs.h121
-rw-r--r--drivers/scsi/csiostor/csio_hw.c4395
-rw-r--r--drivers/scsi/csiostor/csio_hw.h665
-rw-r--r--drivers/scsi/csiostor/csio_init.c1274
-rw-r--r--drivers/scsi/csiostor/csio_init.h158
-rw-r--r--drivers/scsi/csiostor/csio_isr.c624
-rw-r--r--drivers/scsi/csiostor/csio_lnode.c2135
-rw-r--r--drivers/scsi/csiostor/csio_lnode.h255
-rw-r--r--drivers/scsi/csiostor/csio_mb.c1750
-rw-r--r--drivers/scsi/csiostor/csio_mb.h278
-rw-r--r--drivers/scsi/csiostor/csio_rnode.c913
-rw-r--r--drivers/scsi/csiostor/csio_rnode.h141
-rw-r--r--drivers/scsi/csiostor/csio_scsi.c2555
-rw-r--r--drivers/scsi/csiostor/csio_scsi.h342
-rw-r--r--drivers/scsi/csiostor/csio_wr.c1632
-rw-r--r--drivers/scsi/csiostor/csio_wr.h512
-rw-r--r--drivers/scsi/csiostor/t4fw_api_stor.h578
-rw-r--r--drivers/scsi/hptiop.c413
-rw-r--r--drivers/scsi/hptiop.h72
-rw-r--r--drivers/scsi/libsas/sas_expander.c2
-rw-r--r--drivers/scsi/lpfc/lpfc.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c85
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h1
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c2
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c8
-rw-r--r--drivers/scsi/lpfc/lpfc_hw4.h3
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c57
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c19
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c16
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.h4
-rw-r--r--drivers/scsi/lpfc/lpfc_sli4.h3
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h2
-rw-r--r--drivers/scsi/mpt3sas/Kconfig67
-rw-r--r--drivers/scsi/mpt3sas/Makefile8
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2.h1164
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h3323
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_init.h560
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_ioc.h1665
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_raid.h346
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_sas.h295
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_tool.h437
-rw-r--r--drivers/scsi/mpt3sas/mpi/mpi2_type.h56
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_base.c4840
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_base.h1139
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_config.c1650
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_ctl.c3297
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_ctl.h418
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_debug.h219
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_scsih.c8167
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_transport.c2128
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c434
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_trigger_diag.h193
-rw-r--r--drivers/scsi/mvsas/mv_94xx.h14
-rw-r--r--drivers/scsi/mvsas/mv_sas.h2
-rw-r--r--drivers/scsi/osd/osd_uld.c28
-rw-r--r--drivers/scsi/qla2xxx/qla_attr.c3
-rw-r--r--drivers/scsi/qla2xxx/qla_bsg.c72
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.c8
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h4
-rw-r--r--drivers/scsi/qla2xxx/qla_fw.h21
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h3
-rw-r--r--drivers/scsi/qla2xxx/qla_gs.c6
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c153
-rw-r--r--drivers/scsi/qla2xxx/qla_iocb.c15
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c42
-rw-r--r--drivers/scsi/qla2xxx/qla_mbx.c6
-rw-r--r--drivers/scsi/qla2xxx/qla_nx.c83
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c57
-rw-r--r--drivers/scsi/qla2xxx/qla_target.c14
-rw-r--r--drivers/scsi/qla2xxx/qla_version.h2
-rw-r--r--drivers/scsi/qla2xxx/tcm_qla2xxx.c2
-rw-r--r--drivers/scsi/scsi_pm.c98
-rw-r--r--drivers/scsi/scsi_sysfs.c11
-rw-r--r--drivers/scsi/scsi_transport_sas.c1
-rw-r--r--drivers/scsi/scsi_transport_srp.c51
-rw-r--r--drivers/scsi/sd.c35
-rw-r--r--drivers/scsi/virtio_scsi.c2
-rw-r--r--drivers/sh/clk/cpg.c1
-rw-r--r--drivers/spi/Kconfig31
-rw-r--r--drivers/spi/Makefile5
-rw-r--r--drivers/spi/spi-altera.c6
-rw-r--r--drivers/spi/spi-ath79.c6
-rw-r--r--drivers/spi/spi-atmel.c4
-rw-r--r--drivers/spi/spi-bcm63xx.c22
-rw-r--r--drivers/spi/spi-bfin-sport.c8
-rw-r--r--drivers/spi/spi-bfin5xx.c4
-rw-r--r--drivers/spi/spi-bitbang.c27
-rw-r--r--drivers/spi/spi-clps711x.c296
-rw-r--r--drivers/spi/spi-coldfire-qspi.c6
-rw-r--r--drivers/spi/spi-davinci.c6
-rw-r--r--drivers/spi/spi-dw-mmio.c6
-rw-r--r--drivers/spi/spi-dw-pci.c6
-rw-r--r--drivers/spi/spi-dw.c6
-rw-r--r--drivers/spi/spi-ep93xx.c6
-rw-r--r--drivers/spi/spi-falcon.c6
-rw-r--r--drivers/spi/spi-fsl-espi.c8
-rw-r--r--drivers/spi/spi-fsl-lib.c4
-rw-r--r--drivers/spi/spi-fsl-spi.c14
-rw-r--r--drivers/spi/spi-gpio.c13
-rw-r--r--drivers/spi/spi-imx.c6
-rw-r--r--drivers/spi/spi-mpc512x-psc.c10
-rw-r--r--drivers/spi/spi-mpc52xx-psc.c8
-rw-r--r--drivers/spi/spi-mpc52xx.c8
-rw-r--r--drivers/spi/spi-mxs.c6
-rw-r--r--drivers/spi/spi-nuc900.c6
-rw-r--r--drivers/spi/spi-oc-tiny.c10
-rw-r--r--drivers/spi/spi-octeon.c6
-rw-r--r--drivers/spi/spi-omap-100k.c2
-rw-r--r--drivers/spi/spi-omap2-mcspi.c76
-rw-r--r--drivers/spi/spi-orion.c27
-rw-r--r--drivers/spi/spi-pl022.c61
-rw-r--r--drivers/spi/spi-pxa2xx-pci.c6
-rw-r--r--drivers/spi/spi-pxa2xx.c4
-rw-r--r--drivers/spi/spi-rspi.c10
-rw-r--r--drivers/spi/spi-s3c24xx.c6
-rw-r--r--drivers/spi/spi-s3c64xx.c38
-rw-r--r--drivers/spi/spi-sh-hspi.c49
-rw-r--r--drivers/spi/spi-sh-msiof.c6
-rw-r--r--drivers/spi/spi-sh.c6
-rw-r--r--drivers/spi/spi-sirf.c6
-rw-r--r--drivers/spi/spi-stmp.c664
-rw-r--r--drivers/spi/spi-tegra20-sflash.c665
-rw-r--r--drivers/spi/spi-tegra20-slink.c1358
-rw-r--r--drivers/spi/spi-ti-ssp.c6
-rw-r--r--drivers/spi/spi-tle62x0.c6
-rw-r--r--drivers/spi/spi-topcliff-pch.c12
-rw-r--r--drivers/spi/spi-xcomm.c6
-rw-r--r--drivers/spi/spi-xilinx.c6
-rw-r--r--drivers/spi/spi.c101
-rw-r--r--drivers/spi/spidev.c16
-rw-r--r--drivers/ssb/Kconfig9
-rw-r--r--drivers/ssb/Makefile1
-rw-r--r--drivers/ssb/driver_chipcommon.c78
-rw-r--r--drivers/ssb/driver_extif.c43
-rw-r--r--drivers/ssb/driver_gpio.c176
-rw-r--r--drivers/ssb/main.c7
-rw-r--r--drivers/ssb/ssb_private.h17
-rw-r--r--drivers/staging/comedi/drivers/usbdux.c2
-rw-r--r--drivers/staging/media/dt3155v4l/dt3155v4l.c4
-rw-r--r--drivers/staging/media/go7007/go7007-fw.c42
-rw-r--r--drivers/staging/media/go7007/go7007-v4l2.c2
-rw-r--r--drivers/staging/media/go7007/s2250-board.c13
-rw-r--r--drivers/staging/media/go7007/wis-ov7640.c20
-rw-r--r--drivers/staging/media/go7007/wis-saa7113.c20
-rw-r--r--drivers/staging/media/go7007/wis-saa7115.c20
-rw-r--r--drivers/staging/media/go7007/wis-sony-tuner.c13
-rw-r--r--drivers/staging/media/go7007/wis-tw2804.c13
-rw-r--r--drivers/staging/media/go7007/wis-tw9903.c13
-rw-r--r--drivers/staging/media/go7007/wis-uda1342.c13
-rw-r--r--drivers/staging/media/lirc/lirc_serial.c6
-rw-r--r--drivers/staging/omapdrm/omap_drv.c11
-rw-r--r--drivers/staging/tidspbridge/rmgr/node.c2
-rw-r--r--drivers/target/iscsi/iscsi_target_configfs.c2
-rw-r--r--drivers/target/iscsi/iscsi_target_erl0.c6
-rw-r--r--drivers/target/iscsi/iscsi_target_parameters.c1
-rw-r--r--drivers/target/iscsi/iscsi_target_util.c2
-rw-r--r--drivers/thermal/Kconfig2
-rw-r--r--drivers/tty/Kconfig1
-rw-r--r--drivers/tty/hvc/hvc_opal.c2
-rw-r--r--drivers/tty/hvc/hvc_vio.c2
-rw-r--r--drivers/tty/ipwireless/setup_protocol.h2
-rw-r--r--drivers/tty/serial/8250/Kconfig2
-rw-r--r--drivers/tty/serial/bfin_uart.c2
-rw-r--r--drivers/tty/serial/icom.c2
-rw-r--r--drivers/usb/core/driver.c2
-rw-r--r--drivers/usb/gadget/at91_udc.c2
-rw-r--r--drivers/usb/storage/realtek_cr.c2
-rw-r--r--drivers/vhost/tcm_vhost.c2
-rw-r--r--drivers/video/Kconfig17
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/console/newport_con.c11
-rw-r--r--drivers/video/console/softcursor.c3
-rw-r--r--drivers/video/da8xx-fb.c170
-rw-r--r--drivers/video/exynos/exynos_dp_core.c697
-rw-r--r--drivers/video/exynos/exynos_dp_core.h21
-rw-r--r--drivers/video/exynos/exynos_dp_reg.c77
-rw-r--r--drivers/video/exynos/exynos_dp_reg.h3
-rw-r--r--drivers/video/fsl-diu-fb.c201
-rw-r--r--drivers/video/omap2/Kconfig3
-rw-r--r--drivers/video/omap2/Makefile1
-rw-r--r--drivers/video/omap2/displays/panel-acx565akm.c23
-rw-r--r--drivers/video/omap2/displays/panel-generic-dpi.c36
-rw-r--r--drivers/video/omap2/displays/panel-lgphilips-lb035q02.c34
-rw-r--r--drivers/video/omap2/displays/panel-n8x0.c89
-rw-r--r--drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c24
-rw-r--r--drivers/video/omap2/displays/panel-picodlp.c45
-rw-r--r--drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c17
-rw-r--r--drivers/video/omap2/displays/panel-taal.c72
-rw-r--r--drivers/video/omap2/displays/panel-tfp410.c33
-rw-r--r--drivers/video/omap2/displays/panel-tpo-td043mtea1.c20
-rw-r--r--drivers/video/omap2/dss/Kconfig35
-rw-r--r--drivers/video/omap2/dss/Makefile7
-rw-r--r--drivers/video/omap2/dss/apply.c331
-rw-r--r--drivers/video/omap2/dss/core.c72
-rw-r--r--drivers/video/omap2/dss/dispc-compat.c667
-rw-r--r--drivers/video/omap2/dss/dispc-compat.h30
-rw-r--r--drivers/video/omap2/dss/dispc.c1063
-rw-r--r--drivers/video/omap2/dss/display-sysfs.c321
-rw-r--r--drivers/video/omap2/dss/display.c386
-rw-r--r--drivers/video/omap2/dss/dpi.c126
-rw-r--r--drivers/video/omap2/dss/dsi.c247
-rw-r--r--drivers/video/omap2/dss/dss.c101
-rw-r--r--drivers/video/omap2/dss/dss.h124
-rw-r--r--drivers/video/omap2/dss/dss_features.c15
-rw-r--r--drivers/video/omap2/dss/dss_features.h7
-rw-r--r--drivers/video/omap2/dss/hdmi.c159
-rw-r--r--drivers/video/omap2/dss/hdmi_panel.c82
-rw-r--r--drivers/video/omap2/dss/manager.c39
-rw-r--r--drivers/video/omap2/dss/output.c90
-rw-r--r--drivers/video/omap2/dss/overlay.c17
-rw-r--r--drivers/video/omap2/dss/rfbi.c23
-rw-r--r--drivers/video/omap2/dss/sdi.c11
-rw-r--r--drivers/video/omap2/dss/ti_hdmi.h3
-rw-r--r--drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c11
-rw-r--r--drivers/video/omap2/dss/venc.c11
-rw-r--r--drivers/video/omap2/dss/venc_panel.c19
-rw-r--r--drivers/video/omap2/omapfb/Kconfig1
-rw-r--r--drivers/video/omap2/omapfb/omapfb-ioctl.c46
-rw-r--r--drivers/video/omap2/omapfb/omapfb-main.c204
-rw-r--r--drivers/video/omap2/omapfb/omapfb-sysfs.c4
-rw-r--r--drivers/video/omap2/omapfb/omapfb.h20
-rw-r--r--drivers/video/omap2/vram.c514
-rw-r--r--drivers/video/s3c-fb.c24
-rw-r--r--drivers/video/sh_mipi_dsi.c69
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c74
-rw-r--r--drivers/video/sh_mobile_lcdcfb.h1
-rw-r--r--drivers/video/ssd1307fb.c396
-rw-r--r--drivers/virt/Kconfig1
-rw-r--r--drivers/watchdog/booke_wdt.c2
-rw-r--r--drivers/xen/Kconfig3
-rw-r--r--drivers/xen/Makefile7
-rw-r--r--drivers/xen/balloon.c5
-rw-r--r--drivers/xen/privcmd.c72
-rw-r--r--drivers/xen/xen-acpi-pad.c182
-rw-r--r--drivers/xen/xen-pciback/pci_stub.c120
-rw-r--r--drivers/xen/xenbus/xenbus_xs.c1
883 files changed, 85056 insertions, 10825 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 0300bf61294..38c5078da11 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -267,6 +267,15 @@ config ACPI_CUSTOM_DSDT
bool
default ACPI_CUSTOM_DSDT_FILE != ""
+config ACPI_INITRD_TABLE_OVERRIDE
+ bool "ACPI tables can be passed via uncompressed cpio in initrd"
+ default n
+ help
+ This option provides functionality to override arbitrary ACPI tables
+ via initrd. No functional change if no ACPI tables are passed via
+ initrd, therefore it's safe to say Y.
+ See Documentation/acpi/initrd_table_override.txt for details
+
config ACPI_BLACKLIST_YEAR
int "Disable ACPI for systems before Jan 1st this year" if X86_32
default 0
diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c
index 0df024e5fb6..d09c6b4bab2 100644
--- a/drivers/acpi/acpica/dsopcode.c
+++ b/drivers/acpi/acpica/dsopcode.c
@@ -1,6 +1,6 @@
/******************************************************************************
*
- * Module Name: dsopcode - Dispatcher suport for regions and fields
+ * Module Name: dsopcode - Dispatcher support for regions and fields
*
*****************************************************************************/
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 6dc4a2b1e95..3ff26786154 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -534,6 +534,137 @@ acpi_os_predefined_override(const struct acpi_predefined_names *init_val,
return AE_OK;
}
+#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
+#include <linux/earlycpio.h>
+#include <linux/memblock.h>
+
+static u64 acpi_tables_addr;
+static int all_tables_size;
+
+/* Copied from acpica/tbutils.c:acpi_tb_checksum() */
+u8 __init acpi_table_checksum(u8 *buffer, u32 length)
+{
+ u8 sum = 0;
+ u8 *end = buffer + length;
+
+ while (buffer < end)
+ sum = (u8) (sum + *(buffer++));
+ return sum;
+}
+
+/* All but ACPI_SIG_RSDP and ACPI_SIG_FACS: */
+static const char * const table_sigs[] = {
+ ACPI_SIG_BERT, ACPI_SIG_CPEP, ACPI_SIG_ECDT, ACPI_SIG_EINJ,
+ ACPI_SIG_ERST, ACPI_SIG_HEST, ACPI_SIG_MADT, ACPI_SIG_MSCT,
+ ACPI_SIG_SBST, ACPI_SIG_SLIT, ACPI_SIG_SRAT, ACPI_SIG_ASF,
+ ACPI_SIG_BOOT, ACPI_SIG_DBGP, ACPI_SIG_DMAR, ACPI_SIG_HPET,
+ ACPI_SIG_IBFT, ACPI_SIG_IVRS, ACPI_SIG_MCFG, ACPI_SIG_MCHI,
+ ACPI_SIG_SLIC, ACPI_SIG_SPCR, ACPI_SIG_SPMI, ACPI_SIG_TCPA,
+ ACPI_SIG_UEFI, ACPI_SIG_WAET, ACPI_SIG_WDAT, ACPI_SIG_WDDT,
+ ACPI_SIG_WDRT, ACPI_SIG_DSDT, ACPI_SIG_FADT, ACPI_SIG_PSDT,
+ ACPI_SIG_RSDT, ACPI_SIG_XSDT, ACPI_SIG_SSDT, NULL };
+
+/* Non-fatal errors: Affected tables/files are ignored */
+#define INVALID_TABLE(x, path, name) \
+ { pr_err("ACPI OVERRIDE: " x " [%s%s]\n", path, name); continue; }
+
+#define ACPI_HEADER_SIZE sizeof(struct acpi_table_header)
+
+/* Must not increase 10 or needs code modification below */
+#define ACPI_OVERRIDE_TABLES 10
+
+void __init acpi_initrd_override(void *data, size_t size)
+{
+ int sig, no, table_nr = 0, total_offset = 0;
+ long offset = 0;
+ struct acpi_table_header *table;
+ char cpio_path[32] = "kernel/firmware/acpi/";
+ struct cpio_data file;
+ struct cpio_data early_initrd_files[ACPI_OVERRIDE_TABLES];
+ char *p;
+
+ if (data == NULL || size == 0)
+ return;
+
+ for (no = 0; no < ACPI_OVERRIDE_TABLES; no++) {
+ file = find_cpio_data(cpio_path, data, size, &offset);
+ if (!file.data)
+ break;
+
+ data += offset;
+ size -= offset;
+
+ if (file.size < sizeof(struct acpi_table_header))
+ INVALID_TABLE("Table smaller than ACPI header",
+ cpio_path, file.name);
+
+ table = file.data;
+
+ for (sig = 0; table_sigs[sig]; sig++)
+ if (!memcmp(table->signature, table_sigs[sig], 4))
+ break;
+
+ if (!table_sigs[sig])
+ INVALID_TABLE("Unknown signature",
+ cpio_path, file.name);
+ if (file.size != table->length)
+ INVALID_TABLE("File length does not match table length",
+ cpio_path, file.name);
+ if (acpi_table_checksum(file.data, table->length))
+ INVALID_TABLE("Bad table checksum",
+ cpio_path, file.name);
+
+ pr_info("%4.4s ACPI table found in initrd [%s%s][0x%x]\n",
+ table->signature, cpio_path, file.name, table->length);
+
+ all_tables_size += table->length;
+ early_initrd_files[table_nr].data = file.data;
+ early_initrd_files[table_nr].size = file.size;
+ table_nr++;
+ }
+ if (table_nr == 0)
+ return;
+
+ acpi_tables_addr =
+ memblock_find_in_range(0, max_low_pfn_mapped << PAGE_SHIFT,
+ all_tables_size, PAGE_SIZE);
+ if (!acpi_tables_addr) {
+ WARN_ON(1);
+ return;
+ }
+ /*
+ * Only calling e820_add_reserve does not work and the
+ * tables are invalid (memory got used) later.
+ * memblock_reserve works as expected and the tables won't get modified.
+ * But it's not enough on X86 because ioremap will
+ * complain later (used by acpi_os_map_memory) that the pages
+ * that should get mapped are not marked "reserved".
+ * Both memblock_reserve and e820_add_region (via arch_reserve_mem_area)
+ * works fine.
+ */
+ memblock_reserve(acpi_tables_addr, acpi_tables_addr + all_tables_size);
+ arch_reserve_mem_area(acpi_tables_addr, all_tables_size);
+
+ p = early_ioremap(acpi_tables_addr, all_tables_size);
+
+ for (no = 0; no < table_nr; no++) {
+ memcpy(p + total_offset, early_initrd_files[no].data,
+ early_initrd_files[no].size);
+ total_offset += early_initrd_files[no].size;
+ }
+ early_iounmap(p, all_tables_size);
+}
+#endif /* CONFIG_ACPI_INITRD_TABLE_OVERRIDE */
+
+static void acpi_table_taint(struct acpi_table_header *table)
+{
+ pr_warn(PREFIX
+ "Override [%4.4s-%8.8s], this is unsafe: tainting kernel\n",
+ table->signature, table->oem_table_id);
+ add_taint(TAINT_OVERRIDDEN_ACPI_TABLE);
+}
+
+
acpi_status
acpi_os_table_override(struct acpi_table_header * existing_table,
struct acpi_table_header ** new_table)
@@ -547,24 +678,73 @@ acpi_os_table_override(struct acpi_table_header * existing_table,
if (strncmp(existing_table->signature, "DSDT", 4) == 0)
*new_table = (struct acpi_table_header *)AmlCode;
#endif
- if (*new_table != NULL) {
- printk(KERN_WARNING PREFIX "Override [%4.4s-%8.8s], "
- "this is unsafe: tainting kernel\n",
- existing_table->signature,
- existing_table->oem_table_id);
- add_taint(TAINT_OVERRIDDEN_ACPI_TABLE);
- }
+ if (*new_table != NULL)
+ acpi_table_taint(existing_table);
return AE_OK;
}
acpi_status
acpi_os_physical_table_override(struct acpi_table_header *existing_table,
- acpi_physical_address * new_address,
- u32 *new_table_length)
+ acpi_physical_address *address,
+ u32 *table_length)
{
- return AE_SUPPORT;
-}
+#ifndef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
+ *table_length = 0;
+ *address = 0;
+ return AE_OK;
+#else
+ int table_offset = 0;
+ struct acpi_table_header *table;
+
+ *table_length = 0;
+ *address = 0;
+
+ if (!acpi_tables_addr)
+ return AE_OK;
+
+ do {
+ if (table_offset + ACPI_HEADER_SIZE > all_tables_size) {
+ WARN_ON(1);
+ return AE_OK;
+ }
+ table = acpi_os_map_memory(acpi_tables_addr + table_offset,
+ ACPI_HEADER_SIZE);
+
+ if (table_offset + table->length > all_tables_size) {
+ acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
+ WARN_ON(1);
+ return AE_OK;
+ }
+
+ table_offset += table->length;
+
+ if (memcmp(existing_table->signature, table->signature, 4)) {
+ acpi_os_unmap_memory(table,
+ ACPI_HEADER_SIZE);
+ continue;
+ }
+
+ /* Only override tables with matching oem id */
+ if (memcmp(table->oem_table_id, existing_table->oem_table_id,
+ ACPI_OEM_TABLE_ID_SIZE)) {
+ acpi_os_unmap_memory(table,
+ ACPI_HEADER_SIZE);
+ continue;
+ }
+
+ table_offset -= table->length;
+ *table_length = table->length;
+ acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
+ *address = acpi_tables_addr + table_offset;
+ break;
+ } while (table_offset + ACPI_HEADER_SIZE < all_tables_size);
+
+ if (*address != 0)
+ acpi_table_taint(existing_table);
+ return AE_OK;
+#endif
+}
static irqreturn_t acpi_irq(int irq, void *dev_id)
{
diff --git a/drivers/acpi/pci_bind.c b/drivers/acpi/pci_bind.c
index 2ef04098cc1..a1dee29beed 100644
--- a/drivers/acpi/pci_bind.c
+++ b/drivers/acpi/pci_bind.c
@@ -45,11 +45,12 @@ static int acpi_pci_unbind(struct acpi_device *device)
device_set_run_wake(&dev->dev, false);
pci_acpi_remove_pm_notifier(device);
+ acpi_power_resource_unregister_device(&dev->dev, device->handle);
if (!dev->subordinate)
goto out;
- acpi_pci_irq_del_prt(dev->subordinate);
+ acpi_pci_irq_del_prt(pci_domain_nr(dev->bus), dev->subordinate->number);
device->ops.bind = NULL;
device->ops.unbind = NULL;
@@ -63,7 +64,7 @@ static int acpi_pci_bind(struct acpi_device *device)
{
acpi_status status;
acpi_handle handle;
- struct pci_bus *bus;
+ unsigned char bus;
struct pci_dev *dev;
dev = acpi_get_pci_dev(device->handle);
@@ -71,6 +72,7 @@ static int acpi_pci_bind(struct acpi_device *device)
return 0;
pci_acpi_add_pm_notifier(device, dev);
+ acpi_power_resource_register_device(&dev->dev, device->handle);
if (device->wakeup.flags.run_wake)
device_set_run_wake(&dev->dev, true);
@@ -100,11 +102,11 @@ static int acpi_pci_bind(struct acpi_device *device)
goto out;
if (dev->subordinate)
- bus = dev->subordinate;
+ bus = dev->subordinate->number;
else
- bus = dev->bus;
+ bus = dev->bus->number;
- acpi_pci_irq_add_prt(device->handle, bus);
+ acpi_pci_irq_add_prt(device->handle, pci_domain_nr(dev->bus), bus);
out:
pci_dev_put(dev);
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c
index 23a03249013..68a921d0324 100644
--- a/drivers/acpi/pci_irq.c
+++ b/drivers/acpi/pci_irq.c
@@ -184,7 +184,7 @@ static void do_prt_fixups(struct acpi_prt_entry *entry,
}
}
-static int acpi_pci_irq_add_entry(acpi_handle handle, struct pci_bus *bus,
+static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus,
struct acpi_pci_routing_table *prt)
{
struct acpi_prt_entry *entry;
@@ -198,8 +198,8 @@ static int acpi_pci_irq_add_entry(acpi_handle handle, struct pci_bus *bus,
* 1=INTA, 2=INTB. We use the PCI encoding throughout, so convert
* it here.
*/
- entry->id.segment = pci_domain_nr(bus);
- entry->id.bus = bus->number;
+ entry->id.segment = segment;
+ entry->id.bus = bus;
entry->id.device = (prt->address >> 16) & 0xFFFF;
entry->pin = prt->pin + 1;
@@ -244,7 +244,7 @@ static int acpi_pci_irq_add_entry(acpi_handle handle, struct pci_bus *bus,
return 0;
}
-int acpi_pci_irq_add_prt(acpi_handle handle, struct pci_bus *bus)
+int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus)
{
acpi_status status;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -273,7 +273,7 @@ int acpi_pci_irq_add_prt(acpi_handle handle, struct pci_bus *bus)
entry = buffer.pointer;
while (entry && (entry->length > 0)) {
- acpi_pci_irq_add_entry(handle, bus, entry);
+ acpi_pci_irq_add_entry(handle, segment, bus, entry);
entry = (struct acpi_pci_routing_table *)
((unsigned long)entry + entry->length);
}
@@ -282,17 +282,16 @@ int acpi_pci_irq_add_prt(acpi_handle handle, struct pci_bus *bus)
return 0;
}
-void acpi_pci_irq_del_prt(struct pci_bus *bus)
+void acpi_pci_irq_del_prt(int segment, int bus)
{
struct acpi_prt_entry *entry, *tmp;
printk(KERN_DEBUG
"ACPI: Delete PCI Interrupt Routing Table for %04x:%02x\n",
- pci_domain_nr(bus), bus->number);
+ segment, bus);
spin_lock(&acpi_prt_lock);
list_for_each_entry_safe(entry, tmp, &acpi_prt_list, list) {
- if (pci_domain_nr(bus) == entry->id.segment
- && bus->number == entry->id.bus) {
+ if (segment == entry->id.segment && bus == entry->id.bus) {
list_del(&entry->list);
kfree(entry);
}
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index f70b9e5fc1b..7928d4dc705 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -454,6 +454,7 @@ static int acpi_pci_root_add(struct acpi_device *device)
acpi_handle handle;
struct acpi_device *child;
u32 flags, base_flags;
+ bool is_osc_granted = false;
root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL);
if (!root)
@@ -501,85 +502,47 @@ static int acpi_pci_root_add(struct acpi_device *device)
strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS);
device->driver_data = root;
- root->mcfg_addr = acpi_pci_root_get_mcfg_addr(device->handle);
-
- /*
- * All supported architectures that use ACPI have support for
- * PCI domains, so we indicate this in _OSC support capabilities.
- */
- flags = base_flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT;
- acpi_pci_osc_support(root, flags);
-
- /*
- * TBD: Need PCI interface for enumeration/configuration of roots.
- */
-
- mutex_lock(&acpi_pci_root_lock);
- list_add_tail(&root->node, &acpi_pci_roots);
- mutex_unlock(&acpi_pci_root_lock);
-
printk(KERN_INFO PREFIX "%s [%s] (domain %04x %pR)\n",
acpi_device_name(device), acpi_device_bid(device),
root->segment, &root->secondary);
/*
- * Scan the Root Bridge
- * --------------------
- * Must do this prior to any attempt to bind the root device, as the
- * PCI namespace does not get created until this call is made (and
- * thus the root bridge's pci_dev does not exist).
- */
- root->bus = pci_acpi_scan_root(root);
- if (!root->bus) {
- printk(KERN_ERR PREFIX
- "Bus %04x:%02x not present in PCI namespace\n",
- root->segment, (unsigned int)root->secondary.start);
- result = -ENODEV;
- goto out_del_root;
- }
-
- /*
- * Attach ACPI-PCI Context
- * -----------------------
- * Thus binding the ACPI and PCI devices.
- */
- result = acpi_pci_bind_root(device);
- if (result)
- goto out_del_root;
-
- /*
* PCI Routing Table
* -----------------
* Evaluate and parse _PRT, if exists.
*/
status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle);
if (ACPI_SUCCESS(status))
- result = acpi_pci_irq_add_prt(device->handle, root->bus);
+ result = acpi_pci_irq_add_prt(device->handle, root->segment,
+ root->secondary.start);
+
+ root->mcfg_addr = acpi_pci_root_get_mcfg_addr(device->handle);
/*
- * Scan and bind all _ADR-Based Devices
+ * All supported architectures that use ACPI have support for
+ * PCI domains, so we indicate this in _OSC support capabilities.
*/
- list_for_each_entry(child, &device->children, node)
- acpi_pci_bridge_scan(child);
+ flags = base_flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT;
+ acpi_pci_osc_support(root, flags);
/* Indicate support for various _OSC capabilities. */
- if (pci_ext_cfg_avail(root->bus->self))
+ if (pci_ext_cfg_avail())
flags |= OSC_EXT_PCI_CONFIG_SUPPORT;
- if (pcie_aspm_support_enabled())
+ if (pcie_aspm_support_enabled()) {
flags |= OSC_ACTIVE_STATE_PWR_SUPPORT |
- OSC_CLOCK_PWR_CAPABILITY_SUPPORT;
+ OSC_CLOCK_PWR_CAPABILITY_SUPPORT;
+ }
if (pci_msi_enabled())
flags |= OSC_MSI_SUPPORT;
if (flags != base_flags) {
status = acpi_pci_osc_support(root, flags);
if (ACPI_FAILURE(status)) {
- dev_info(root->bus->bridge, "ACPI _OSC support "
+ dev_info(&device->dev, "ACPI _OSC support "
"notification failed, disabling PCIe ASPM\n");
pcie_no_aspm();
flags = base_flags;
}
}
-
if (!pcie_ports_disabled
&& (flags & ACPI_PCIE_REQ_SUPPORT) == ACPI_PCIE_REQ_SUPPORT) {
flags = OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL
@@ -588,40 +551,81 @@ static int acpi_pci_root_add(struct acpi_device *device)
if (pci_aer_available()) {
if (aer_acpi_firmware_first())
- dev_dbg(root->bus->bridge,
+ dev_dbg(&device->dev,
"PCIe errors handled by BIOS.\n");
else
flags |= OSC_PCI_EXPRESS_AER_CONTROL;
}
- dev_info(root->bus->bridge,
+ dev_info(&device->dev,
"Requesting ACPI _OSC control (0x%02x)\n", flags);
status = acpi_pci_osc_control_set(device->handle, &flags,
- OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+ OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
if (ACPI_SUCCESS(status)) {
- dev_info(root->bus->bridge,
+ is_osc_granted = true;
+ dev_info(&device->dev,
"ACPI _OSC control (0x%02x) granted\n", flags);
- if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) {
- /*
- * We have ASPM control, but the FADT indicates
- * that it's unsupported. Clear it.
- */
- pcie_clear_aspm(root->bus);
- }
} else {
- dev_info(root->bus->bridge,
+ is_osc_granted = false;
+ dev_info(&device->dev,
"ACPI _OSC request failed (%s), "
"returned control mask: 0x%02x\n",
acpi_format_exception(status), flags);
- pr_info("ACPI _OSC control for PCIe not granted, "
- "disabling ASPM\n");
- pcie_no_aspm();
}
} else {
- dev_info(root->bus->bridge,
- "Unable to request _OSC control "
- "(_OSC support mask: 0x%02x)\n", flags);
+ dev_info(&device->dev,
+ "Unable to request _OSC control "
+ "(_OSC support mask: 0x%02x)\n", flags);
+ }
+
+ /*
+ * TBD: Need PCI interface for enumeration/configuration of roots.
+ */
+
+ mutex_lock(&acpi_pci_root_lock);
+ list_add_tail(&root->node, &acpi_pci_roots);
+ mutex_unlock(&acpi_pci_root_lock);
+
+ /*
+ * Scan the Root Bridge
+ * --------------------
+ * Must do this prior to any attempt to bind the root device, as the
+ * PCI namespace does not get created until this call is made (and
+ * thus the root bridge's pci_dev does not exist).
+ */
+ root->bus = pci_acpi_scan_root(root);
+ if (!root->bus) {
+ printk(KERN_ERR PREFIX
+ "Bus %04x:%02x not present in PCI namespace\n",
+ root->segment, (unsigned int)root->secondary.start);
+ result = -ENODEV;
+ goto out_del_root;
+ }
+
+ /*
+ * Attach ACPI-PCI Context
+ * -----------------------
+ * Thus binding the ACPI and PCI devices.
+ */
+ result = acpi_pci_bind_root(device);
+ if (result)
+ goto out_del_root;
+
+ /*
+ * Scan and bind all _ADR-Based Devices
+ */
+ list_for_each_entry(child, &device->children, node)
+ acpi_pci_bridge_scan(child);
+
+ /* ASPM setting */
+ if (is_osc_granted) {
+ if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM)
+ pcie_clear_aspm(root->bus);
+ } else {
+ pr_info("ACPI _OSC control for PCIe not granted, "
+ "disabling ASPM\n");
+ pcie_no_aspm();
}
pci_acpi_add_bus_pm_notifier(device, root->bus);
@@ -634,6 +638,8 @@ out_del_root:
mutex_lock(&acpi_pci_root_lock);
list_del(&root->node);
mutex_unlock(&acpi_pci_root_lock);
+
+ acpi_pci_irq_del_prt(root->segment, root->secondary.start);
end:
kfree(root);
return result;
@@ -644,12 +650,19 @@ static int acpi_pci_root_start(struct acpi_device *device)
struct acpi_pci_root *root = acpi_driver_data(device);
struct acpi_pci_driver *driver;
+ if (system_state != SYSTEM_BOOTING)
+ pci_assign_unassigned_bus_resources(root->bus);
+
mutex_lock(&acpi_pci_root_lock);
list_for_each_entry(driver, &acpi_pci_drivers, node)
if (driver->add)
driver->add(root);
mutex_unlock(&acpi_pci_root_lock);
+ /* need to after hot-added ioapic is registered */
+ if (system_state != SYSTEM_BOOTING)
+ pci_enable_bridges(root->bus);
+
pci_bus_add_devices(root->bus);
return 0;
@@ -657,17 +670,29 @@ static int acpi_pci_root_start(struct acpi_device *device)
static int acpi_pci_root_remove(struct acpi_device *device, int type)
{
+ acpi_status status;
+ acpi_handle handle;
struct acpi_pci_root *root = acpi_driver_data(device);
struct acpi_pci_driver *driver;
+ pci_stop_root_bus(root->bus);
+
mutex_lock(&acpi_pci_root_lock);
- list_for_each_entry(driver, &acpi_pci_drivers, node)
+ list_for_each_entry_reverse(driver, &acpi_pci_drivers, node)
if (driver->remove)
driver->remove(root);
+ mutex_unlock(&acpi_pci_root_lock);
device_set_run_wake(root->bus->bridge, false);
pci_acpi_remove_bus_pm_notifier(device);
+ status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle);
+ if (ACPI_SUCCESS(status))
+ acpi_pci_irq_del_prt(root->segment, root->secondary.start);
+
+ pci_remove_root_bus(root->bus);
+
+ mutex_lock(&acpi_pci_root_lock);
list_del(&root->node);
mutex_unlock(&acpi_pci_root_lock);
kfree(root);
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index b7078afddb7..1cc467bdb63 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -25,6 +25,8 @@
#include <linux/ahci_platform.h>
#include "ahci.h"
+static void ahci_host_stop(struct ata_host *host);
+
enum ahci_type {
AHCI, /* standard platform ahci */
IMX53_AHCI, /* ahci on i.mx53 */
@@ -47,6 +49,15 @@ static struct platform_device_id ahci_devtype[] = {
};
MODULE_DEVICE_TABLE(platform, ahci_devtype);
+static struct ata_port_operations ahci_platform_ops = {
+ .inherits = &ahci_ops,
+ .host_stop = ahci_host_stop,
+};
+
+static struct ata_port_operations ahci_platform_retry_srst_ops = {
+ .inherits = &ahci_pmp_retry_srst_ops,
+ .host_stop = ahci_host_stop,
+};
static const struct ata_port_info ahci_port_info[] = {
/* by features */
@@ -54,20 +65,20 @@ static const struct ata_port_info ahci_port_info[] = {
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
- .port_ops = &ahci_ops,
+ .port_ops = &ahci_platform_ops,
},
[IMX53_AHCI] = {
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
- .port_ops = &ahci_pmp_retry_srst_ops,
+ .port_ops = &ahci_platform_retry_srst_ops,
},
[STRICT_AHCI] = {
AHCI_HFLAGS (AHCI_HFLAG_DELAY_ENGINE),
.flags = AHCI_FLAG_COMMON,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
- .port_ops = &ahci_ops,
+ .port_ops = &ahci_platform_ops,
},
};
@@ -75,7 +86,7 @@ static struct scsi_host_template ahci_platform_sht = {
AHCI_SHT("ahci_platform"),
};
-static int __init ahci_probe(struct platform_device *pdev)
+static int __devinit ahci_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ahci_platform_data *pdata = dev_get_platdata(dev);
@@ -218,15 +229,12 @@ free_clk:
return rc;
}
-static int __devexit ahci_remove(struct platform_device *pdev)
+static void ahci_host_stop(struct ata_host *host)
{
- struct device *dev = &pdev->dev;
+ struct device *dev = host->dev;
struct ahci_platform_data *pdata = dev_get_platdata(dev);
- struct ata_host *host = dev_get_drvdata(dev);
struct ahci_host_priv *hpriv = host->private_data;
- ata_host_detach(host);
-
if (pdata && pdata->exit)
pdata->exit(dev);
@@ -234,8 +242,6 @@ static int __devexit ahci_remove(struct platform_device *pdev)
clk_disable_unprepare(hpriv->clk);
clk_put(hpriv->clk);
}
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -317,7 +323,7 @@ disable_unprepare_clk:
}
#endif
-SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_suspend, ahci_resume);
+static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_suspend, ahci_resume);
static const struct of_device_id ahci_of_match[] = {
{ .compatible = "snps,spear-ahci", },
@@ -326,7 +332,8 @@ static const struct of_device_id ahci_of_match[] = {
MODULE_DEVICE_TABLE(of, ahci_of_match);
static struct platform_driver ahci_driver = {
- .remove = __devexit_p(ahci_remove),
+ .probe = ahci_probe,
+ .remove = ata_platform_remove_one,
.driver = {
.name = "ahci",
.owner = THIS_MODULE,
@@ -335,18 +342,7 @@ static struct platform_driver ahci_driver = {
},
.id_table = ahci_devtype,
};
-
-static int __init ahci_init(void)
-{
- return platform_driver_probe(&ahci_driver, ahci_probe);
-}
-module_init(ahci_init);
-
-static void __exit ahci_exit(void)
-{
- platform_driver_unregister(&ahci_driver);
-}
-module_exit(ahci_exit);
+module_platform_driver(ahci_driver);
MODULE_DESCRIPTION("AHCI SATA platform driver");
MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>");
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
index ef773e12af7..acffcf0b3ad 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -164,28 +164,6 @@ struct piix_host_priv {
void __iomem *sidpr;
};
-static int piix_init_one(struct pci_dev *pdev,
- const struct pci_device_id *ent);
-static void piix_remove_one(struct pci_dev *pdev);
-static int piix_pata_prereset(struct ata_link *link, unsigned long deadline);
-static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev);
-static void piix_set_dmamode(struct ata_port *ap, struct ata_device *adev);
-static void ich_set_dmamode(struct ata_port *ap, struct ata_device *adev);
-static int ich_pata_cable_detect(struct ata_port *ap);
-static u8 piix_vmw_bmdma_status(struct ata_port *ap);
-static int piix_sidpr_scr_read(struct ata_link *link,
- unsigned int reg, u32 *val);
-static int piix_sidpr_scr_write(struct ata_link *link,
- unsigned int reg, u32 val);
-static int piix_sidpr_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
- unsigned hints);
-static bool piix_irq_check(struct ata_port *ap);
-static int piix_port_start(struct ata_port *ap);
-#ifdef CONFIG_PM
-static int piix_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg);
-static int piix_pci_device_resume(struct pci_dev *pdev);
-#endif
-
static unsigned int in_module_init = 1;
static const struct pci_device_id piix_pci_tbl[] = {
@@ -342,64 +320,6 @@ static const struct pci_device_id piix_pci_tbl[] = {
{ } /* terminate list */
};
-static struct pci_driver piix_pci_driver = {
- .name = DRV_NAME,
- .id_table = piix_pci_tbl,
- .probe = piix_init_one,
- .remove = piix_remove_one,
-#ifdef CONFIG_PM
- .suspend = piix_pci_device_suspend,
- .resume = piix_pci_device_resume,
-#endif
-};
-
-static struct scsi_host_template piix_sht = {
- ATA_BMDMA_SHT(DRV_NAME),
-};
-
-static struct ata_port_operations piix_sata_ops = {
- .inherits = &ata_bmdma32_port_ops,
- .sff_irq_check = piix_irq_check,
- .port_start = piix_port_start,
-};
-
-static struct ata_port_operations piix_pata_ops = {
- .inherits = &piix_sata_ops,
- .cable_detect = ata_cable_40wire,
- .set_piomode = piix_set_piomode,
- .set_dmamode = piix_set_dmamode,
- .prereset = piix_pata_prereset,
-};
-
-static struct ata_port_operations piix_vmw_ops = {
- .inherits = &piix_pata_ops,
- .bmdma_status = piix_vmw_bmdma_status,
-};
-
-static struct ata_port_operations ich_pata_ops = {
- .inherits = &piix_pata_ops,
- .cable_detect = ich_pata_cable_detect,
- .set_dmamode = ich_set_dmamode,
-};
-
-static struct device_attribute *piix_sidpr_shost_attrs[] = {
- &dev_attr_link_power_management_policy,
- NULL
-};
-
-static struct scsi_host_template piix_sidpr_sht = {
- ATA_BMDMA_SHT(DRV_NAME),
- .shost_attrs = piix_sidpr_shost_attrs,
-};
-
-static struct ata_port_operations piix_sidpr_sata_ops = {
- .inherits = &piix_sata_ops,
- .hardreset = sata_std_hardreset,
- .scr_read = piix_sidpr_scr_read,
- .scr_write = piix_sidpr_scr_write,
- .set_lpm = piix_sidpr_set_lpm,
-};
-
static const struct piix_map_db ich5_map_db = {
.mask = 0x7,
.port_enable = 0x3,
@@ -504,147 +424,6 @@ static const struct piix_map_db *piix_map_db_table[] = {
[ich8_sata_snb] = &ich8_map_db,
};
-static struct ata_port_info piix_port_info[] = {
- [piix_pata_mwdma] = /* PIIX3 MWDMA only */
- {
- .flags = PIIX_PATA_FLAGS,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA12_ONLY, /* mwdma1-2 ?? CHECK 0 should be ok but slow */
- .port_ops = &piix_pata_ops,
- },
-
- [piix_pata_33] = /* PIIX4 at 33MHz */
- {
- .flags = PIIX_PATA_FLAGS,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA12_ONLY, /* mwdma1-2 ?? CHECK 0 should be ok but slow */
- .udma_mask = ATA_UDMA2,
- .port_ops = &piix_pata_ops,
- },
-
- [ich_pata_33] = /* ICH0 - ICH at 33Mhz*/
- {
- .flags = PIIX_PATA_FLAGS,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA12_ONLY, /* Check: maybe MWDMA0 is ok */
- .udma_mask = ATA_UDMA2,
- .port_ops = &ich_pata_ops,
- },
-
- [ich_pata_66] = /* ICH controllers up to 66MHz */
- {
- .flags = PIIX_PATA_FLAGS,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA12_ONLY, /* MWDMA0 is broken on chip */
- .udma_mask = ATA_UDMA4,
- .port_ops = &ich_pata_ops,
- },
-
- [ich_pata_100] =
- {
- .flags = PIIX_PATA_FLAGS | PIIX_FLAG_CHECKINTR,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA12_ONLY,
- .udma_mask = ATA_UDMA5,
- .port_ops = &ich_pata_ops,
- },
-
- [ich_pata_100_nomwdma1] =
- {
- .flags = PIIX_PATA_FLAGS | PIIX_FLAG_CHECKINTR,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA2_ONLY,
- .udma_mask = ATA_UDMA5,
- .port_ops = &ich_pata_ops,
- },
-
- [ich5_sata] =
- {
- .flags = PIIX_SATA_FLAGS,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA2,
- .udma_mask = ATA_UDMA6,
- .port_ops = &piix_sata_ops,
- },
-
- [ich6_sata] =
- {
- .flags = PIIX_SATA_FLAGS,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA2,
- .udma_mask = ATA_UDMA6,
- .port_ops = &piix_sata_ops,
- },
-
- [ich6m_sata] =
- {
- .flags = PIIX_SATA_FLAGS,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA2,
- .udma_mask = ATA_UDMA6,
- .port_ops = &piix_sata_ops,
- },
-
- [ich8_sata] =
- {
- .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SIDPR,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA2,
- .udma_mask = ATA_UDMA6,
- .port_ops = &piix_sata_ops,
- },
-
- [ich8_2port_sata] =
- {
- .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SIDPR,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA2,
- .udma_mask = ATA_UDMA6,
- .port_ops = &piix_sata_ops,
- },
-
- [tolapai_sata] =
- {
- .flags = PIIX_SATA_FLAGS,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA2,
- .udma_mask = ATA_UDMA6,
- .port_ops = &piix_sata_ops,
- },
-
- [ich8m_apple_sata] =
- {
- .flags = PIIX_SATA_FLAGS,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA2,
- .udma_mask = ATA_UDMA6,
- .port_ops = &piix_sata_ops,
- },
-
- [piix_pata_vmw] =
- {
- .flags = PIIX_PATA_FLAGS,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA12_ONLY, /* mwdma1-2 ?? CHECK 0 should be ok but slow */
- .udma_mask = ATA_UDMA2,
- .port_ops = &piix_vmw_ops,
- },
-
- /*
- * some Sandybridge chipsets have broken 32 mode up to now,
- * see https://bugzilla.kernel.org/show_bug.cgi?id=40592
- */
- [ich8_sata_snb] =
- {
- .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SIDPR | PIIX_FLAG_PIO16,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA2,
- .udma_mask = ATA_UDMA6,
- .port_ops = &piix_sata_ops,
- },
-
-};
-
static struct pci_bits piix_enable_bits[] = {
{ 0x41U, 1U, 0x80UL, 0x80UL }, /* port 0 */
{ 0x43U, 1U, 0x80UL, 0x80UL }, /* port 1 */
@@ -1261,6 +1040,193 @@ static u8 piix_vmw_bmdma_status(struct ata_port *ap)
return ata_bmdma_status(ap) & ~ATA_DMA_ERR;
}
+static struct scsi_host_template piix_sht = {
+ ATA_BMDMA_SHT(DRV_NAME),
+};
+
+static struct ata_port_operations piix_sata_ops = {
+ .inherits = &ata_bmdma32_port_ops,
+ .sff_irq_check = piix_irq_check,
+ .port_start = piix_port_start,
+};
+
+static struct ata_port_operations piix_pata_ops = {
+ .inherits = &piix_sata_ops,
+ .cable_detect = ata_cable_40wire,
+ .set_piomode = piix_set_piomode,
+ .set_dmamode = piix_set_dmamode,
+ .prereset = piix_pata_prereset,
+};
+
+static struct ata_port_operations piix_vmw_ops = {
+ .inherits = &piix_pata_ops,
+ .bmdma_status = piix_vmw_bmdma_status,
+};
+
+static struct ata_port_operations ich_pata_ops = {
+ .inherits = &piix_pata_ops,
+ .cable_detect = ich_pata_cable_detect,
+ .set_dmamode = ich_set_dmamode,
+};
+
+static struct device_attribute *piix_sidpr_shost_attrs[] = {
+ &dev_attr_link_power_management_policy,
+ NULL
+};
+
+static struct scsi_host_template piix_sidpr_sht = {
+ ATA_BMDMA_SHT(DRV_NAME),
+ .shost_attrs = piix_sidpr_shost_attrs,
+};
+
+static struct ata_port_operations piix_sidpr_sata_ops = {
+ .inherits = &piix_sata_ops,
+ .hardreset = sata_std_hardreset,
+ .scr_read = piix_sidpr_scr_read,
+ .scr_write = piix_sidpr_scr_write,
+ .set_lpm = piix_sidpr_set_lpm,
+};
+
+static struct ata_port_info piix_port_info[] = {
+ [piix_pata_mwdma] = /* PIIX3 MWDMA only */
+ {
+ .flags = PIIX_PATA_FLAGS,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA12_ONLY, /* mwdma1-2 ?? CHECK 0 should be ok but slow */
+ .port_ops = &piix_pata_ops,
+ },
+
+ [piix_pata_33] = /* PIIX4 at 33MHz */
+ {
+ .flags = PIIX_PATA_FLAGS,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA12_ONLY, /* mwdma1-2 ?? CHECK 0 should be ok but slow */
+ .udma_mask = ATA_UDMA2,
+ .port_ops = &piix_pata_ops,
+ },
+
+ [ich_pata_33] = /* ICH0 - ICH at 33Mhz*/
+ {
+ .flags = PIIX_PATA_FLAGS,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA12_ONLY, /* Check: maybe MWDMA0 is ok */
+ .udma_mask = ATA_UDMA2,
+ .port_ops = &ich_pata_ops,
+ },
+
+ [ich_pata_66] = /* ICH controllers up to 66MHz */
+ {
+ .flags = PIIX_PATA_FLAGS,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA12_ONLY, /* MWDMA0 is broken on chip */
+ .udma_mask = ATA_UDMA4,
+ .port_ops = &ich_pata_ops,
+ },
+
+ [ich_pata_100] =
+ {
+ .flags = PIIX_PATA_FLAGS | PIIX_FLAG_CHECKINTR,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA12_ONLY,
+ .udma_mask = ATA_UDMA5,
+ .port_ops = &ich_pata_ops,
+ },
+
+ [ich_pata_100_nomwdma1] =
+ {
+ .flags = PIIX_PATA_FLAGS | PIIX_FLAG_CHECKINTR,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2_ONLY,
+ .udma_mask = ATA_UDMA5,
+ .port_ops = &ich_pata_ops,
+ },
+
+ [ich5_sata] =
+ {
+ .flags = PIIX_SATA_FLAGS,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &piix_sata_ops,
+ },
+
+ [ich6_sata] =
+ {
+ .flags = PIIX_SATA_FLAGS,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &piix_sata_ops,
+ },
+
+ [ich6m_sata] =
+ {
+ .flags = PIIX_SATA_FLAGS,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &piix_sata_ops,
+ },
+
+ [ich8_sata] =
+ {
+ .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SIDPR,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &piix_sata_ops,
+ },
+
+ [ich8_2port_sata] =
+ {
+ .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SIDPR,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &piix_sata_ops,
+ },
+
+ [tolapai_sata] =
+ {
+ .flags = PIIX_SATA_FLAGS,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &piix_sata_ops,
+ },
+
+ [ich8m_apple_sata] =
+ {
+ .flags = PIIX_SATA_FLAGS,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &piix_sata_ops,
+ },
+
+ [piix_pata_vmw] =
+ {
+ .flags = PIIX_PATA_FLAGS,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA12_ONLY, /* mwdma1-2 ?? CHECK 0 should be ok but slow */
+ .udma_mask = ATA_UDMA2,
+ .port_ops = &piix_vmw_ops,
+ },
+
+ /*
+ * some Sandybridge chipsets have broken 32 mode up to now,
+ * see https://bugzilla.kernel.org/show_bug.cgi?id=40592
+ */
+ [ich8_sata_snb] =
+ {
+ .flags = PIIX_SATA_FLAGS | PIIX_FLAG_SIDPR | PIIX_FLAG_PIO16,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &piix_sata_ops,
+ },
+};
+
#define AHCI_PCI_BAR 5
#define AHCI_GLOBAL_CTL 0x04
#define AHCI_ENABLE (1 << 31)
@@ -1585,12 +1551,31 @@ static void piix_ignore_devices_quirk(struct ata_host *host)
},
{ } /* terminate list */
};
- const struct dmi_system_id *dmi = dmi_first_match(ignore_hyperv);
+ static const struct dmi_system_id allow_virtual_pc[] = {
+ {
+ /* In MS Virtual PC guests the DMI ident is nearly
+ * identical to a Hyper-V guest. One difference is the
+ * product version which is used here to identify
+ * a Virtual PC guest. This entry allows ata_piix to
+ * drive the emulated hardware.
+ */
+ .ident = "MS Virtual PC 2007",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "Microsoft Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "VS2005R2"),
+ },
+ },
+ { } /* terminate list */
+ };
+ const struct dmi_system_id *ignore = dmi_first_match(ignore_hyperv);
+ const struct dmi_system_id *allow = dmi_first_match(allow_virtual_pc);
- if (dmi && prefer_ms_hyperv) {
+ if (ignore && !allow && prefer_ms_hyperv) {
host->flags |= ATA_HOST_IGNORE_ATA;
dev_info(host->dev, "%s detected, ATA device ignore set\n",
- dmi->ident);
+ ignore->ident);
}
#endif
}
@@ -1727,6 +1712,17 @@ static void piix_remove_one(struct pci_dev *pdev)
ata_pci_remove_one(pdev);
}
+static struct pci_driver piix_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = piix_pci_tbl,
+ .probe = piix_init_one,
+ .remove = piix_remove_one,
+#ifdef CONFIG_PM
+ .suspend = piix_pci_device_suspend,
+ .resume = piix_pci_device_resume,
+#endif
+};
+
static int __init piix_init(void)
{
int rc;
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 4201e535a8c..320712a7b9e 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -1384,7 +1384,7 @@ int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class,
if (rc == -EIO) {
irq_sts = readl(port_mmio + PORT_IRQ_STAT);
if (irq_sts & PORT_IRQ_BAD_PMP) {
- ata_link_printk(link, KERN_WARNING,
+ ata_link_warn(link,
"applying PMP SRST workaround "
"and retrying\n");
rc = ahci_do_softreset(link, class, 0, deadline,
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
index 5b0ba3f20ed..ef01ac07502 100644
--- a/drivers/ata/libata-acpi.c
+++ b/drivers/ata/libata-acpi.c
@@ -76,6 +76,9 @@ acpi_handle ata_dev_acpi_handle(struct ata_device *dev)
acpi_integer adr;
struct ata_port *ap = dev->link->ap;
+ if (dev->flags & ATA_DFLAG_ACPI_DISABLED)
+ return NULL;
+
if (ap->flags & ATA_FLAG_ACPI_SATA) {
if (!sata_pmp_attached(ap))
adr = SATA_ADR(ap->port_no, NO_PORT_MULT);
@@ -945,6 +948,7 @@ int ata_acpi_on_devcfg(struct ata_device *dev)
return rc;
}
+ dev->flags |= ATA_DFLAG_ACPI_DISABLED;
ata_dev_warn(dev, "ACPI: failed the second time, disabled\n");
/* We can safely continue if no _GTF command has been executed
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index f46fbd3bd3f..9e8b99af400 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -67,6 +67,7 @@
#include <linux/cdrom.h>
#include <linux/ratelimit.h>
#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
#include "libata.h"
#include "libata-transport.h"
@@ -2560,6 +2561,7 @@ int ata_bus_probe(struct ata_port *ap)
* bus as we may be talking too fast.
*/
dev->pio_mode = XFER_PIO_0;
+ dev->dma_mode = 0xff;
/* If the controller has a pio mode setup function
* then use it to set the chipset to rights. Don't
@@ -6286,8 +6288,7 @@ void ata_host_detach(struct ata_host *host)
*/
void ata_pci_remove_one(struct pci_dev *pdev)
{
- struct device *dev = &pdev->dev;
- struct ata_host *host = dev_get_drvdata(dev);
+ struct ata_host *host = pci_get_drvdata(pdev);
ata_host_detach(host);
}
@@ -6356,7 +6357,7 @@ int ata_pci_device_do_resume(struct pci_dev *pdev)
int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
{
- struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ struct ata_host *host = pci_get_drvdata(pdev);
int rc = 0;
rc = ata_host_suspend(host, mesg);
@@ -6370,7 +6371,7 @@ int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg)
int ata_pci_device_resume(struct pci_dev *pdev)
{
- struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ struct ata_host *host = pci_get_drvdata(pdev);
int rc;
rc = ata_pci_device_do_resume(pdev);
@@ -6382,6 +6383,26 @@ int ata_pci_device_resume(struct pci_dev *pdev)
#endif /* CONFIG_PCI */
+/**
+ * ata_platform_remove_one - Platform layer callback for device removal
+ * @pdev: Platform device that was removed
+ *
+ * Platform layer indicates to libata via this hook that hot-unplug or
+ * module unload event has occurred. Detach all ports. Resource
+ * release is handled via devres.
+ *
+ * LOCKING:
+ * Inherited from platform layer (may sleep).
+ */
+int ata_platform_remove_one(struct platform_device *pdev)
+{
+ struct ata_host *host = platform_get_drvdata(pdev);
+
+ ata_host_detach(host);
+
+ return 0;
+}
+
static int __init ata_parse_force_one(char **cur,
struct ata_force_ent *force_ent,
const char **reason)
@@ -6877,6 +6898,8 @@ EXPORT_SYMBOL_GPL(ata_pci_device_resume);
#endif /* CONFIG_PM */
#endif /* CONFIG_PCI */
+EXPORT_SYMBOL_GPL(ata_platform_remove_one);
+
EXPORT_SYMBOL_GPL(__ata_ehi_push_desc);
EXPORT_SYMBOL_GPL(ata_ehi_push_desc);
EXPORT_SYMBOL_GPL(ata_ehi_clear_desc);
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index e60437cd0d1..bf039b0e97b 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -2657,6 +2657,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
* bus as we may be talking too fast.
*/
dev->pio_mode = XFER_PIO_0;
+ dev->dma_mode = 0xff;
/* If the controller has a pio mode setup function
* then use it to set the chipset to rights. Don't
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index a6df6a351d6..7c337e754da 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -309,7 +309,8 @@ ata_scsi_activity_show(struct device *dev, struct device_attribute *attr,
struct ata_port *ap = ata_shost_to_port(sdev->host);
struct ata_device *atadev = ata_scsi_find_dev(ap, sdev);
- if (ap->ops->sw_activity_show && (ap->flags & ATA_FLAG_SW_ACTIVITY))
+ if (atadev && ap->ops->sw_activity_show &&
+ (ap->flags & ATA_FLAG_SW_ACTIVITY))
return ap->ops->sw_activity_show(atadev, buf);
return -EINVAL;
}
@@ -324,7 +325,8 @@ ata_scsi_activity_store(struct device *dev, struct device_attribute *attr,
enum sw_activity val;
int rc;
- if (ap->ops->sw_activity_store && (ap->flags & ATA_FLAG_SW_ACTIVITY)) {
+ if (atadev && ap->ops->sw_activity_store &&
+ (ap->flags & ATA_FLAG_SW_ACTIVITY)) {
val = simple_strtoul(buf, NULL, 0);
switch (val) {
case OFF: case BLINK_ON: case BLINK_OFF:
diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c
index 371fd2c698b..9764e80f932 100644
--- a/drivers/ata/pata_arasan_cf.c
+++ b/drivers/ata/pata_arasan_cf.c
@@ -674,13 +674,16 @@ void arasan_cf_error_handler(struct ata_port *ap)
static void arasan_cf_dma_start(struct arasan_cf_dev *acdev)
{
+ struct ata_queued_cmd *qc = acdev->qc;
+ struct ata_port *ap = qc->ap;
+ struct ata_taskfile *tf = &qc->tf;
u32 xfer_ctr = readl(acdev->vbase + XFER_CTR) & ~XFER_DIR_MASK;
- u32 write = acdev->qc->tf.flags & ATA_TFLAG_WRITE;
+ u32 write = tf->flags & ATA_TFLAG_WRITE;
xfer_ctr |= write ? XFER_WRITE : XFER_READ;
writel(xfer_ctr, acdev->vbase + XFER_CTR);
- acdev->qc->ap->ops->sff_exec_command(acdev->qc->ap, &acdev->qc->tf);
+ ap->ops->sff_exec_command(ap, tf);
ata_sff_queue_work(&acdev->work);
}
diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c
index 7ba01415b67..2949cfc2dd3 100644
--- a/drivers/ata/pata_cmd64x.c
+++ b/drivers/ata/pata_cmd64x.c
@@ -474,14 +474,14 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
/* check for enabled ports */
pci_read_config_byte(pdev, CNTRL, &reg);
if (!port_ok)
- dev_printk(KERN_NOTICE, &pdev->dev, "Mobility Bridge detected, ignoring CNTRL port enable/disable\n");
+ dev_notice(&pdev->dev, "Mobility Bridge detected, ignoring CNTRL port enable/disable\n");
if (port_ok && cntrl_ch0_ok && !(reg & CNTRL_CH0)) {
- dev_printk(KERN_NOTICE, &pdev->dev, "Primary port is disabled\n");
+ dev_notice(&pdev->dev, "Primary port is disabled\n");
ppi[0] = &ata_dummy_port_info;
}
if (port_ok && !(reg & CNTRL_CH1)) {
- dev_printk(KERN_NOTICE, &pdev->dev, "Secondary port is disabled\n");
+ dev_notice(&pdev->dev, "Secondary port is disabled\n");
ppi[1] = &ata_dummy_port_info;
}
diff --git a/drivers/ata/pata_cs5536.c b/drivers/ata/pata_cs5536.c
index dec1b6c4b35..0448860a207 100644
--- a/drivers/ata/pata_cs5536.c
+++ b/drivers/ata/pata_cs5536.c
@@ -38,6 +38,7 @@
#include <linux/delay.h>
#include <linux/libata.h>
#include <scsi/scsi_host.h>
+#include <linux/dmi.h>
#ifdef CONFIG_X86_32
#include <asm/msr.h>
@@ -80,6 +81,21 @@ enum {
IDE_ETC_UDMA_MASK = 0xc0,
};
+/* Some Bachmann OT200 devices have a non working UDMA support due a
+ * missing resistor.
+ */
+static const struct dmi_system_id udma_quirk_dmi_table[] = {
+ {
+ .ident = "Bachmann electronic OT200",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Bachmann electronic"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "OT200"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "1")
+ },
+ },
+ { }
+};
+
static int cs5536_read(struct pci_dev *pdev, int reg, u32 *val)
{
if (unlikely(use_msr)) {
@@ -242,9 +258,23 @@ static int cs5536_init_one(struct pci_dev *dev, const struct pci_device_id *id)
.port_ops = &cs5536_port_ops,
};
- const struct ata_port_info *ppi[] = { &info, &ata_dummy_port_info };
+ static const struct ata_port_info no_udma_info = {
+ .flags = ATA_FLAG_SLAVE_POSS,
+ .pio_mask = ATA_PIO4,
+ .port_ops = &cs5536_port_ops,
+ };
+
+
+ const struct ata_port_info *ppi[2];
u32 cfg;
+ if (dmi_check_system(udma_quirk_dmi_table))
+ ppi[0] = &no_udma_info;
+ else
+ ppi[0] = &info;
+
+ ppi[1] = &ata_dummy_port_info;
+
if (use_msr)
printk(KERN_ERR DRV_NAME ": Using MSR regs instead of PCI\n");
diff --git a/drivers/ata/pata_ep93xx.c b/drivers/ata/pata_ep93xx.c
index e056406d6a1..3982cef91f3 100644
--- a/drivers/ata/pata_ep93xx.c
+++ b/drivers/ata/pata_ep93xx.c
@@ -822,8 +822,7 @@ static int ep93xx_pata_softreset(struct ata_link *al, unsigned int *classes,
rc = ep93xx_pata_bus_softreset(ap, devmask, deadline);
/* if link is ocuppied, -ENODEV too is an error */
if (rc && (rc != -ENODEV || sata_scr_valid(al))) {
- ata_link_printk(al, KERN_ERR, "SRST failed (errno=%d)\n",
- rc);
+ ata_link_err(al, "SRST failed (errno=%d)\n", rc);
return rc;
}
@@ -857,8 +856,7 @@ static void ep93xx_pata_drain_fifo(struct ata_queued_cmd *qc)
/* Can become DEBUG later */
if (count)
- ata_port_printk(ap, KERN_DEBUG,
- "drained %d bytes to clear DRQ.\n", count);
+ ata_port_dbg(ap, "drained %d bytes to clear DRQ.\n", count);
}
diff --git a/drivers/ata/pata_imx.c b/drivers/ata/pata_imx.c
index 87bb05b3caf..7d40b526ef1 100644
--- a/drivers/ata/pata_imx.c
+++ b/drivers/ata/pata_imx.c
@@ -60,7 +60,7 @@ static int pata_imx_set_mode(struct ata_link *link, struct ata_device **unused)
val &= ~PATA_IMX_ATA_CTRL_IORDY_EN;
__raw_writel(val, priv->host_regs + PATA_IMX_ATA_CONTROL);
- ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
+ ata_dev_info(dev, "configured for PIO\n");
}
return 0;
}
diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c
index badb1789a91..0566e67b5e1 100644
--- a/drivers/ata/pata_ixp4xx_cf.c
+++ b/drivers/ata/pata_ixp4xx_cf.c
@@ -187,22 +187,13 @@ static __devinit int ixp4xx_pata_probe(struct platform_device *pdev)
return ata_host_activate(host, irq, ata_sff_interrupt, 0, &ixp4xx_sht);
}
-static __devexit int ixp4xx_pata_remove(struct platform_device *dev)
-{
- struct ata_host *host = platform_get_drvdata(dev);
-
- ata_host_detach(host);
-
- return 0;
-}
-
static struct platform_driver ixp4xx_pata_platform_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
.probe = ixp4xx_pata_probe,
- .remove = __devexit_p(ixp4xx_pata_remove),
+ .remove = ata_platform_remove_one,
};
module_platform_driver(ixp4xx_pata_platform_driver);
diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c
index d2c102fd433..ec67f54dc56 100644
--- a/drivers/ata/pata_mpc52xx.c
+++ b/drivers/ata/pata_mpc52xx.c
@@ -663,18 +663,6 @@ mpc52xx_ata_init_one(struct device *dev, struct mpc52xx_ata_priv *priv,
&mpc52xx_ata_sht);
}
-static struct mpc52xx_ata_priv *
-mpc52xx_ata_remove_one(struct device *dev)
-{
- struct ata_host *host = dev_get_drvdata(dev);
- struct mpc52xx_ata_priv *priv = host->private_data;
-
- ata_host_detach(host);
-
- return priv;
-}
-
-
/* ======================================================================== */
/* OF Platform driver */
/* ======================================================================== */
@@ -815,11 +803,12 @@ mpc52xx_ata_probe(struct platform_device *op)
static int
mpc52xx_ata_remove(struct platform_device *op)
{
- struct mpc52xx_ata_priv *priv;
+ struct ata_host *host = platform_get_drvdata(op);
+ struct mpc52xx_ata_priv *priv = host->private_data;
int task_irq;
/* Deregister the ATA interface */
- priv = mpc52xx_ata_remove_one(&op->dev);
+ ata_platform_remove_one(op);
/* Clean up DMA */
task_irq = bcom_get_task_irq(priv->dmatsk);
diff --git a/drivers/ata/pata_octeon_cf.c b/drivers/ata/pata_octeon_cf.c
index 1d61d5d278f..4e1194b4c27 100644
--- a/drivers/ata/pata_octeon_cf.c
+++ b/drivers/ata/pata_octeon_cf.c
@@ -5,19 +5,22 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
- * Copyright (C) 2005 - 2009 Cavium Networks
+ * Copyright (C) 2005 - 2012 Cavium Inc.
* Copyright (C) 2008 Wind River Systems
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/libata.h>
-#include <linux/irq.h>
+#include <linux/hrtimer.h>
#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
#include <linux/platform_device.h>
-#include <linux/workqueue.h>
#include <scsi/scsi_host.h>
+#include <asm/byteorder.h>
#include <asm/octeon/octeon.h>
/*
@@ -34,20 +37,36 @@
*/
#define DRV_NAME "pata_octeon_cf"
-#define DRV_VERSION "2.1"
+#define DRV_VERSION "2.2"
+
+/* Poll interval in nS. */
+#define OCTEON_CF_BUSY_POLL_INTERVAL 500000
+#define DMA_CFG 0
+#define DMA_TIM 0x20
+#define DMA_INT 0x38
+#define DMA_INT_EN 0x50
struct octeon_cf_port {
- struct workqueue_struct *wq;
- struct delayed_work delayed_finish;
+ struct hrtimer delayed_finish;
struct ata_port *ap;
int dma_finished;
+ void *c0;
+ unsigned int cs0;
+ unsigned int cs1;
+ bool is_true_ide;
+ u64 dma_base;
};
static struct scsi_host_template octeon_cf_sht = {
ATA_PIO_SHT(DRV_NAME),
};
+static int enable_dma;
+module_param(enable_dma, int, 0444);
+MODULE_PARM_DESC(enable_dma,
+ "Enable use of DMA on interfaces that support it (0=no dma [default], 1=use dma)");
+
/**
* Convert nanosecond based time to setting used in the
* boot bus timing register, based on timing multiple
@@ -66,12 +85,29 @@ static unsigned int ns_to_tim_reg(unsigned int tim_mult, unsigned int nsecs)
return val;
}
-static void octeon_cf_set_boot_reg_cfg(int cs)
+static void octeon_cf_set_boot_reg_cfg(int cs, unsigned int multiplier)
{
union cvmx_mio_boot_reg_cfgx reg_cfg;
+ unsigned int tim_mult;
+
+ switch (multiplier) {
+ case 8:
+ tim_mult = 3;
+ break;
+ case 4:
+ tim_mult = 0;
+ break;
+ case 2:
+ tim_mult = 2;
+ break;
+ default:
+ tim_mult = 1;
+ break;
+ }
+
reg_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(cs));
reg_cfg.s.dmack = 0; /* Don't assert DMACK on access */
- reg_cfg.s.tim_mult = 2; /* Timing mutiplier 2x */
+ reg_cfg.s.tim_mult = tim_mult; /* Timing mutiplier */
reg_cfg.s.rd_dly = 0; /* Sample on falling edge of BOOT_OE */
reg_cfg.s.sam = 0; /* Don't combine write and output enable */
reg_cfg.s.we_ext = 0; /* No write enable extension */
@@ -92,12 +128,12 @@ static void octeon_cf_set_boot_reg_cfg(int cs)
*/
static void octeon_cf_set_piomode(struct ata_port *ap, struct ata_device *dev)
{
- struct octeon_cf_data *ocd = ap->dev->platform_data;
+ struct octeon_cf_port *cf_port = ap->private_data;
union cvmx_mio_boot_reg_timx reg_tim;
- int cs = ocd->base_region;
int T;
struct ata_timing timing;
+ unsigned int div;
int use_iordy;
int trh;
int pause;
@@ -106,7 +142,15 @@ static void octeon_cf_set_piomode(struct ata_port *ap, struct ata_device *dev)
int t2;
int t2i;
- T = (int)(2000000000000LL / octeon_get_clock_rate());
+ /*
+ * A divisor value of four will overflow the timing fields at
+ * clock rates greater than 800MHz
+ */
+ if (octeon_get_io_clock_rate() <= 800000000)
+ div = 4;
+ else
+ div = 8;
+ T = (int)((1000000000000LL * div) / octeon_get_io_clock_rate());
if (ata_timing_compute(dev, dev->pio_mode, &timing, T, T))
BUG();
@@ -121,23 +165,26 @@ static void octeon_cf_set_piomode(struct ata_port *ap, struct ata_device *dev)
if (t2i)
t2i--;
- trh = ns_to_tim_reg(2, 20);
+ trh = ns_to_tim_reg(div, 20);
if (trh)
trh--;
- pause = timing.cycle - timing.active - timing.setup - trh;
+ pause = (int)timing.cycle - (int)timing.active -
+ (int)timing.setup - trh;
+ if (pause < 0)
+ pause = 0;
if (pause)
pause--;
- octeon_cf_set_boot_reg_cfg(cs);
- if (ocd->dma_engine >= 0)
+ octeon_cf_set_boot_reg_cfg(cf_port->cs0, div);
+ if (cf_port->is_true_ide)
/* True IDE mode, program both chip selects. */
- octeon_cf_set_boot_reg_cfg(cs + 1);
+ octeon_cf_set_boot_reg_cfg(cf_port->cs1, div);
use_iordy = ata_pio_need_iordy(dev);
- reg_tim.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_TIMX(cs));
+ reg_tim.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_TIMX(cf_port->cs0));
/* Disable page mode */
reg_tim.s.pagem = 0;
/* Enable dynamic timing */
@@ -161,20 +208,22 @@ static void octeon_cf_set_piomode(struct ata_port *ap, struct ata_device *dev)
/* How long read enable is asserted */
reg_tim.s.oe = t2;
/* Time after CE that read/write starts */
- reg_tim.s.ce = ns_to_tim_reg(2, 5);
+ reg_tim.s.ce = ns_to_tim_reg(div, 5);
/* Time before CE that address is valid */
reg_tim.s.adr = 0;
/* Program the bootbus region timing for the data port chip select. */
- cvmx_write_csr(CVMX_MIO_BOOT_REG_TIMX(cs), reg_tim.u64);
- if (ocd->dma_engine >= 0)
+ cvmx_write_csr(CVMX_MIO_BOOT_REG_TIMX(cf_port->cs0), reg_tim.u64);
+ if (cf_port->is_true_ide)
/* True IDE mode, program both chip selects. */
- cvmx_write_csr(CVMX_MIO_BOOT_REG_TIMX(cs + 1), reg_tim.u64);
+ cvmx_write_csr(CVMX_MIO_BOOT_REG_TIMX(cf_port->cs1),
+ reg_tim.u64);
}
static void octeon_cf_set_dmamode(struct ata_port *ap, struct ata_device *dev)
{
- struct octeon_cf_data *ocd = dev->link->ap->dev->platform_data;
+ struct octeon_cf_port *cf_port = ap->private_data;
+ union cvmx_mio_boot_pin_defs pin_defs;
union cvmx_mio_boot_dma_timx dma_tim;
unsigned int oe_a;
unsigned int oe_n;
@@ -183,6 +232,7 @@ static void octeon_cf_set_dmamode(struct ata_port *ap, struct ata_device *dev)
unsigned int pause;
unsigned int T0, Tkr, Td;
unsigned int tim_mult;
+ int c;
const struct ata_timing *timing;
@@ -199,13 +249,19 @@ static void octeon_cf_set_dmamode(struct ata_port *ap, struct ata_device *dev)
/* not spec'ed, value in eclocks, not affected by tim_mult */
dma_arq = 8;
pause = 25 - dma_arq * 1000 /
- (octeon_get_clock_rate() / 1000000); /* Tz */
+ (octeon_get_io_clock_rate() / 1000000); /* Tz */
oe_a = Td;
/* Tkr from cf spec, lengthened to meet T0 */
oe_n = max(T0 - oe_a, Tkr);
- dma_tim.s.dmack_pi = 1;
+ pin_defs.u64 = cvmx_read_csr(CVMX_MIO_BOOT_PIN_DEFS);
+
+ /* DMA channel number. */
+ c = (cf_port->dma_base & 8) >> 3;
+
+ /* Invert the polarity if the default is 0*/
+ dma_tim.s.dmack_pi = (pin_defs.u64 & (1ull << (11 + c))) ? 0 : 1;
dma_tim.s.oe_n = ns_to_tim_reg(tim_mult, oe_n);
dma_tim.s.oe_a = ns_to_tim_reg(tim_mult, oe_a);
@@ -228,14 +284,11 @@ static void octeon_cf_set_dmamode(struct ata_port *ap, struct ata_device *dev)
pr_debug("ns to ticks (mult %d) of %d is: %d\n", tim_mult, 60,
ns_to_tim_reg(tim_mult, 60));
- pr_debug("oe_n: %d, oe_a: %d, dmack_s: %d, dmack_h: "
- "%d, dmarq: %d, pause: %d\n",
+ pr_debug("oe_n: %d, oe_a: %d, dmack_s: %d, dmack_h: %d, dmarq: %d, pause: %d\n",
dma_tim.s.oe_n, dma_tim.s.oe_a, dma_tim.s.dmack_s,
dma_tim.s.dmack_h, dma_tim.s.dmarq, dma_tim.s.pause);
- cvmx_write_csr(CVMX_MIO_BOOT_DMA_TIMX(ocd->dma_engine),
- dma_tim.u64);
-
+ cvmx_write_csr(cf_port->dma_base + DMA_TIM, dma_tim.u64);
}
/**
@@ -489,15 +542,10 @@ static void octeon_cf_exec_command16(struct ata_port *ap,
ata_wait_idle(ap);
}
-static void octeon_cf_irq_on(struct ata_port *ap)
+static void octeon_cf_ata_port_noaction(struct ata_port *ap)
{
}
-static void octeon_cf_irq_clear(struct ata_port *ap)
-{
- return;
-}
-
static void octeon_cf_dma_setup(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
@@ -519,7 +567,7 @@ static void octeon_cf_dma_setup(struct ata_queued_cmd *qc)
*/
static void octeon_cf_dma_start(struct ata_queued_cmd *qc)
{
- struct octeon_cf_data *ocd = qc->ap->dev->platform_data;
+ struct octeon_cf_port *cf_port = qc->ap->private_data;
union cvmx_mio_boot_dma_cfgx mio_boot_dma_cfg;
union cvmx_mio_boot_dma_intx mio_boot_dma_int;
struct scatterlist *sg;
@@ -535,15 +583,16 @@ static void octeon_cf_dma_start(struct ata_queued_cmd *qc)
*/
mio_boot_dma_int.u64 = 0;
mio_boot_dma_int.s.done = 1;
- cvmx_write_csr(CVMX_MIO_BOOT_DMA_INTX(ocd->dma_engine),
- mio_boot_dma_int.u64);
+ cvmx_write_csr(cf_port->dma_base + DMA_INT, mio_boot_dma_int.u64);
/* Enable the interrupt. */
- cvmx_write_csr(CVMX_MIO_BOOT_DMA_INT_ENX(ocd->dma_engine),
- mio_boot_dma_int.u64);
+ cvmx_write_csr(cf_port->dma_base + DMA_INT_EN, mio_boot_dma_int.u64);
/* Set the direction of the DMA */
mio_boot_dma_cfg.u64 = 0;
+#ifdef __LITTLE_ENDIAN
+ mio_boot_dma_cfg.s.endian = 1;
+#endif
mio_boot_dma_cfg.s.en = 1;
mio_boot_dma_cfg.s.rw = ((qc->tf.flags & ATA_TFLAG_WRITE) != 0);
@@ -569,8 +618,7 @@ static void octeon_cf_dma_start(struct ata_queued_cmd *qc)
(mio_boot_dma_cfg.s.rw) ? "write" : "read", sg->length,
(void *)(unsigned long)mio_boot_dma_cfg.s.adr);
- cvmx_write_csr(CVMX_MIO_BOOT_DMA_CFGX(ocd->dma_engine),
- mio_boot_dma_cfg.u64);
+ cvmx_write_csr(cf_port->dma_base + DMA_CFG, mio_boot_dma_cfg.u64);
}
/**
@@ -583,10 +631,9 @@ static unsigned int octeon_cf_dma_finished(struct ata_port *ap,
struct ata_queued_cmd *qc)
{
struct ata_eh_info *ehi = &ap->link.eh_info;
- struct octeon_cf_data *ocd = ap->dev->platform_data;
+ struct octeon_cf_port *cf_port = ap->private_data;
union cvmx_mio_boot_dma_cfgx dma_cfg;
union cvmx_mio_boot_dma_intx dma_int;
- struct octeon_cf_port *cf_port;
u8 status;
VPRINTK("ata%u: protocol %d task_state %d\n",
@@ -596,9 +643,7 @@ static unsigned int octeon_cf_dma_finished(struct ata_port *ap,
if (ap->hsm_task_state != HSM_ST_LAST)
return 0;
- cf_port = ap->private_data;
-
- dma_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_DMA_CFGX(ocd->dma_engine));
+ dma_cfg.u64 = cvmx_read_csr(cf_port->dma_base + DMA_CFG);
if (dma_cfg.s.size != 0xfffff) {
/* Error, the transfer was not complete. */
qc->err_mask |= AC_ERR_HOST_BUS;
@@ -608,15 +653,15 @@ static unsigned int octeon_cf_dma_finished(struct ata_port *ap,
/* Stop and clear the dma engine. */
dma_cfg.u64 = 0;
dma_cfg.s.size = -1;
- cvmx_write_csr(CVMX_MIO_BOOT_DMA_CFGX(ocd->dma_engine), dma_cfg.u64);
+ cvmx_write_csr(cf_port->dma_base + DMA_CFG, dma_cfg.u64);
/* Disable the interrupt. */
dma_int.u64 = 0;
- cvmx_write_csr(CVMX_MIO_BOOT_DMA_INT_ENX(ocd->dma_engine), dma_int.u64);
+ cvmx_write_csr(cf_port->dma_base + DMA_INT_EN, dma_int.u64);
/* Clear the DMA complete status */
dma_int.s.done = 1;
- cvmx_write_csr(CVMX_MIO_BOOT_DMA_INTX(ocd->dma_engine), dma_int.u64);
+ cvmx_write_csr(cf_port->dma_base + DMA_INT, dma_int.u64);
status = ap->ops->sff_check_status(ap);
@@ -649,69 +694,68 @@ static irqreturn_t octeon_cf_interrupt(int irq, void *dev_instance)
struct ata_queued_cmd *qc;
union cvmx_mio_boot_dma_intx dma_int;
union cvmx_mio_boot_dma_cfgx dma_cfg;
- struct octeon_cf_data *ocd;
ap = host->ports[i];
- ocd = ap->dev->platform_data;
cf_port = ap->private_data;
- dma_int.u64 =
- cvmx_read_csr(CVMX_MIO_BOOT_DMA_INTX(ocd->dma_engine));
- dma_cfg.u64 =
- cvmx_read_csr(CVMX_MIO_BOOT_DMA_CFGX(ocd->dma_engine));
+
+ dma_int.u64 = cvmx_read_csr(cf_port->dma_base + DMA_INT);
+ dma_cfg.u64 = cvmx_read_csr(cf_port->dma_base + DMA_CFG);
qc = ata_qc_from_tag(ap, ap->link.active_tag);
- if (qc && !(qc->tf.flags & ATA_TFLAG_POLLING)) {
- if (dma_int.s.done && !dma_cfg.s.en) {
- if (!sg_is_last(qc->cursg)) {
- qc->cursg = sg_next(qc->cursg);
- handled = 1;
- octeon_cf_dma_start(qc);
- continue;
- } else {
- cf_port->dma_finished = 1;
- }
- }
- if (!cf_port->dma_finished)
- continue;
- status = ioread8(ap->ioaddr.altstatus_addr);
- if (status & (ATA_BUSY | ATA_DRQ)) {
- /*
- * We are busy, try to handle it
- * later. This is the DMA finished
- * interrupt, and it could take a
- * little while for the card to be
- * ready for more commands.
- */
- /* Clear DMA irq. */
- dma_int.u64 = 0;
- dma_int.s.done = 1;
- cvmx_write_csr(CVMX_MIO_BOOT_DMA_INTX(ocd->dma_engine),
- dma_int.u64);
-
- queue_delayed_work(cf_port->wq,
- &cf_port->delayed_finish, 1);
+ if (!qc || (qc->tf.flags & ATA_TFLAG_POLLING))
+ continue;
+
+ if (dma_int.s.done && !dma_cfg.s.en) {
+ if (!sg_is_last(qc->cursg)) {
+ qc->cursg = sg_next(qc->cursg);
handled = 1;
+ octeon_cf_dma_start(qc);
+ continue;
} else {
- handled |= octeon_cf_dma_finished(ap, qc);
+ cf_port->dma_finished = 1;
}
}
+ if (!cf_port->dma_finished)
+ continue;
+ status = ioread8(ap->ioaddr.altstatus_addr);
+ if (status & (ATA_BUSY | ATA_DRQ)) {
+ /*
+ * We are busy, try to handle it later. This
+ * is the DMA finished interrupt, and it could
+ * take a little while for the card to be
+ * ready for more commands.
+ */
+ /* Clear DMA irq. */
+ dma_int.u64 = 0;
+ dma_int.s.done = 1;
+ cvmx_write_csr(cf_port->dma_base + DMA_INT,
+ dma_int.u64);
+ hrtimer_start_range_ns(&cf_port->delayed_finish,
+ ns_to_ktime(OCTEON_CF_BUSY_POLL_INTERVAL),
+ OCTEON_CF_BUSY_POLL_INTERVAL / 5,
+ HRTIMER_MODE_REL);
+ handled = 1;
+ } else {
+ handled |= octeon_cf_dma_finished(ap, qc);
+ }
}
spin_unlock_irqrestore(&host->lock, flags);
DPRINTK("EXIT\n");
return IRQ_RETVAL(handled);
}
-static void octeon_cf_delayed_finish(struct work_struct *work)
+static enum hrtimer_restart octeon_cf_delayed_finish(struct hrtimer *hrt)
{
- struct octeon_cf_port *cf_port = container_of(work,
+ struct octeon_cf_port *cf_port = container_of(hrt,
struct octeon_cf_port,
- delayed_finish.work);
+ delayed_finish);
struct ata_port *ap = cf_port->ap;
struct ata_host *host = ap->host;
struct ata_queued_cmd *qc;
unsigned long flags;
u8 status;
+ enum hrtimer_restart rv = HRTIMER_NORESTART;
spin_lock_irqsave(&host->lock, flags);
@@ -726,15 +770,17 @@ static void octeon_cf_delayed_finish(struct work_struct *work)
status = ioread8(ap->ioaddr.altstatus_addr);
if (status & (ATA_BUSY | ATA_DRQ)) {
/* Still busy, try again. */
- queue_delayed_work(cf_port->wq,
- &cf_port->delayed_finish, 1);
+ hrtimer_forward_now(hrt,
+ ns_to_ktime(OCTEON_CF_BUSY_POLL_INTERVAL));
+ rv = HRTIMER_RESTART;
goto out;
}
qc = ata_qc_from_tag(ap, ap->link.active_tag);
- if (qc && !(qc->tf.flags & ATA_TFLAG_POLLING))
+ if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING)))
octeon_cf_dma_finished(ap, qc);
out:
spin_unlock_irqrestore(&host->lock, flags);
+ return rv;
}
static void octeon_cf_dev_config(struct ata_device *dev)
@@ -786,8 +832,8 @@ static struct ata_port_operations octeon_cf_ops = {
.qc_prep = ata_noop_qc_prep,
.qc_issue = octeon_cf_qc_issue,
.sff_dev_select = octeon_cf_dev_select,
- .sff_irq_on = octeon_cf_irq_on,
- .sff_irq_clear = octeon_cf_irq_clear,
+ .sff_irq_on = octeon_cf_ata_port_noaction,
+ .sff_irq_clear = octeon_cf_ata_port_noaction,
.cable_detect = ata_cable_40wire,
.set_piomode = octeon_cf_set_piomode,
.set_dmamode = octeon_cf_set_dmamode,
@@ -798,46 +844,113 @@ static int __devinit octeon_cf_probe(struct platform_device *pdev)
{
struct resource *res_cs0, *res_cs1;
+ bool is_16bit;
+ const __be32 *cs_num;
+ struct property *reg_prop;
+ int n_addr, n_size, reg_len;
+ struct device_node *node;
+ const void *prop;
void __iomem *cs0;
void __iomem *cs1 = NULL;
struct ata_host *host;
struct ata_port *ap;
- struct octeon_cf_data *ocd;
int irq = 0;
irq_handler_t irq_handler = NULL;
void __iomem *base;
struct octeon_cf_port *cf_port;
- char version[32];
+ int rv = -ENOMEM;
- res_cs0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res_cs0)
+ node = pdev->dev.of_node;
+ if (node == NULL)
return -EINVAL;
- ocd = pdev->dev.platform_data;
+ cf_port = kzalloc(sizeof(*cf_port), GFP_KERNEL);
+ if (!cf_port)
+ return -ENOMEM;
- cs0 = devm_ioremap_nocache(&pdev->dev, res_cs0->start,
- resource_size(res_cs0));
+ cf_port->is_true_ide = (of_find_property(node, "cavium,true-ide", NULL) != NULL);
- if (!cs0)
- return -ENOMEM;
+ prop = of_get_property(node, "cavium,bus-width", NULL);
+ if (prop)
+ is_16bit = (be32_to_cpup(prop) == 16);
+ else
+ is_16bit = false;
- /* Determine from availability of DMA if True IDE mode or not */
- if (ocd->dma_engine >= 0) {
- res_cs1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!res_cs1)
- return -EINVAL;
+ n_addr = of_n_addr_cells(node);
+ n_size = of_n_size_cells(node);
+ reg_prop = of_find_property(node, "reg", &reg_len);
+ if (!reg_prop || reg_len < sizeof(__be32)) {
+ rv = -EINVAL;
+ goto free_cf_port;
+ }
+ cs_num = reg_prop->value;
+ cf_port->cs0 = be32_to_cpup(cs_num);
+
+ if (cf_port->is_true_ide) {
+ struct device_node *dma_node;
+ dma_node = of_parse_phandle(node,
+ "cavium,dma-engine-handle", 0);
+ if (dma_node) {
+ struct platform_device *dma_dev;
+ dma_dev = of_find_device_by_node(dma_node);
+ if (dma_dev) {
+ struct resource *res_dma;
+ int i;
+ res_dma = platform_get_resource(dma_dev, IORESOURCE_MEM, 0);
+ if (!res_dma) {
+ of_node_put(dma_node);
+ rv = -EINVAL;
+ goto free_cf_port;
+ }
+ cf_port->dma_base = (u64)devm_ioremap_nocache(&pdev->dev, res_dma->start,
+ resource_size(res_dma));
+
+ if (!cf_port->dma_base) {
+ of_node_put(dma_node);
+ rv = -EINVAL;
+ goto free_cf_port;
+ }
+
+ irq_handler = octeon_cf_interrupt;
+ i = platform_get_irq(dma_dev, 0);
+ if (i > 0)
+ irq = i;
+ }
+ of_node_put(dma_node);
+ }
+ res_cs1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res_cs1) {
+ rv = -EINVAL;
+ goto free_cf_port;
+ }
cs1 = devm_ioremap_nocache(&pdev->dev, res_cs1->start,
- resource_size(res_cs1));
+ res_cs1->end - res_cs1->start + 1);
if (!cs1)
- return -ENOMEM;
+ goto free_cf_port;
+
+ if (reg_len < (n_addr + n_size + 1) * sizeof(__be32)) {
+ rv = -EINVAL;
+ goto free_cf_port;
+ }
+ cs_num += n_addr + n_size;
+ cf_port->cs1 = be32_to_cpup(cs_num);
}
- cf_port = kzalloc(sizeof(*cf_port), GFP_KERNEL);
- if (!cf_port)
- return -ENOMEM;
+ res_cs0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res_cs0) {
+ rv = -EINVAL;
+ goto free_cf_port;
+ }
+
+ cs0 = devm_ioremap_nocache(&pdev->dev, res_cs0->start,
+ resource_size(res_cs0));
+
+ if (!cs0)
+ goto free_cf_port;
/* allocate host */
host = ata_host_alloc(&pdev->dev, 1);
@@ -846,21 +959,22 @@ static int __devinit octeon_cf_probe(struct platform_device *pdev)
ap = host->ports[0];
ap->private_data = cf_port;
+ pdev->dev.platform_data = cf_port;
cf_port->ap = ap;
ap->ops = &octeon_cf_ops;
ap->pio_mask = ATA_PIO6;
ap->flags |= ATA_FLAG_NO_ATAPI | ATA_FLAG_PIO_POLLING;
- base = cs0 + ocd->base_region_bias;
- if (!ocd->is16bit) {
+ if (!is_16bit) {
+ base = cs0 + 0x800;
ap->ioaddr.cmd_addr = base;
ata_sff_std_ports(&ap->ioaddr);
ap->ioaddr.altstatus_addr = base + 0xe;
ap->ioaddr.ctl_addr = base + 0xe;
octeon_cf_ops.sff_data_xfer = octeon_cf_data_xfer8;
- } else if (cs1) {
- /* Presence of cs1 indicates True IDE mode. */
+ } else if (cf_port->is_true_ide) {
+ base = cs0;
ap->ioaddr.cmd_addr = base + (ATA_REG_CMD << 1) + 1;
ap->ioaddr.data_addr = base + (ATA_REG_DATA << 1);
ap->ioaddr.error_addr = base + (ATA_REG_ERR << 1) + 1;
@@ -876,19 +990,15 @@ static int __devinit octeon_cf_probe(struct platform_device *pdev)
ap->ioaddr.ctl_addr = cs1 + (6 << 1) + 1;
octeon_cf_ops.sff_data_xfer = octeon_cf_data_xfer16;
- ap->mwdma_mask = ATA_MWDMA4;
- irq = platform_get_irq(pdev, 0);
- irq_handler = octeon_cf_interrupt;
-
- /* True IDE mode needs delayed work to poll for not-busy. */
- cf_port->wq = create_singlethread_workqueue(DRV_NAME);
- if (!cf_port->wq)
- goto free_cf_port;
- INIT_DELAYED_WORK(&cf_port->delayed_finish,
- octeon_cf_delayed_finish);
+ ap->mwdma_mask = enable_dma ? ATA_MWDMA4 : 0;
+ /* True IDE mode needs a timer to poll for not-busy. */
+ hrtimer_init(&cf_port->delayed_finish, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ cf_port->delayed_finish.function = octeon_cf_delayed_finish;
} else {
/* 16 bit but not True IDE */
+ base = cs0 + 0x800;
octeon_cf_ops.sff_data_xfer = octeon_cf_data_xfer16;
octeon_cf_ops.softreset = octeon_cf_softreset16;
octeon_cf_ops.sff_check_status = octeon_cf_check_status16;
@@ -902,28 +1012,71 @@ static int __devinit octeon_cf_probe(struct platform_device *pdev)
ap->ioaddr.ctl_addr = base + 0xe;
ap->ioaddr.altstatus_addr = base + 0xe;
}
+ cf_port->c0 = ap->ioaddr.ctl_addr;
+
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64);
+ pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
ata_port_desc(ap, "cmd %p ctl %p", base, ap->ioaddr.ctl_addr);
- snprintf(version, sizeof(version), "%s %d bit%s",
- DRV_VERSION,
- (ocd->is16bit) ? 16 : 8,
- (cs1) ? ", True IDE" : "");
- ata_print_version_once(&pdev->dev, version);
+ dev_info(&pdev->dev, "version " DRV_VERSION" %d bit%s.\n",
+ is_16bit ? 16 : 8,
+ cf_port->is_true_ide ? ", True IDE" : "");
- return ata_host_activate(host, irq, irq_handler, 0, &octeon_cf_sht);
+ return ata_host_activate(host, irq, irq_handler,
+ IRQF_SHARED, &octeon_cf_sht);
free_cf_port:
kfree(cf_port);
- return -ENOMEM;
+ return rv;
+}
+
+static void octeon_cf_shutdown(struct device *dev)
+{
+ union cvmx_mio_boot_dma_cfgx dma_cfg;
+ union cvmx_mio_boot_dma_intx dma_int;
+
+ struct octeon_cf_port *cf_port = dev->platform_data;
+
+ if (cf_port->dma_base) {
+ /* Stop and clear the dma engine. */
+ dma_cfg.u64 = 0;
+ dma_cfg.s.size = -1;
+ cvmx_write_csr(cf_port->dma_base + DMA_CFG, dma_cfg.u64);
+
+ /* Disable the interrupt. */
+ dma_int.u64 = 0;
+ cvmx_write_csr(cf_port->dma_base + DMA_INT_EN, dma_int.u64);
+
+ /* Clear the DMA complete status */
+ dma_int.s.done = 1;
+ cvmx_write_csr(cf_port->dma_base + DMA_INT, dma_int.u64);
+
+ __raw_writeb(0, cf_port->c0);
+ udelay(20);
+ __raw_writeb(ATA_SRST, cf_port->c0);
+ udelay(20);
+ __raw_writeb(0, cf_port->c0);
+ mdelay(100);
+ }
}
+static struct of_device_id octeon_cf_match[] = {
+ {
+ .compatible = "cavium,ebt3000-compact-flash",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, octeon_i2c_match);
+
static struct platform_driver octeon_cf_driver = {
.probe = octeon_cf_probe,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
+ .of_match_table = octeon_cf_match,
+ .shutdown = octeon_cf_shutdown
},
};
diff --git a/drivers/ata/pata_of_platform.c b/drivers/ata/pata_of_platform.c
index 1654dc27e7f..e5b234c370f 100644
--- a/drivers/ata/pata_of_platform.c
+++ b/drivers/ata/pata_of_platform.c
@@ -14,6 +14,7 @@
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/ata_platform.h>
+#include <linux/libata.h>
static int __devinit pata_of_platform_probe(struct platform_device *ofdev)
{
@@ -76,11 +77,6 @@ static int __devinit pata_of_platform_probe(struct platform_device *ofdev)
reg_shift, pio_mask);
}
-static int __devexit pata_of_platform_remove(struct platform_device *ofdev)
-{
- return __pata_platform_remove(&ofdev->dev);
-}
-
static struct of_device_id pata_of_platform_match[] = {
{ .compatible = "ata-generic", },
{ .compatible = "electra-ide", },
@@ -95,7 +91,7 @@ static struct platform_driver pata_of_platform_driver = {
.of_match_table = pata_of_platform_match,
},
.probe = pata_of_platform_probe,
- .remove = __devexit_p(pata_of_platform_remove),
+ .remove = ata_platform_remove_one,
};
module_platform_driver(pata_of_platform_driver);
diff --git a/drivers/ata/pata_palmld.c b/drivers/ata/pata_palmld.c
index 5ff31b68135..f9f79fc04a8 100644
--- a/drivers/ata/pata_palmld.c
+++ b/drivers/ata/pata_palmld.c
@@ -111,9 +111,7 @@ err1:
static __devexit int palmld_pata_remove(struct platform_device *dev)
{
- struct ata_host *host = platform_get_drvdata(dev);
-
- ata_host_detach(host);
+ ata_platform_remove_one(dev);
/* power down the HDD */
gpio_set_value(GPIO_NR_PALMLD_IDE_PWEN, 0);
diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c
index f1848aeda78..f4372d0c7ce 100644
--- a/drivers/ata/pata_platform.c
+++ b/drivers/ata/pata_platform.c
@@ -178,23 +178,6 @@ int __devinit __pata_platform_probe(struct device *dev,
}
EXPORT_SYMBOL_GPL(__pata_platform_probe);
-/**
- * __pata_platform_remove - unplug a platform interface
- * @dev: device
- *
- * A platform bus ATA device has been unplugged. Perform the needed
- * cleanup. Also called on module unload for any active devices.
- */
-int __pata_platform_remove(struct device *dev)
-{
- struct ata_host *host = dev_get_drvdata(dev);
-
- ata_host_detach(host);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(__pata_platform_remove);
-
static int __devinit pata_platform_probe(struct platform_device *pdev)
{
struct resource *io_res;
@@ -242,14 +225,9 @@ static int __devinit pata_platform_probe(struct platform_device *pdev)
pio_mask);
}
-static int __devexit pata_platform_remove(struct platform_device *pdev)
-{
- return __pata_platform_remove(&pdev->dev);
-}
-
static struct platform_driver pata_platform_driver = {
.probe = pata_platform_probe,
- .remove = __devexit_p(pata_platform_remove),
+ .remove = ata_platform_remove_one,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
diff --git a/drivers/ata/pata_rdc.c b/drivers/ata/pata_rdc.c
index 32a3499e83e..e71f998dd90 100644
--- a/drivers/ata/pata_rdc.c
+++ b/drivers/ata/pata_rdc.c
@@ -327,7 +327,6 @@ static int __devinit rdc_init_one(struct pci_dev *pdev,
struct device *dev = &pdev->dev;
struct ata_port_info port_info[2];
const struct ata_port_info *ppi[] = { &port_info[0], &port_info[1] };
- unsigned long port_flags;
struct ata_host *host;
struct rdc_host_priv *hpriv;
int rc;
@@ -337,8 +336,6 @@ static int __devinit rdc_init_one(struct pci_dev *pdev,
port_info[0] = rdc_port_info;
port_info[1] = rdc_port_info;
- port_flags = port_info[0].flags;
-
/* enable device and prepare host */
rc = pcim_enable_device(pdev);
if (rc)
diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c
index 937aeb34b31..2e391730e8b 100755..100644
--- a/drivers/ata/sata_dwc_460ex.c
+++ b/drivers/ata/sata_dwc_460ex.c
@@ -43,6 +43,7 @@
/* These two are defined in "libata.h" */
#undef DRV_NAME
#undef DRV_VERSION
+
#define DRV_NAME "sata-dwc"
#define DRV_VERSION "1.3"
diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c
index 400bf1c3e98..dc7d78eecb1 100644
--- a/drivers/ata/sata_highbank.c
+++ b/drivers/ata/sata_highbank.c
@@ -213,7 +213,7 @@ static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class,
/* clear D2H reception area to properly wait for D2H FIS */
ata_tf_init(link->device, &tf);
- tf.command = 0x80;
+ tf.command = ATA_BUSY;
ata_tf_to_fis(&tf, 0, 0, d2h_fis);
do {
@@ -368,16 +368,6 @@ err0:
return rc;
}
-static int __devexit ahci_highbank_remove(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct ata_host *host = dev_get_drvdata(dev);
-
- ata_host_detach(host);
-
- return 0;
-}
-
#ifdef CONFIG_PM_SLEEP
static int ahci_highbank_suspend(struct device *dev)
{
@@ -432,7 +422,7 @@ SIMPLE_DEV_PM_OPS(ahci_highbank_pm_ops,
ahci_highbank_suspend, ahci_highbank_resume);
static struct platform_driver ahci_highbank_driver = {
- .remove = __devexit_p(ahci_highbank_remove),
+ .remove = ata_platform_remove_one,
.driver = {
.name = "highbank-ahci",
.owner = THIS_MODULE,
diff --git a/drivers/ata/sata_inic162x.c b/drivers/ata/sata_inic162x.c
index dc35f4d42b8..1e6827c8942 100644
--- a/drivers/ata/sata_inic162x.c
+++ b/drivers/ata/sata_inic162x.c
@@ -273,12 +273,10 @@ static void inic_reset_port(void __iomem *port_base)
static int inic_scr_read(struct ata_link *link, unsigned sc_reg, u32 *val)
{
void __iomem *scr_addr = inic_port_base(link->ap) + PORT_SCR;
- void __iomem *addr;
if (unlikely(sc_reg >= ARRAY_SIZE(scr_map)))
return -EINVAL;
- addr = scr_addr + scr_map[sc_reg] * 4;
*val = readl(scr_addr + scr_map[sc_reg] * 4);
/* this controller has stuck DIAG.N, ignore it */
diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c
index 489c8176832..fb0dd87f889 100644
--- a/drivers/ata/sata_promise.c
+++ b/drivers/ata/sata_promise.c
@@ -147,6 +147,10 @@ struct pdc_port_priv {
dma_addr_t pkt_dma;
};
+struct pdc_host_priv {
+ spinlock_t hard_reset_lock;
+};
+
static int pdc_sata_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val);
static int pdc_sata_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val);
static int pdc_ata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
@@ -801,9 +805,10 @@ static void pdc_hard_reset_port(struct ata_port *ap)
void __iomem *host_mmio = ap->host->iomap[PDC_MMIO_BAR];
void __iomem *pcictl_b1_mmio = host_mmio + PDC_PCI_CTL + 1;
unsigned int ata_no = pdc_ata_port_to_ata_no(ap);
+ struct pdc_host_priv *hpriv = ap->host->private_data;
u8 tmp;
- spin_lock(&ap->host->lock);
+ spin_lock(&hpriv->hard_reset_lock);
tmp = readb(pcictl_b1_mmio);
tmp &= ~(0x10 << ata_no);
@@ -814,7 +819,7 @@ static void pdc_hard_reset_port(struct ata_port *ap)
writeb(tmp, pcictl_b1_mmio);
readb(pcictl_b1_mmio); /* flush */
- spin_unlock(&ap->host->lock);
+ spin_unlock(&hpriv->hard_reset_lock);
}
static int pdc_sata_hardreset(struct ata_link *link, unsigned int *class,
@@ -1182,6 +1187,7 @@ static int pdc_ata_init_one(struct pci_dev *pdev,
const struct ata_port_info *pi = &pdc_port_info[ent->driver_data];
const struct ata_port_info *ppi[PDC_MAX_PORTS];
struct ata_host *host;
+ struct pdc_host_priv *hpriv;
void __iomem *host_mmio;
int n_ports, i, rc;
int is_sataii_tx4;
@@ -1218,6 +1224,11 @@ static int pdc_ata_init_one(struct pci_dev *pdev,
dev_err(&pdev->dev, "failed to allocate host\n");
return -ENOMEM;
}
+ hpriv = devm_kzalloc(&pdev->dev, sizeof *hpriv, GFP_KERNEL);
+ if (!hpriv)
+ return -ENOMEM;
+ spin_lock_init(&hpriv->hard_reset_lock);
+ host->private_data = hpriv;
host->iomap = pcim_iomap_table(pdev);
is_sataii_tx4 = pdc_is_sataii_tx4(pi->flags);
diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c
index a5f2a563a26..59f0d630d63 100644
--- a/drivers/ata/sata_sil24.c
+++ b/drivers/ata/sata_sil24.c
@@ -506,8 +506,6 @@ static int sil24_scr_read(struct ata_link *link, unsigned sc_reg, u32 *val)
void __iomem *scr_addr = sil24_port_base(link->ap) + PORT_SCONTROL;
if (sc_reg < ARRAY_SIZE(sil24_scr_map)) {
- void __iomem *addr;
- addr = scr_addr + sil24_scr_map[sc_reg] * 4;
*val = readl(scr_addr + sil24_scr_map[sc_reg] * 4);
return 0;
}
@@ -519,8 +517,6 @@ static int sil24_scr_write(struct ata_link *link, unsigned sc_reg, u32 val)
void __iomem *scr_addr = sil24_port_base(link->ap) + PORT_SCONTROL;
if (sc_reg < ARRAY_SIZE(sil24_scr_map)) {
- void __iomem *addr;
- addr = scr_addr + sil24_scr_map[sc_reg] * 4;
writel(val, scr_addr + sil24_scr_map[sc_reg] * 4);
return 0;
}
diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c
index 12260559316..7b7127a58f5 100644
--- a/drivers/ata/sata_sx4.c
+++ b/drivers/ata/sata_sx4.c
@@ -315,9 +315,8 @@ static int pdc_port_start(struct ata_port *ap)
return 0;
}
-static inline void pdc20621_ata_sg(struct ata_taskfile *tf, u8 *buf,
- unsigned int portno,
- unsigned int total_len)
+static inline void pdc20621_ata_sg(u8 *buf, unsigned int portno,
+ unsigned int total_len)
{
u32 addr;
unsigned int dw = PDC_DIMM_APKT_PRD >> 2;
@@ -337,9 +336,8 @@ static inline void pdc20621_ata_sg(struct ata_taskfile *tf, u8 *buf,
buf32[dw], buf32[dw + 1]);
}
-static inline void pdc20621_host_sg(struct ata_taskfile *tf, u8 *buf,
- unsigned int portno,
- unsigned int total_len)
+static inline void pdc20621_host_sg(u8 *buf, unsigned int portno,
+ unsigned int total_len)
{
u32 addr;
unsigned int dw = PDC_DIMM_HPKT_PRD >> 2;
@@ -486,10 +484,10 @@ static void pdc20621_dma_prep(struct ata_queued_cmd *qc)
/*
* Build ATA, host DMA packets
*/
- pdc20621_host_sg(&qc->tf, &pp->dimm_buf[0], portno, total_len);
+ pdc20621_host_sg(&pp->dimm_buf[0], portno, total_len);
pdc20621_host_pkt(&qc->tf, &pp->dimm_buf[0], portno);
- pdc20621_ata_sg(&qc->tf, &pp->dimm_buf[0], portno, total_len);
+ pdc20621_ata_sg(&pp->dimm_buf[0], portno, total_len);
i = pdc20621_ata_pkt(&qc->tf, qc->dev->devno, &pp->dimm_buf[0], portno);
if (qc->tf.flags & ATA_TFLAG_LBA48)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 417913974df..a235085e343 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -171,6 +171,27 @@ ssize_t device_show_int(struct device *dev,
}
EXPORT_SYMBOL_GPL(device_show_int);
+ssize_t device_store_bool(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct dev_ext_attribute *ea = to_ext_attr(attr);
+
+ if (strtobool(buf, ea->var) < 0)
+ return -EINVAL;
+
+ return size;
+}
+EXPORT_SYMBOL_GPL(device_store_bool);
+
+ssize_t device_show_bool(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct dev_ext_attribute *ea = to_ext_attr(attr);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", *(bool *)(ea->var));
+}
+EXPORT_SYMBOL_GPL(device_show_bool);
+
/**
* device_release - free device structure.
* @kobj: device's kobject.
diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c
index 3fbedc75e7c..0ce39a33b3c 100644
--- a/drivers/base/dma-mapping.c
+++ b/drivers/base/dma-mapping.c
@@ -218,6 +218,8 @@ void dmam_release_declared_memory(struct device *dev)
}
EXPORT_SYMBOL(dmam_release_declared_memory);
+#endif
+
/*
* Create scatter-list for the already allocated DMA buffer.
*/
@@ -236,8 +238,6 @@ int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
}
EXPORT_SYMBOL(dma_common_get_sgtable);
-#endif
-
/*
* Create userspace mapping for the DMA-coherent memory.
*/
diff --git a/drivers/base/node.c b/drivers/base/node.c
index 294e3162621..fac124a7e1c 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -227,7 +227,7 @@ static node_registration_func_t __hugetlb_unregister_node;
static inline bool hugetlb_register_node(struct node *node)
{
if (__hugetlb_register_node &&
- node_state(node->dev.id, N_HIGH_MEMORY)) {
+ node_state(node->dev.id, N_MEMORY)) {
__hugetlb_register_node(node);
return true;
}
@@ -644,6 +644,9 @@ static struct node_attr node_state_attr[] = {
#ifdef CONFIG_HIGHMEM
[N_HIGH_MEMORY] = _NODE_ATTR(has_high_memory, N_HIGH_MEMORY),
#endif
+#ifdef CONFIG_MOVABLE_NODE
+ [N_MEMORY] = _NODE_ATTR(has_memory, N_MEMORY),
+#endif
[N_CPU] = _NODE_ATTR(has_cpu, N_CPU),
};
@@ -654,6 +657,9 @@ static struct attribute *node_state_attrs[] = {
#ifdef CONFIG_HIGHMEM
&node_state_attr[N_HIGH_MEMORY].attr.attr,
#endif
+#ifdef CONFIG_MOVABLE_NODE
+ &node_state_attr[N_MEMORY].attr.attr,
+#endif
&node_state_attr[N_CPU].attr.attr,
NULL
};
diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig
index a533af21836..d7b56a88c9f 100644
--- a/drivers/bcma/Kconfig
+++ b/drivers/bcma/Kconfig
@@ -65,6 +65,15 @@ config BCMA_DRIVER_GMAC_CMN
If unsure, say N
+config BCMA_DRIVER_GPIO
+ bool "BCMA GPIO driver"
+ depends on BCMA
+ select GPIOLIB
+ help
+ Driver to provide access to the GPIO pins of the bcma bus.
+
+ If unsure, say N
+
config BCMA_DEBUG
bool "BCMA debugging"
depends on BCMA
diff --git a/drivers/bcma/Makefile b/drivers/bcma/Makefile
index 8ad42d41b2f..734b32f09c0 100644
--- a/drivers/bcma/Makefile
+++ b/drivers/bcma/Makefile
@@ -6,6 +6,7 @@ bcma-y += driver_pci.o
bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) += driver_pci_host.o
bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o
bcma-$(CONFIG_BCMA_DRIVER_GMAC_CMN) += driver_gmac_cmn.o
+bcma-$(CONFIG_BCMA_DRIVER_GPIO) += driver_gpio.o
bcma-$(CONFIG_BCMA_HOST_PCI) += host_pci.o
bcma-$(CONFIG_BCMA_HOST_SOC) += host_soc.o
obj-$(CONFIG_BCMA) += bcma.o
diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h
index 537ae53231c..4a2d72ec6d4 100644
--- a/drivers/bcma/bcma_private.h
+++ b/drivers/bcma/bcma_private.h
@@ -91,4 +91,14 @@ bool __devinit bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc);
void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc);
#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
+#ifdef CONFIG_BCMA_DRIVER_GPIO
+/* driver_gpio.c */
+int bcma_gpio_init(struct bcma_drv_cc *cc);
+#else
+static inline int bcma_gpio_init(struct bcma_drv_cc *cc)
+{
+ return -ENOTSUPP;
+}
+#endif /* CONFIG_BCMA_DRIVER_GPIO */
+
#endif
diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c
index dc96dd8ebff..e461ad25fda 100644
--- a/drivers/bcma/driver_chipcommon.c
+++ b/drivers/bcma/driver_chipcommon.c
@@ -114,6 +114,8 @@ void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc)
if (cc->early_setup_done)
return;
+ spin_lock_init(&cc->gpio_lock);
+
if (cc->core->id.rev >= 11)
cc->status = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT);
cc->capabilities = bcma_cc_read32(cc, BCMA_CC_CAP);
@@ -202,28 +204,97 @@ u32 bcma_chipco_gpio_in(struct bcma_drv_cc *cc, u32 mask)
u32 bcma_chipco_gpio_out(struct bcma_drv_cc *cc, u32 mask, u32 value)
{
- return bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUT, mask, value);
+ unsigned long flags;
+ u32 res;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUT, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
}
u32 bcma_chipco_gpio_outen(struct bcma_drv_cc *cc, u32 mask, u32 value)
{
- return bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUTEN, mask, value);
+ unsigned long flags;
+ u32 res;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUTEN, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
}
+/*
+ * If the bit is set to 0, chipcommon controlls this GPIO,
+ * if the bit is set to 1, it is used by some part of the chip and not our code.
+ */
u32 bcma_chipco_gpio_control(struct bcma_drv_cc *cc, u32 mask, u32 value)
{
- return bcma_cc_write32_masked(cc, BCMA_CC_GPIOCTL, mask, value);
+ unsigned long flags;
+ u32 res;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOCTL, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
}
EXPORT_SYMBOL_GPL(bcma_chipco_gpio_control);
u32 bcma_chipco_gpio_intmask(struct bcma_drv_cc *cc, u32 mask, u32 value)
{
- return bcma_cc_write32_masked(cc, BCMA_CC_GPIOIRQ, mask, value);
+ unsigned long flags;
+ u32 res;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOIRQ, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
}
u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, u32 value)
{
- return bcma_cc_write32_masked(cc, BCMA_CC_GPIOPOL, mask, value);
+ unsigned long flags;
+ u32 res;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOPOL, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
+}
+
+u32 bcma_chipco_gpio_pullup(struct bcma_drv_cc *cc, u32 mask, u32 value)
+{
+ unsigned long flags;
+ u32 res;
+
+ if (cc->core->id.rev < 20)
+ return 0;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOPULLUP, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
+}
+
+u32 bcma_chipco_gpio_pulldown(struct bcma_drv_cc *cc, u32 mask, u32 value)
+{
+ unsigned long flags;
+ u32 res;
+
+ if (cc->core->id.rev < 20)
+ return 0;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOPULLDOWN, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
}
#ifdef CONFIG_BCMA_DRIVER_MIPS
diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c
new file mode 100644
index 00000000000..9a6f585da2d
--- /dev/null
+++ b/drivers/bcma/driver_gpio.c
@@ -0,0 +1,98 @@
+/*
+ * Broadcom specific AMBA
+ * GPIO driver
+ *
+ * Copyright 2011, Broadcom Corporation
+ * Copyright 2012, Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/gpio.h>
+#include <linux/export.h>
+#include <linux/bcma/bcma.h>
+
+#include "bcma_private.h"
+
+static inline struct bcma_drv_cc *bcma_gpio_get_cc(struct gpio_chip *chip)
+{
+ return container_of(chip, struct bcma_drv_cc, gpio);
+}
+
+static int bcma_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+ struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
+
+ return !!bcma_chipco_gpio_in(cc, 1 << gpio);
+}
+
+static void bcma_gpio_set_value(struct gpio_chip *chip, unsigned gpio,
+ int value)
+{
+ struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
+
+ bcma_chipco_gpio_out(cc, 1 << gpio, value ? 1 << gpio : 0);
+}
+
+static int bcma_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+{
+ struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
+
+ bcma_chipco_gpio_outen(cc, 1 << gpio, 0);
+ return 0;
+}
+
+static int bcma_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
+ int value)
+{
+ struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
+
+ bcma_chipco_gpio_outen(cc, 1 << gpio, 1 << gpio);
+ bcma_chipco_gpio_out(cc, 1 << gpio, value ? 1 << gpio : 0);
+ return 0;
+}
+
+static int bcma_gpio_request(struct gpio_chip *chip, unsigned gpio)
+{
+ struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
+
+ bcma_chipco_gpio_control(cc, 1 << gpio, 0);
+ /* clear pulldown */
+ bcma_chipco_gpio_pulldown(cc, 1 << gpio, 0);
+ /* Set pullup */
+ bcma_chipco_gpio_pullup(cc, 1 << gpio, 1 << gpio);
+
+ return 0;
+}
+
+static void bcma_gpio_free(struct gpio_chip *chip, unsigned gpio)
+{
+ struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
+
+ /* clear pullup */
+ bcma_chipco_gpio_pullup(cc, 1 << gpio, 0);
+}
+
+int bcma_gpio_init(struct bcma_drv_cc *cc)
+{
+ struct gpio_chip *chip = &cc->gpio;
+
+ chip->label = "bcma_gpio";
+ chip->owner = THIS_MODULE;
+ chip->request = bcma_gpio_request;
+ chip->free = bcma_gpio_free;
+ chip->get = bcma_gpio_get_value;
+ chip->set = bcma_gpio_set_value;
+ chip->direction_input = bcma_gpio_direction_input;
+ chip->direction_output = bcma_gpio_direction_output;
+ chip->ngpio = 16;
+ /* There is just one SoC in one device and its GPIO addresses should be
+ * deterministic to address them more easily. The other buses could get
+ * a random base number. */
+ if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC)
+ chip->base = 0;
+ else
+ chip->base = -1;
+
+ return gpiochip_add(chip);
+}
diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
index debd4f142f9..53ba20ca17e 100644
--- a/drivers/bcma/main.c
+++ b/drivers/bcma/main.c
@@ -164,6 +164,11 @@ static int bcma_register_cores(struct bcma_bus *bus)
bcma_err(bus, "Error registering NAND flash\n");
}
#endif
+ err = bcma_gpio_init(&bus->drv_cc);
+ if (err == -ENOTSUPP)
+ bcma_debug(bus, "GPIO driver not activated\n");
+ else if (err)
+ bcma_err(bus, "Error registering GPIO driver: %i\n", err);
if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
err = bcma_chipco_watchdog_register(&bus->drv_cc);
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index a0c84bb3085..053201b062a 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -3789,7 +3789,7 @@ static int handle_one_recv_msg(ipmi_smi_t intf,
} else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
&& (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD)) {
- /* It's an asyncronous event. */
+ /* It's an asynchronous event. */
requeue = handle_read_event_rsp(intf, msg);
} else {
/* It's a response from the local BMC. */
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index 20ab5b3a891..cfdfecd5bc7 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -155,7 +155,7 @@ enum si_stat_indexes {
/* Number of watchdog pretimeouts. */
SI_STAT_watchdog_pretimeouts,
- /* Number of asyncronous messages received. */
+ /* Number of asynchronous messages received. */
SI_STAT_incoming_messages,
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
index d0d824ebf2c..1cd49241e60 100644
--- a/drivers/char/ppdev.c
+++ b/drivers/char/ppdev.c
@@ -251,12 +251,8 @@ static ssize_t pp_write (struct file * file, const char __user * buf,
break;
}
- if (signal_pending (current)) {
- if (!bytes_written) {
- bytes_written = -EINTR;
- }
+ if (signal_pending (current))
break;
- }
cond_resched();
}
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 823f62d900b..a47e6ee98b8 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -64,3 +64,5 @@ config CLK_TWL6040
as functional clock.
endmenu
+
+source "drivers/clk/mvebu/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 4e1ccb1e661..ee90e87e767 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_PLAT_SPEAR) += spear/
obj-$(CONFIG_ARCH_U300) += clk-u300.o
obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/
obj-$(CONFIG_ARCH_PRIMA2) += clk-prima2.o
+obj-$(CONFIG_PLAT_ORION) += mvebu/
ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_MMP) += mmp/
endif
diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig
new file mode 100644
index 00000000000..57323fd15ec
--- /dev/null
+++ b/drivers/clk/mvebu/Kconfig
@@ -0,0 +1,8 @@
+config MVEBU_CLK_CORE
+ bool
+
+config MVEBU_CLK_CPU
+ bool
+
+config MVEBU_CLK_GATING
+ bool
diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile
new file mode 100644
index 00000000000..58df3dc4936
--- /dev/null
+++ b/drivers/clk/mvebu/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_MVEBU_CLK_CORE) += clk.o clk-core.o
+obj-$(CONFIG_MVEBU_CLK_CPU) += clk-cpu.o
+obj-$(CONFIG_MVEBU_CLK_GATING) += clk-gating-ctrl.o
diff --git a/drivers/clk/mvebu/clk-core.c b/drivers/clk/mvebu/clk-core.c
new file mode 100644
index 00000000000..69056a7479e
--- /dev/null
+++ b/drivers/clk/mvebu/clk-core.c
@@ -0,0 +1,675 @@
+/*
+ * Marvell EBU clock core handling defined at reset
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include "clk-core.h"
+
+struct core_ratio {
+ int id;
+ const char *name;
+};
+
+struct core_clocks {
+ u32 (*get_tclk_freq)(void __iomem *sar);
+ u32 (*get_cpu_freq)(void __iomem *sar);
+ void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div);
+ const struct core_ratio *ratios;
+ int num_ratios;
+};
+
+static struct clk_onecell_data clk_data;
+
+static void __init mvebu_clk_core_setup(struct device_node *np,
+ struct core_clocks *coreclk)
+{
+ const char *tclk_name = "tclk";
+ const char *cpuclk_name = "cpuclk";
+ void __iomem *base;
+ unsigned long rate;
+ int n;
+
+ base = of_iomap(np, 0);
+ if (WARN_ON(!base))
+ return;
+
+ /*
+ * Allocate struct for TCLK, cpu clk, and core ratio clocks
+ */
+ clk_data.clk_num = 2 + coreclk->num_ratios;
+ clk_data.clks = kzalloc(clk_data.clk_num * sizeof(struct clk *),
+ GFP_KERNEL);
+ if (WARN_ON(!clk_data.clks))
+ return;
+
+ /*
+ * Register TCLK
+ */
+ of_property_read_string_index(np, "clock-output-names", 0,
+ &tclk_name);
+ rate = coreclk->get_tclk_freq(base);
+ clk_data.clks[0] = clk_register_fixed_rate(NULL, tclk_name, NULL,
+ CLK_IS_ROOT, rate);
+ WARN_ON(IS_ERR(clk_data.clks[0]));
+
+ /*
+ * Register CPU clock
+ */
+ of_property_read_string_index(np, "clock-output-names", 1,
+ &cpuclk_name);
+ rate = coreclk->get_cpu_freq(base);
+ clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL,
+ CLK_IS_ROOT, rate);
+ WARN_ON(IS_ERR(clk_data.clks[1]));
+
+ /*
+ * Register fixed-factor clocks derived from CPU clock
+ */
+ for (n = 0; n < coreclk->num_ratios; n++) {
+ const char *rclk_name = coreclk->ratios[n].name;
+ int mult, div;
+
+ of_property_read_string_index(np, "clock-output-names",
+ 2+n, &rclk_name);
+ coreclk->get_clk_ratio(base, coreclk->ratios[n].id,
+ &mult, &div);
+ clk_data.clks[2+n] = clk_register_fixed_factor(NULL, rclk_name,
+ cpuclk_name, 0, mult, div);
+ WARN_ON(IS_ERR(clk_data.clks[2+n]));
+ };
+
+ /*
+ * SAR register isn't needed anymore
+ */
+ iounmap(base);
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+}
+
+#ifdef CONFIG_MACH_ARMADA_370_XP
+/*
+ * Armada 370/XP Sample At Reset is a 64 bit bitfiled split in two
+ * register of 32 bits
+ */
+
+#define SARL 0 /* Low part [0:31] */
+#define SARL_AXP_PCLK_FREQ_OPT 21
+#define SARL_AXP_PCLK_FREQ_OPT_MASK 0x7
+#define SARL_A370_PCLK_FREQ_OPT 11
+#define SARL_A370_PCLK_FREQ_OPT_MASK 0xF
+#define SARL_AXP_FAB_FREQ_OPT 24
+#define SARL_AXP_FAB_FREQ_OPT_MASK 0xF
+#define SARL_A370_FAB_FREQ_OPT 15
+#define SARL_A370_FAB_FREQ_OPT_MASK 0x1F
+#define SARL_A370_TCLK_FREQ_OPT 20
+#define SARL_A370_TCLK_FREQ_OPT_MASK 0x1
+#define SARH 4 /* High part [32:63] */
+#define SARH_AXP_PCLK_FREQ_OPT (52-32)
+#define SARH_AXP_PCLK_FREQ_OPT_MASK 0x1
+#define SARH_AXP_PCLK_FREQ_OPT_SHIFT 3
+#define SARH_AXP_FAB_FREQ_OPT (51-32)
+#define SARH_AXP_FAB_FREQ_OPT_MASK 0x1
+#define SARH_AXP_FAB_FREQ_OPT_SHIFT 4
+
+static const u32 __initconst armada_370_tclk_frequencies[] = {
+ 16600000,
+ 20000000,
+};
+
+static u32 __init armada_370_get_tclk_freq(void __iomem *sar)
+{
+ u8 tclk_freq_select = 0;
+
+ tclk_freq_select = ((readl(sar) >> SARL_A370_TCLK_FREQ_OPT) &
+ SARL_A370_TCLK_FREQ_OPT_MASK);
+ return armada_370_tclk_frequencies[tclk_freq_select];
+}
+
+static const u32 __initconst armada_370_cpu_frequencies[] = {
+ 400000000,
+ 533000000,
+ 667000000,
+ 800000000,
+ 1000000000,
+ 1067000000,
+ 1200000000,
+};
+
+static u32 __init armada_370_get_cpu_freq(void __iomem *sar)
+{
+ u32 cpu_freq;
+ u8 cpu_freq_select = 0;
+
+ cpu_freq_select = ((readl(sar) >> SARL_A370_PCLK_FREQ_OPT) &
+ SARL_A370_PCLK_FREQ_OPT_MASK);
+ if (cpu_freq_select > ARRAY_SIZE(armada_370_cpu_frequencies)) {
+ pr_err("CPU freq select unsuported %d\n", cpu_freq_select);
+ cpu_freq = 0;
+ } else
+ cpu_freq = armada_370_cpu_frequencies[cpu_freq_select];
+
+ return cpu_freq;
+}
+
+enum { A370_XP_NBCLK, A370_XP_HCLK, A370_XP_DRAMCLK };
+
+static const struct core_ratio __initconst armada_370_xp_core_ratios[] = {
+ { .id = A370_XP_NBCLK, .name = "nbclk" },
+ { .id = A370_XP_HCLK, .name = "hclk" },
+ { .id = A370_XP_DRAMCLK, .name = "dramclk" },
+};
+
+static const int __initconst armada_370_xp_nbclk_ratios[32][2] = {
+ {0, 1}, {1, 2}, {2, 2}, {2, 2},
+ {1, 2}, {1, 2}, {1, 1}, {2, 3},
+ {0, 1}, {1, 2}, {2, 4}, {0, 1},
+ {1, 2}, {0, 1}, {0, 1}, {2, 2},
+ {0, 1}, {0, 1}, {0, 1}, {1, 1},
+ {2, 3}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {1, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+};
+
+static const int __initconst armada_370_xp_hclk_ratios[32][2] = {
+ {0, 1}, {1, 2}, {2, 6}, {2, 3},
+ {1, 3}, {1, 4}, {1, 2}, {2, 6},
+ {0, 1}, {1, 6}, {2, 10}, {0, 1},
+ {1, 4}, {0, 1}, {0, 1}, {2, 5},
+ {0, 1}, {0, 1}, {0, 1}, {1, 2},
+ {2, 6}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {1, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+};
+
+static const int __initconst armada_370_xp_dramclk_ratios[32][2] = {
+ {0, 1}, {1, 2}, {2, 3}, {2, 3},
+ {1, 3}, {1, 2}, {1, 2}, {2, 6},
+ {0, 1}, {1, 3}, {2, 5}, {0, 1},
+ {1, 4}, {0, 1}, {0, 1}, {2, 5},
+ {0, 1}, {0, 1}, {0, 1}, {1, 1},
+ {2, 3}, {0, 1}, {0, 1}, {0, 1},
+ {0, 1}, {0, 1}, {0, 1}, {1, 1},
+ {0, 1}, {0, 1}, {0, 1}, {0, 1},
+};
+
+static void __init armada_370_xp_get_clk_ratio(u32 opt,
+ void __iomem *sar, int id, int *mult, int *div)
+{
+ switch (id) {
+ case A370_XP_NBCLK:
+ *mult = armada_370_xp_nbclk_ratios[opt][0];
+ *div = armada_370_xp_nbclk_ratios[opt][1];
+ break;
+ case A370_XP_HCLK:
+ *mult = armada_370_xp_hclk_ratios[opt][0];
+ *div = armada_370_xp_hclk_ratios[opt][1];
+ break;
+ case A370_XP_DRAMCLK:
+ *mult = armada_370_xp_dramclk_ratios[opt][0];
+ *div = armada_370_xp_dramclk_ratios[opt][1];
+ break;
+ }
+}
+
+static void __init armada_370_get_clk_ratio(
+ void __iomem *sar, int id, int *mult, int *div)
+{
+ u32 opt = ((readl(sar) >> SARL_A370_FAB_FREQ_OPT) &
+ SARL_A370_FAB_FREQ_OPT_MASK);
+
+ armada_370_xp_get_clk_ratio(opt, sar, id, mult, div);
+}
+
+
+static const struct core_clocks armada_370_core_clocks = {
+ .get_tclk_freq = armada_370_get_tclk_freq,
+ .get_cpu_freq = armada_370_get_cpu_freq,
+ .get_clk_ratio = armada_370_get_clk_ratio,
+ .ratios = armada_370_xp_core_ratios,
+ .num_ratios = ARRAY_SIZE(armada_370_xp_core_ratios),
+};
+
+static const u32 __initconst armada_xp_cpu_frequencies[] = {
+ 1000000000,
+ 1066000000,
+ 1200000000,
+ 1333000000,
+ 1500000000,
+ 1666000000,
+ 1800000000,
+ 2000000000,
+ 667000000,
+ 0,
+ 800000000,
+ 1600000000,
+};
+
+/* For Armada XP TCLK frequency is fix: 250MHz */
+static u32 __init armada_xp_get_tclk_freq(void __iomem *sar)
+{
+ return 250 * 1000 * 1000;
+}
+
+static u32 __init armada_xp_get_cpu_freq(void __iomem *sar)
+{
+ u32 cpu_freq;
+ u8 cpu_freq_select = 0;
+
+ cpu_freq_select = ((readl(sar) >> SARL_AXP_PCLK_FREQ_OPT) &
+ SARL_AXP_PCLK_FREQ_OPT_MASK);
+ /*
+ * The upper bit is not contiguous to the other ones and
+ * located in the high part of the SAR registers
+ */
+ cpu_freq_select |= (((readl(sar+4) >> SARH_AXP_PCLK_FREQ_OPT) &
+ SARH_AXP_PCLK_FREQ_OPT_MASK)
+ << SARH_AXP_PCLK_FREQ_OPT_SHIFT);
+ if (cpu_freq_select > ARRAY_SIZE(armada_xp_cpu_frequencies)) {
+ pr_err("CPU freq select unsuported: %d\n", cpu_freq_select);
+ cpu_freq = 0;
+ } else
+ cpu_freq = armada_xp_cpu_frequencies[cpu_freq_select];
+
+ return cpu_freq;
+}
+
+static void __init armada_xp_get_clk_ratio(
+ void __iomem *sar, int id, int *mult, int *div)
+{
+
+ u32 opt = ((readl(sar) >> SARL_AXP_FAB_FREQ_OPT) &
+ SARL_AXP_FAB_FREQ_OPT_MASK);
+ /*
+ * The upper bit is not contiguous to the other ones and
+ * located in the high part of the SAR registers
+ */
+ opt |= (((readl(sar+4) >> SARH_AXP_FAB_FREQ_OPT) &
+ SARH_AXP_FAB_FREQ_OPT_MASK)
+ << SARH_AXP_FAB_FREQ_OPT_SHIFT);
+
+ armada_370_xp_get_clk_ratio(opt, sar, id, mult, div);
+}
+
+static const struct core_clocks armada_xp_core_clocks = {
+ .get_tclk_freq = armada_xp_get_tclk_freq,
+ .get_cpu_freq = armada_xp_get_cpu_freq,
+ .get_clk_ratio = armada_xp_get_clk_ratio,
+ .ratios = armada_370_xp_core_ratios,
+ .num_ratios = ARRAY_SIZE(armada_370_xp_core_ratios),
+};
+
+#endif /* CONFIG_MACH_ARMADA_370_XP */
+
+/*
+ * Dove PLL sample-at-reset configuration
+ *
+ * SAR0[8:5] : CPU frequency
+ * 5 = 1000 MHz
+ * 6 = 933 MHz
+ * 7 = 933 MHz
+ * 8 = 800 MHz
+ * 9 = 800 MHz
+ * 10 = 800 MHz
+ * 11 = 1067 MHz
+ * 12 = 667 MHz
+ * 13 = 533 MHz
+ * 14 = 400 MHz
+ * 15 = 333 MHz
+ * others reserved.
+ *
+ * SAR0[11:9] : CPU to L2 Clock divider ratio
+ * 0 = (1/1) * CPU
+ * 2 = (1/2) * CPU
+ * 4 = (1/3) * CPU
+ * 6 = (1/4) * CPU
+ * others reserved.
+ *
+ * SAR0[15:12] : CPU to DDR DRAM Clock divider ratio
+ * 0 = (1/1) * CPU
+ * 2 = (1/2) * CPU
+ * 3 = (2/5) * CPU
+ * 4 = (1/3) * CPU
+ * 6 = (1/4) * CPU
+ * 8 = (1/5) * CPU
+ * 10 = (1/6) * CPU
+ * 12 = (1/7) * CPU
+ * 14 = (1/8) * CPU
+ * 15 = (1/10) * CPU
+ * others reserved.
+ *
+ * SAR0[24:23] : TCLK frequency
+ * 0 = 166 MHz
+ * 1 = 125 MHz
+ * others reserved.
+ */
+#ifdef CONFIG_ARCH_DOVE
+#define SAR_DOVE_CPU_FREQ 5
+#define SAR_DOVE_CPU_FREQ_MASK 0xf
+#define SAR_DOVE_L2_RATIO 9
+#define SAR_DOVE_L2_RATIO_MASK 0x7
+#define SAR_DOVE_DDR_RATIO 12
+#define SAR_DOVE_DDR_RATIO_MASK 0xf
+#define SAR_DOVE_TCLK_FREQ 23
+#define SAR_DOVE_TCLK_FREQ_MASK 0x3
+
+static const u32 __initconst dove_tclk_frequencies[] = {
+ 166666667,
+ 125000000,
+ 0, 0
+};
+
+static u32 __init dove_get_tclk_freq(void __iomem *sar)
+{
+ u32 opt = (readl(sar) >> SAR_DOVE_TCLK_FREQ) &
+ SAR_DOVE_TCLK_FREQ_MASK;
+ return dove_tclk_frequencies[opt];
+}
+
+static const u32 __initconst dove_cpu_frequencies[] = {
+ 0, 0, 0, 0, 0,
+ 1000000000,
+ 933333333, 933333333,
+ 800000000, 800000000, 800000000,
+ 1066666667,
+ 666666667,
+ 533333333,
+ 400000000,
+ 333333333
+};
+
+static u32 __init dove_get_cpu_freq(void __iomem *sar)
+{
+ u32 opt = (readl(sar) >> SAR_DOVE_CPU_FREQ) &
+ SAR_DOVE_CPU_FREQ_MASK;
+ return dove_cpu_frequencies[opt];
+}
+
+enum { DOVE_CPU_TO_L2, DOVE_CPU_TO_DDR };
+
+static const struct core_ratio __initconst dove_core_ratios[] = {
+ { .id = DOVE_CPU_TO_L2, .name = "l2clk", },
+ { .id = DOVE_CPU_TO_DDR, .name = "ddrclk", }
+};
+
+static const int __initconst dove_cpu_l2_ratios[8][2] = {
+ { 1, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 },
+ { 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 }
+};
+
+static const int __initconst dove_cpu_ddr_ratios[16][2] = {
+ { 1, 1 }, { 0, 1 }, { 1, 2 }, { 2, 5 },
+ { 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 },
+ { 1, 5 }, { 0, 1 }, { 1, 6 }, { 0, 1 },
+ { 1, 7 }, { 0, 1 }, { 1, 8 }, { 1, 10 }
+};
+
+static void __init dove_get_clk_ratio(
+ void __iomem *sar, int id, int *mult, int *div)
+{
+ switch (id) {
+ case DOVE_CPU_TO_L2:
+ {
+ u32 opt = (readl(sar) >> SAR_DOVE_L2_RATIO) &
+ SAR_DOVE_L2_RATIO_MASK;
+ *mult = dove_cpu_l2_ratios[opt][0];
+ *div = dove_cpu_l2_ratios[opt][1];
+ break;
+ }
+ case DOVE_CPU_TO_DDR:
+ {
+ u32 opt = (readl(sar) >> SAR_DOVE_DDR_RATIO) &
+ SAR_DOVE_DDR_RATIO_MASK;
+ *mult = dove_cpu_ddr_ratios[opt][0];
+ *div = dove_cpu_ddr_ratios[opt][1];
+ break;
+ }
+ }
+}
+
+static const struct core_clocks dove_core_clocks = {
+ .get_tclk_freq = dove_get_tclk_freq,
+ .get_cpu_freq = dove_get_cpu_freq,
+ .get_clk_ratio = dove_get_clk_ratio,
+ .ratios = dove_core_ratios,
+ .num_ratios = ARRAY_SIZE(dove_core_ratios),
+};
+#endif /* CONFIG_ARCH_DOVE */
+
+/*
+ * Kirkwood PLL sample-at-reset configuration
+ * (6180 has different SAR layout than other Kirkwood SoCs)
+ *
+ * SAR0[4:3,22,1] : CPU frequency (6281,6292,6282)
+ * 4 = 600 MHz
+ * 6 = 800 MHz
+ * 7 = 1000 MHz
+ * 9 = 1200 MHz
+ * 12 = 1500 MHz
+ * 13 = 1600 MHz
+ * 14 = 1800 MHz
+ * 15 = 2000 MHz
+ * others reserved.
+ *
+ * SAR0[19,10:9] : CPU to L2 Clock divider ratio (6281,6292,6282)
+ * 1 = (1/2) * CPU
+ * 3 = (1/3) * CPU
+ * 5 = (1/4) * CPU
+ * others reserved.
+ *
+ * SAR0[8:5] : CPU to DDR DRAM Clock divider ratio (6281,6292,6282)
+ * 2 = (1/2) * CPU
+ * 4 = (1/3) * CPU
+ * 6 = (1/4) * CPU
+ * 7 = (2/9) * CPU
+ * 8 = (1/5) * CPU
+ * 9 = (1/6) * CPU
+ * others reserved.
+ *
+ * SAR0[4:2] : Kirkwood 6180 cpu/l2/ddr clock configuration (6180 only)
+ * 5 = [CPU = 600 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/3) * CPU]
+ * 6 = [CPU = 800 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/4) * CPU]
+ * 7 = [CPU = 1000 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/5) * CPU]
+ * others reserved.
+ *
+ * SAR0[21] : TCLK frequency
+ * 0 = 200 MHz
+ * 1 = 166 MHz
+ * others reserved.
+ */
+#ifdef CONFIG_ARCH_KIRKWOOD
+#define SAR_KIRKWOOD_CPU_FREQ(x) \
+ (((x & (1 << 1)) >> 1) | \
+ ((x & (1 << 22)) >> 21) | \
+ ((x & (3 << 3)) >> 1))
+#define SAR_KIRKWOOD_L2_RATIO(x) \
+ (((x & (3 << 9)) >> 9) | \
+ (((x & (1 << 19)) >> 17)))
+#define SAR_KIRKWOOD_DDR_RATIO 5
+#define SAR_KIRKWOOD_DDR_RATIO_MASK 0xf
+#define SAR_MV88F6180_CLK 2
+#define SAR_MV88F6180_CLK_MASK 0x7
+#define SAR_KIRKWOOD_TCLK_FREQ 21
+#define SAR_KIRKWOOD_TCLK_FREQ_MASK 0x1
+
+enum { KIRKWOOD_CPU_TO_L2, KIRKWOOD_CPU_TO_DDR };
+
+static const struct core_ratio __initconst kirkwood_core_ratios[] = {
+ { .id = KIRKWOOD_CPU_TO_L2, .name = "l2clk", },
+ { .id = KIRKWOOD_CPU_TO_DDR, .name = "ddrclk", }
+};
+
+static u32 __init kirkwood_get_tclk_freq(void __iomem *sar)
+{
+ u32 opt = (readl(sar) >> SAR_KIRKWOOD_TCLK_FREQ) &
+ SAR_KIRKWOOD_TCLK_FREQ_MASK;
+ return (opt) ? 166666667 : 200000000;
+}
+
+static const u32 __initconst kirkwood_cpu_frequencies[] = {
+ 0, 0, 0, 0,
+ 600000000,
+ 0,
+ 800000000,
+ 1000000000,
+ 0,
+ 1200000000,
+ 0, 0,
+ 1500000000,
+ 1600000000,
+ 1800000000,
+ 2000000000
+};
+
+static u32 __init kirkwood_get_cpu_freq(void __iomem *sar)
+{
+ u32 opt = SAR_KIRKWOOD_CPU_FREQ(readl(sar));
+ return kirkwood_cpu_frequencies[opt];
+}
+
+static const int __initconst kirkwood_cpu_l2_ratios[8][2] = {
+ { 0, 1 }, { 1, 2 }, { 0, 1 }, { 1, 3 },
+ { 0, 1 }, { 1, 4 }, { 0, 1 }, { 0, 1 }
+};
+
+static const int __initconst kirkwood_cpu_ddr_ratios[16][2] = {
+ { 0, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 },
+ { 1, 3 }, { 0, 1 }, { 1, 4 }, { 2, 9 },
+ { 1, 5 }, { 1, 6 }, { 0, 1 }, { 0, 1 },
+ { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }
+};
+
+static void __init kirkwood_get_clk_ratio(
+ void __iomem *sar, int id, int *mult, int *div)
+{
+ switch (id) {
+ case KIRKWOOD_CPU_TO_L2:
+ {
+ u32 opt = SAR_KIRKWOOD_L2_RATIO(readl(sar));
+ *mult = kirkwood_cpu_l2_ratios[opt][0];
+ *div = kirkwood_cpu_l2_ratios[opt][1];
+ break;
+ }
+ case KIRKWOOD_CPU_TO_DDR:
+ {
+ u32 opt = (readl(sar) >> SAR_KIRKWOOD_DDR_RATIO) &
+ SAR_KIRKWOOD_DDR_RATIO_MASK;
+ *mult = kirkwood_cpu_ddr_ratios[opt][0];
+ *div = kirkwood_cpu_ddr_ratios[opt][1];
+ break;
+ }
+ }
+}
+
+static const struct core_clocks kirkwood_core_clocks = {
+ .get_tclk_freq = kirkwood_get_tclk_freq,
+ .get_cpu_freq = kirkwood_get_cpu_freq,
+ .get_clk_ratio = kirkwood_get_clk_ratio,
+ .ratios = kirkwood_core_ratios,
+ .num_ratios = ARRAY_SIZE(kirkwood_core_ratios),
+};
+
+static const u32 __initconst mv88f6180_cpu_frequencies[] = {
+ 0, 0, 0, 0, 0,
+ 600000000,
+ 800000000,
+ 1000000000
+};
+
+static u32 __init mv88f6180_get_cpu_freq(void __iomem *sar)
+{
+ u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) & SAR_MV88F6180_CLK_MASK;
+ return mv88f6180_cpu_frequencies[opt];
+}
+
+static const int __initconst mv88f6180_cpu_ddr_ratios[8][2] = {
+ { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 },
+ { 0, 1 }, { 1, 3 }, { 1, 4 }, { 1, 5 }
+};
+
+static void __init mv88f6180_get_clk_ratio(
+ void __iomem *sar, int id, int *mult, int *div)
+{
+ switch (id) {
+ case KIRKWOOD_CPU_TO_L2:
+ {
+ /* mv88f6180 has a fixed 1:2 CPU-to-L2 ratio */
+ *mult = 1;
+ *div = 2;
+ break;
+ }
+ case KIRKWOOD_CPU_TO_DDR:
+ {
+ u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) &
+ SAR_MV88F6180_CLK_MASK;
+ *mult = mv88f6180_cpu_ddr_ratios[opt][0];
+ *div = mv88f6180_cpu_ddr_ratios[opt][1];
+ break;
+ }
+ }
+}
+
+static const struct core_clocks mv88f6180_core_clocks = {
+ .get_tclk_freq = kirkwood_get_tclk_freq,
+ .get_cpu_freq = mv88f6180_get_cpu_freq,
+ .get_clk_ratio = mv88f6180_get_clk_ratio,
+ .ratios = kirkwood_core_ratios,
+ .num_ratios = ARRAY_SIZE(kirkwood_core_ratios),
+};
+#endif /* CONFIG_ARCH_KIRKWOOD */
+
+static const __initdata struct of_device_id clk_core_match[] = {
+#ifdef CONFIG_MACH_ARMADA_370_XP
+ {
+ .compatible = "marvell,armada-370-core-clock",
+ .data = &armada_370_core_clocks,
+ },
+ {
+ .compatible = "marvell,armada-xp-core-clock",
+ .data = &armada_xp_core_clocks,
+ },
+#endif
+#ifdef CONFIG_ARCH_DOVE
+ {
+ .compatible = "marvell,dove-core-clock",
+ .data = &dove_core_clocks,
+ },
+#endif
+
+#ifdef CONFIG_ARCH_KIRKWOOD
+ {
+ .compatible = "marvell,kirkwood-core-clock",
+ .data = &kirkwood_core_clocks,
+ },
+ {
+ .compatible = "marvell,mv88f6180-core-clock",
+ .data = &mv88f6180_core_clocks,
+ },
+#endif
+
+ { }
+};
+
+void __init mvebu_core_clk_init(void)
+{
+ struct device_node *np;
+
+ for_each_matching_node(np, clk_core_match) {
+ const struct of_device_id *match =
+ of_match_node(clk_core_match, np);
+ mvebu_clk_core_setup(np, (struct core_clocks *)match->data);
+ }
+}
diff --git a/drivers/clk/mvebu/clk-core.h b/drivers/clk/mvebu/clk-core.h
new file mode 100644
index 00000000000..28b5e02e988
--- /dev/null
+++ b/drivers/clk/mvebu/clk-core.h
@@ -0,0 +1,18 @@
+/*
+ * * Marvell EBU clock core handling defined at reset
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MVEBU_CLK_CORE_H
+#define __MVEBU_CLK_CORE_H
+
+void __init mvebu_core_clk_init(void);
+
+#endif
diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c
new file mode 100644
index 00000000000..ff004578a11
--- /dev/null
+++ b/drivers/clk/mvebu/clk-cpu.c
@@ -0,0 +1,186 @@
+/*
+ * Marvell MVEBU CPU clock handling.
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include "clk-cpu.h"
+
+#define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET 0x0
+#define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET 0xC
+#define SYS_CTRL_CLK_DIVIDER_MASK 0x3F
+
+#define MAX_CPU 4
+struct cpu_clk {
+ struct clk_hw hw;
+ int cpu;
+ const char *clk_name;
+ const char *parent_name;
+ void __iomem *reg_base;
+};
+
+static struct clk **clks;
+
+static struct clk_onecell_data clk_data;
+
+#define to_cpu_clk(p) container_of(p, struct cpu_clk, hw)
+
+static unsigned long clk_cpu_recalc_rate(struct clk_hw *hwclk,
+ unsigned long parent_rate)
+{
+ struct cpu_clk *cpuclk = to_cpu_clk(hwclk);
+ u32 reg, div;
+
+ reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET);
+ div = (reg >> (cpuclk->cpu * 8)) & SYS_CTRL_CLK_DIVIDER_MASK;
+ return parent_rate / div;
+}
+
+static long clk_cpu_round_rate(struct clk_hw *hwclk, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ /* Valid ratio are 1:1, 1:2 and 1:3 */
+ u32 div;
+
+ div = *parent_rate / rate;
+ if (div == 0)
+ div = 1;
+ else if (div > 3)
+ div = 3;
+
+ return *parent_rate / div;
+}
+
+static int clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct cpu_clk *cpuclk = to_cpu_clk(hwclk);
+ u32 reg, div;
+ u32 reload_mask;
+
+ div = parent_rate / rate;
+ reg = (readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET)
+ & (~(SYS_CTRL_CLK_DIVIDER_MASK << (cpuclk->cpu * 8))))
+ | (div << (cpuclk->cpu * 8));
+ writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET);
+ /* Set clock divider reload smooth bit mask */
+ reload_mask = 1 << (20 + cpuclk->cpu);
+
+ reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET)
+ | reload_mask;
+ writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET);
+
+ /* Now trigger the clock update */
+ reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET)
+ | 1 << 24;
+ writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET);
+
+ /* Wait for clocks to settle down then clear reload request */
+ udelay(1000);
+ reg &= ~(reload_mask | 1 << 24);
+ writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET);
+ udelay(1000);
+
+ return 0;
+}
+
+static const struct clk_ops cpu_ops = {
+ .recalc_rate = clk_cpu_recalc_rate,
+ .round_rate = clk_cpu_round_rate,
+ .set_rate = clk_cpu_set_rate,
+};
+
+void __init of_cpu_clk_setup(struct device_node *node)
+{
+ struct cpu_clk *cpuclk;
+ void __iomem *clock_complex_base = of_iomap(node, 0);
+ int ncpus = 0;
+ struct device_node *dn;
+
+ if (clock_complex_base == NULL) {
+ pr_err("%s: clock-complex base register not set\n",
+ __func__);
+ return;
+ }
+
+ for_each_node_by_type(dn, "cpu")
+ ncpus++;
+
+ cpuclk = kzalloc(ncpus * sizeof(*cpuclk), GFP_KERNEL);
+ if (WARN_ON(!cpuclk))
+ return;
+
+ clks = kzalloc(ncpus * sizeof(*clks), GFP_KERNEL);
+ if (WARN_ON(!clks))
+ return;
+
+ for_each_node_by_type(dn, "cpu") {
+ struct clk_init_data init;
+ struct clk *clk;
+ struct clk *parent_clk;
+ char *clk_name = kzalloc(5, GFP_KERNEL);
+ int cpu, err;
+
+ if (WARN_ON(!clk_name))
+ return;
+
+ err = of_property_read_u32(dn, "reg", &cpu);
+ if (WARN_ON(err))
+ return;
+
+ sprintf(clk_name, "cpu%d", cpu);
+ parent_clk = of_clk_get(node, 0);
+
+ cpuclk[cpu].parent_name = __clk_get_name(parent_clk);
+ cpuclk[cpu].clk_name = clk_name;
+ cpuclk[cpu].cpu = cpu;
+ cpuclk[cpu].reg_base = clock_complex_base;
+ cpuclk[cpu].hw.init = &init;
+
+ init.name = cpuclk[cpu].clk_name;
+ init.ops = &cpu_ops;
+ init.flags = 0;
+ init.parent_names = &cpuclk[cpu].parent_name;
+ init.num_parents = 1;
+
+ clk = clk_register(NULL, &cpuclk[cpu].hw);
+ if (WARN_ON(IS_ERR(clk)))
+ goto bail_out;
+ clks[cpu] = clk;
+ }
+ clk_data.clk_num = MAX_CPU;
+ clk_data.clks = clks;
+ of_clk_add_provider(node, of_clk_src_onecell_get, &clk_data);
+
+ return;
+bail_out:
+ kfree(clks);
+ kfree(cpuclk);
+}
+
+static const __initconst struct of_device_id clk_cpu_match[] = {
+ {
+ .compatible = "marvell,armada-xp-cpu-clock",
+ .data = of_cpu_clk_setup,
+ },
+ {
+ /* sentinel */
+ },
+};
+
+void __init mvebu_cpu_clk_init(void)
+{
+ of_clk_init(clk_cpu_match);
+}
diff --git a/drivers/clk/mvebu/clk-cpu.h b/drivers/clk/mvebu/clk-cpu.h
new file mode 100644
index 00000000000..08e2affba4e
--- /dev/null
+++ b/drivers/clk/mvebu/clk-cpu.h
@@ -0,0 +1,22 @@
+/*
+ * Marvell MVEBU CPU clock handling.
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MVEBU_CLK_CPU_H
+#define __MVEBU_CLK_CPU_H
+
+#ifdef CONFIG_MVEBU_CLK_CPU
+void __init mvebu_cpu_clk_init(void);
+#else
+static inline void mvebu_cpu_clk_init(void) {}
+#endif
+
+#endif
diff --git a/drivers/clk/mvebu/clk-gating-ctrl.c b/drivers/clk/mvebu/clk-gating-ctrl.c
new file mode 100644
index 00000000000..c6d3c263b07
--- /dev/null
+++ b/drivers/clk/mvebu/clk-gating-ctrl.c
@@ -0,0 +1,249 @@
+/*
+ * Marvell MVEBU clock gating control.
+ *
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ * Andrew Lunn <andrew@lunn.ch>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/mvebu.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+struct mvebu_gating_ctrl {
+ spinlock_t lock;
+ struct clk **gates;
+ int num_gates;
+};
+
+struct mvebu_soc_descr {
+ const char *name;
+ const char *parent;
+ int bit_idx;
+};
+
+#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
+
+static struct clk __init *mvebu_clk_gating_get_src(
+ struct of_phandle_args *clkspec, void *data)
+{
+ struct mvebu_gating_ctrl *ctrl = (struct mvebu_gating_ctrl *)data;
+ int n;
+
+ if (clkspec->args_count < 1)
+ return ERR_PTR(-EINVAL);
+
+ for (n = 0; n < ctrl->num_gates; n++) {
+ struct clk_gate *gate =
+ to_clk_gate(__clk_get_hw(ctrl->gates[n]));
+ if (clkspec->args[0] == gate->bit_idx)
+ return ctrl->gates[n];
+ }
+ return ERR_PTR(-ENODEV);
+}
+
+static void __init mvebu_clk_gating_setup(
+ struct device_node *np, const struct mvebu_soc_descr *descr)
+{
+ struct mvebu_gating_ctrl *ctrl;
+ struct clk *clk;
+ void __iomem *base;
+ const char *default_parent = NULL;
+ int n;
+
+ base = of_iomap(np, 0);
+
+ clk = of_clk_get(np, 0);
+ if (!IS_ERR(clk)) {
+ default_parent = __clk_get_name(clk);
+ clk_put(clk);
+ }
+
+ ctrl = kzalloc(sizeof(struct mvebu_gating_ctrl), GFP_KERNEL);
+ if (WARN_ON(!ctrl))
+ return;
+
+ spin_lock_init(&ctrl->lock);
+
+ /*
+ * Count, allocate, and register clock gates
+ */
+ for (n = 0; descr[n].name;)
+ n++;
+
+ ctrl->num_gates = n;
+ ctrl->gates = kzalloc(ctrl->num_gates * sizeof(struct clk *),
+ GFP_KERNEL);
+ if (WARN_ON(!ctrl->gates)) {
+ kfree(ctrl);
+ return;
+ }
+
+ for (n = 0; n < ctrl->num_gates; n++) {
+ u8 flags = 0;
+ const char *parent =
+ (descr[n].parent) ? descr[n].parent : default_parent;
+
+ /*
+ * On Armada 370, the DDR clock is a special case: it
+ * isn't taken by any driver, but should anyway be
+ * kept enabled, so we mark it as IGNORE_UNUSED for
+ * now.
+ */
+ if (!strcmp(descr[n].name, "ddr"))
+ flags |= CLK_IGNORE_UNUSED;
+
+ ctrl->gates[n] = clk_register_gate(NULL, descr[n].name, parent,
+ flags, base, descr[n].bit_idx, 0, &ctrl->lock);
+ WARN_ON(IS_ERR(ctrl->gates[n]));
+ }
+ of_clk_add_provider(np, mvebu_clk_gating_get_src, ctrl);
+}
+
+/*
+ * SoC specific clock gating control
+ */
+
+#ifdef CONFIG_MACH_ARMADA_370
+static const struct mvebu_soc_descr __initconst armada_370_gating_descr[] = {
+ { "audio", NULL, 0 },
+ { "pex0_en", NULL, 1 },
+ { "pex1_en", NULL, 2 },
+ { "ge1", NULL, 3 },
+ { "ge0", NULL, 4 },
+ { "pex0", NULL, 5 },
+ { "pex1", NULL, 9 },
+ { "sata0", NULL, 15 },
+ { "sdio", NULL, 17 },
+ { "tdm", NULL, 25 },
+ { "ddr", NULL, 28 },
+ { "sata1", NULL, 30 },
+ { }
+};
+#endif
+
+#ifdef CONFIG_MACH_ARMADA_XP
+static const struct mvebu_soc_descr __initconst armada_xp_gating_descr[] = {
+ { "audio", NULL, 0 },
+ { "ge3", NULL, 1 },
+ { "ge2", NULL, 2 },
+ { "ge1", NULL, 3 },
+ { "ge0", NULL, 4 },
+ { "pex0", NULL, 5 },
+ { "pex1", NULL, 6 },
+ { "pex2", NULL, 7 },
+ { "pex3", NULL, 8 },
+ { "bp", NULL, 13 },
+ { "sata0lnk", NULL, 14 },
+ { "sata0", "sata0lnk", 15 },
+ { "lcd", NULL, 16 },
+ { "sdio", NULL, 17 },
+ { "usb0", NULL, 18 },
+ { "usb1", NULL, 19 },
+ { "usb2", NULL, 20 },
+ { "xor0", NULL, 22 },
+ { "crypto", NULL, 23 },
+ { "tdm", NULL, 25 },
+ { "xor1", NULL, 28 },
+ { "sata1lnk", NULL, 29 },
+ { "sata1", "sata1lnk", 30 },
+ { }
+};
+#endif
+
+#ifdef CONFIG_ARCH_DOVE
+static const struct mvebu_soc_descr __initconst dove_gating_descr[] = {
+ { "usb0", NULL, 0 },
+ { "usb1", NULL, 1 },
+ { "ge", "gephy", 2 },
+ { "sata", NULL, 3 },
+ { "pex0", NULL, 4 },
+ { "pex1", NULL, 5 },
+ { "sdio0", NULL, 8 },
+ { "sdio1", NULL, 9 },
+ { "nand", NULL, 10 },
+ { "camera", NULL, 11 },
+ { "i2s0", NULL, 12 },
+ { "i2s1", NULL, 13 },
+ { "crypto", NULL, 15 },
+ { "ac97", NULL, 21 },
+ { "pdma", NULL, 22 },
+ { "xor0", NULL, 23 },
+ { "xor1", NULL, 24 },
+ { "gephy", NULL, 30 },
+ { }
+};
+#endif
+
+#ifdef CONFIG_ARCH_KIRKWOOD
+static const struct mvebu_soc_descr __initconst kirkwood_gating_descr[] = {
+ { "ge0", NULL, 0 },
+ { "pex0", NULL, 2 },
+ { "usb0", NULL, 3 },
+ { "sdio", NULL, 4 },
+ { "tsu", NULL, 5 },
+ { "runit", NULL, 7 },
+ { "xor0", NULL, 8 },
+ { "audio", NULL, 9 },
+ { "sata0", NULL, 14 },
+ { "sata1", NULL, 15 },
+ { "xor1", NULL, 16 },
+ { "crypto", NULL, 17 },
+ { "pex1", NULL, 18 },
+ { "ge1", NULL, 19 },
+ { "tdm", NULL, 20 },
+ { }
+};
+#endif
+
+static const __initdata struct of_device_id clk_gating_match[] = {
+#ifdef CONFIG_MACH_ARMADA_370
+ {
+ .compatible = "marvell,armada-370-gating-clock",
+ .data = armada_370_gating_descr,
+ },
+#endif
+
+#ifdef CONFIG_MACH_ARMADA_XP
+ {
+ .compatible = "marvell,armada-xp-gating-clock",
+ .data = armada_xp_gating_descr,
+ },
+#endif
+
+#ifdef CONFIG_ARCH_DOVE
+ {
+ .compatible = "marvell,dove-gating-clock",
+ .data = dove_gating_descr,
+ },
+#endif
+
+#ifdef CONFIG_ARCH_KIRKWOOD
+ {
+ .compatible = "marvell,kirkwood-gating-clock",
+ .data = kirkwood_gating_descr,
+ },
+#endif
+
+ { }
+};
+
+void __init mvebu_gating_clk_init(void)
+{
+ struct device_node *np;
+
+ for_each_matching_node(np, clk_gating_match) {
+ const struct of_device_id *match =
+ of_match_node(clk_gating_match, np);
+ mvebu_clk_gating_setup(np,
+ (const struct mvebu_soc_descr *)match->data);
+ }
+}
diff --git a/drivers/clk/mvebu/clk-gating-ctrl.h b/drivers/clk/mvebu/clk-gating-ctrl.h
new file mode 100644
index 00000000000..9275d1e51f1
--- /dev/null
+++ b/drivers/clk/mvebu/clk-gating-ctrl.h
@@ -0,0 +1,22 @@
+/*
+ * Marvell EBU gating clock handling
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __MVEBU_CLK_GATING_H
+#define __MVEBU_CLK_GATING_H
+
+#ifdef CONFIG_MVEBU_CLK_GATING
+void __init mvebu_gating_clk_init(void);
+#else
+void mvebu_gating_clk_init(void) {}
+#endif
+
+#endif
diff --git a/drivers/clk/mvebu/clk.c b/drivers/clk/mvebu/clk.c
new file mode 100644
index 00000000000..855681b8a9d
--- /dev/null
+++ b/drivers/clk/mvebu/clk.c
@@ -0,0 +1,27 @@
+/*
+ * Marvell EBU SoC clock handling.
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/clk/mvebu.h>
+#include <linux/of.h>
+#include "clk-core.h"
+#include "clk-cpu.h"
+#include "clk-gating-ctrl.h"
+
+void __init mvebu_clocks_init(void)
+{
+ mvebu_core_clk_init();
+ mvebu_gating_clk_init();
+ mvebu_cpu_clk_init();
+}
diff --git a/drivers/clk/spear/spear1310_clock.c b/drivers/clk/spear/spear1310_clock.c
index 147e25f0040..ed9af427861 100644
--- a/drivers/clk/spear/spear1310_clock.c
+++ b/drivers/clk/spear/spear1310_clock.c
@@ -20,6 +20,7 @@
#include <mach/spear.h>
#include "clk.h"
+#define VA_SPEAR1310_RAS_BASE IOMEM(UL(0xFA400000))
/* PLL related registers and bit values */
#define SPEAR1310_PLL_CFG (VA_MISC_BASE + 0x210)
/* PLL_CFG bit values */
diff --git a/drivers/clocksource/time-armada-370-xp.c b/drivers/clocksource/time-armada-370-xp.c
index 4674f94957c..a4605fd7e30 100644
--- a/drivers/clocksource/time-armada-370-xp.c
+++ b/drivers/clocksource/time-armada-370-xp.c
@@ -18,6 +18,7 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
+#include <linux/clk.h>
#include <linux/timer.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
@@ -167,7 +168,6 @@ void __init armada_370_xp_timer_init(void)
u32 u;
struct device_node *np;
unsigned int timer_clk;
- int ret;
np = of_find_compatible_node(NULL, NULL, "marvell,armada-370-xp-timer");
timer_base = of_iomap(np, 0);
WARN_ON(!timer_base);
@@ -179,13 +179,14 @@ void __init armada_370_xp_timer_init(void)
timer_base + TIMER_CTRL_OFF);
timer_clk = 25000000;
} else {
- u32 clk = 0;
- ret = of_property_read_u32(np, "clock-frequency", &clk);
- WARN_ON(!clk || ret < 0);
+ unsigned long rate = 0;
+ struct clk *clk = of_clk_get(np, 0);
+ WARN_ON(IS_ERR(clk));
+ rate = clk_get_rate(clk);
u = readl(timer_base + TIMER_CTRL_OFF);
writel(u & ~(TIMER0_25MHZ | TIMER1_25MHZ),
timer_base + TIMER_CTRL_OFF);
- timer_clk = clk / TIMER_DIVIDER;
+ timer_clk = rate / TIMER_DIVIDER;
}
/* We use timer 0 as clocksource, and timer 1 for
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index f6644f59fd9..87ec4d027c2 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -254,6 +254,7 @@ config CRYPTO_DEV_OMAP_AES
tristate "Support for OMAP AES hw engine"
depends on ARCH_OMAP2 || ARCH_OMAP3
select CRYPTO_AES
+ select CRYPTO_BLKCIPHER2
help
OMAP processors have AES module accelerator. Select this if you
want to use the OMAP module for AES algorithms.
diff --git a/drivers/crypto/picoxcell_crypto.c b/drivers/crypto/picoxcell_crypto.c
index 410a03c01ca..c983f869d2b 100644
--- a/drivers/crypto/picoxcell_crypto.c
+++ b/drivers/crypto/picoxcell_crypto.c
@@ -1863,6 +1863,7 @@ static int __devexit spacc_remove(struct platform_device *pdev)
static const struct platform_device_id spacc_id_table[] = {
{ "picochip,spacc-ipsec", },
{ "picochip,spacc-l2", },
+ { }
};
static struct platform_driver spacc_driver = {
diff --git a/drivers/crypto/s5p-sss.c b/drivers/crypto/s5p-sss.c
index a22714412cd..49ad8cbade6 100644
--- a/drivers/crypto/s5p-sss.c
+++ b/drivers/crypto/s5p-sss.c
@@ -30,7 +30,7 @@
#include <crypto/ctr.h>
#include <plat/cpu.h>
-#include <plat/dma.h>
+#include <mach/dma.h>
#define _SBF(s, v) ((v) << (s))
#define _BIT(b) _SBF(b, 1)
diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c
index da1112765a4..09b184adf31 100644
--- a/drivers/crypto/talitos.c
+++ b/drivers/crypto/talitos.c
@@ -936,8 +936,7 @@ static int sg_to_link_tbl(struct scatterlist *sg, int sg_count,
sg_count--;
link_tbl_ptr--;
}
- link_tbl_ptr->len = cpu_to_be16(be16_to_cpu(link_tbl_ptr->len)
- + cryptlen);
+ be16_add_cpu(&link_tbl_ptr->len, cryptlen);
/* tag end of link table */
link_tbl_ptr->j_extent = DESC_PTR_LNKTBL_RETURN;
diff --git a/drivers/crypto/tegra-aes.c b/drivers/crypto/tegra-aes.c
index e69f3bc473b..eb32fd8cad1 100644
--- a/drivers/crypto/tegra-aes.c
+++ b/drivers/crypto/tegra-aes.c
@@ -672,8 +672,10 @@ static int tegra_aes_get_random(struct crypto_rng *tfm, u8 *rdata,
mutex_lock(&aes_lock);
ret = clk_prepare_enable(dd->aes_clk);
- if (ret)
+ if (ret) {
+ mutex_unlock(&aes_lock);
return ret;
+ }
ctx->dd = dd;
dd->ctx = ctx;
@@ -757,8 +759,10 @@ static int tegra_aes_rng_reset(struct crypto_rng *tfm, u8 *seed,
dd->flags = FLAGS_ENCRYPT | FLAGS_RNG;
ret = clk_prepare_enable(dd->aes_clk);
- if (ret)
+ if (ret) {
+ mutex_unlock(&aes_lock);
return ret;
+ }
aes_set_key(dd);
@@ -1029,7 +1033,7 @@ out:
if (dd->buf_out)
dma_free_coherent(dev, AES_HW_DMA_BUFFER_SIZE_BYTES,
dd->buf_out, dd->dma_buf_out);
- if (IS_ERR(dd->aes_clk))
+ if (!IS_ERR(dd->aes_clk))
clk_put(dd->aes_clk);
if (aes_wq)
destroy_workqueue(aes_wq);
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c
index d12ad00da4c..ac71f555dd7 100644
--- a/drivers/dma/mv_xor.c
+++ b/drivers/dma/mv_xor.c
@@ -26,6 +26,9 @@
#include <linux/platform_device.h>
#include <linux/memory.h>
#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
#include <linux/platform_data/dma-mv_xor.h>
#include "dmaengine.h"
@@ -34,14 +37,14 @@
static void mv_xor_issue_pending(struct dma_chan *chan);
#define to_mv_xor_chan(chan) \
- container_of(chan, struct mv_xor_chan, common)
-
-#define to_mv_xor_device(dev) \
- container_of(dev, struct mv_xor_device, common)
+ container_of(chan, struct mv_xor_chan, dmachan)
#define to_mv_xor_slot(tx) \
container_of(tx, struct mv_xor_desc_slot, async_tx)
+#define mv_chan_to_devp(chan) \
+ ((chan)->dmadev.dev)
+
static void mv_desc_init(struct mv_xor_desc_slot *desc, unsigned long flags)
{
struct mv_xor_desc *hw_desc = desc->hw_desc;
@@ -166,7 +169,7 @@ static int mv_is_err_intr(u32 intr_cause)
static void mv_xor_device_clear_eoc_cause(struct mv_xor_chan *chan)
{
u32 val = ~(1 << (chan->idx * 16));
- dev_dbg(chan->device->common.dev, "%s, val 0x%08x\n", __func__, val);
+ dev_dbg(mv_chan_to_devp(chan), "%s, val 0x%08x\n", __func__, val);
__raw_writel(val, XOR_INTR_CAUSE(chan));
}
@@ -206,9 +209,9 @@ static void mv_set_mode(struct mv_xor_chan *chan,
op_mode = XOR_OPERATION_MODE_MEMSET;
break;
default:
- dev_printk(KERN_ERR, chan->device->common.dev,
- "error: unsupported operation %d.\n",
- type);
+ dev_err(mv_chan_to_devp(chan),
+ "error: unsupported operation %d.\n",
+ type);
BUG();
return;
}
@@ -223,7 +226,7 @@ static void mv_chan_activate(struct mv_xor_chan *chan)
{
u32 activation;
- dev_dbg(chan->device->common.dev, " activate chan.\n");
+ dev_dbg(mv_chan_to_devp(chan), " activate chan.\n");
activation = __raw_readl(XOR_ACTIVATION(chan));
activation |= 0x1;
__raw_writel(activation, XOR_ACTIVATION(chan));
@@ -251,7 +254,7 @@ static int mv_chan_xor_slot_count(size_t len, int src_cnt)
static void mv_xor_free_slots(struct mv_xor_chan *mv_chan,
struct mv_xor_desc_slot *slot)
{
- dev_dbg(mv_chan->device->common.dev, "%s %d slot %p\n",
+ dev_dbg(mv_chan_to_devp(mv_chan), "%s %d slot %p\n",
__func__, __LINE__, slot);
slot->slots_per_op = 0;
@@ -266,7 +269,7 @@ static void mv_xor_free_slots(struct mv_xor_chan *mv_chan,
static void mv_xor_start_new_chain(struct mv_xor_chan *mv_chan,
struct mv_xor_desc_slot *sw_desc)
{
- dev_dbg(mv_chan->device->common.dev, "%s %d: sw_desc %p\n",
+ dev_dbg(mv_chan_to_devp(mv_chan), "%s %d: sw_desc %p\n",
__func__, __LINE__, sw_desc);
if (sw_desc->type != mv_chan->current_type)
mv_set_mode(mv_chan, sw_desc->type);
@@ -284,7 +287,7 @@ static void mv_xor_start_new_chain(struct mv_xor_chan *mv_chan,
mv_chan_set_next_descriptor(mv_chan, sw_desc->async_tx.phys);
}
mv_chan->pending += sw_desc->slot_cnt;
- mv_xor_issue_pending(&mv_chan->common);
+ mv_xor_issue_pending(&mv_chan->dmachan);
}
static dma_cookie_t
@@ -308,8 +311,7 @@ mv_xor_run_tx_complete_actions(struct mv_xor_desc_slot *desc,
*/
if (desc->group_head && desc->unmap_len) {
struct mv_xor_desc_slot *unmap = desc->group_head;
- struct device *dev =
- &mv_chan->device->pdev->dev;
+ struct device *dev = mv_chan_to_devp(mv_chan);
u32 len = unmap->unmap_len;
enum dma_ctrl_flags flags = desc->async_tx.flags;
u32 src_cnt;
@@ -353,7 +355,7 @@ mv_xor_clean_completed_slots(struct mv_xor_chan *mv_chan)
{
struct mv_xor_desc_slot *iter, *_iter;
- dev_dbg(mv_chan->device->common.dev, "%s %d\n", __func__, __LINE__);
+ dev_dbg(mv_chan_to_devp(mv_chan), "%s %d\n", __func__, __LINE__);
list_for_each_entry_safe(iter, _iter, &mv_chan->completed_slots,
completed_node) {
@@ -369,7 +371,7 @@ static int
mv_xor_clean_slot(struct mv_xor_desc_slot *desc,
struct mv_xor_chan *mv_chan)
{
- dev_dbg(mv_chan->device->common.dev, "%s %d: desc %p flags %d\n",
+ dev_dbg(mv_chan_to_devp(mv_chan), "%s %d: desc %p flags %d\n",
__func__, __LINE__, desc, desc->async_tx.flags);
list_del(&desc->chain_node);
/* the client is allowed to attach dependent operations
@@ -393,8 +395,8 @@ static void __mv_xor_slot_cleanup(struct mv_xor_chan *mv_chan)
u32 current_desc = mv_chan_get_current_desc(mv_chan);
int seen_current = 0;
- dev_dbg(mv_chan->device->common.dev, "%s %d\n", __func__, __LINE__);
- dev_dbg(mv_chan->device->common.dev, "current_desc %x\n", current_desc);
+ dev_dbg(mv_chan_to_devp(mv_chan), "%s %d\n", __func__, __LINE__);
+ dev_dbg(mv_chan_to_devp(mv_chan), "current_desc %x\n", current_desc);
mv_xor_clean_completed_slots(mv_chan);
/* free completed slots from the chain starting with
@@ -438,7 +440,7 @@ static void __mv_xor_slot_cleanup(struct mv_xor_chan *mv_chan)
}
if (cookie > 0)
- mv_chan->common.completed_cookie = cookie;
+ mv_chan->dmachan.completed_cookie = cookie;
}
static void
@@ -547,7 +549,7 @@ mv_xor_tx_submit(struct dma_async_tx_descriptor *tx)
dma_cookie_t cookie;
int new_hw_chain = 1;
- dev_dbg(mv_chan->device->common.dev,
+ dev_dbg(mv_chan_to_devp(mv_chan),
"%s sw_desc %p: async_tx %p\n",
__func__, sw_desc, &sw_desc->async_tx);
@@ -570,7 +572,7 @@ mv_xor_tx_submit(struct dma_async_tx_descriptor *tx)
if (!mv_can_chain(grp_start))
goto submit_done;
- dev_dbg(mv_chan->device->common.dev, "Append to last desc %x\n",
+ dev_dbg(mv_chan_to_devp(mv_chan), "Append to last desc %x\n",
old_chain_tail->async_tx.phys);
/* fix up the hardware chain */
@@ -604,9 +606,7 @@ static int mv_xor_alloc_chan_resources(struct dma_chan *chan)
int idx;
struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan);
struct mv_xor_desc_slot *slot = NULL;
- struct mv_xor_platform_data *plat_data =
- mv_chan->device->pdev->dev.platform_data;
- int num_descs_in_pool = plat_data->pool_size/MV_XOR_SLOT_SIZE;
+ int num_descs_in_pool = MV_XOR_POOL_SIZE/MV_XOR_SLOT_SIZE;
/* Allocate descriptor slots */
idx = mv_chan->slots_allocated;
@@ -617,7 +617,7 @@ static int mv_xor_alloc_chan_resources(struct dma_chan *chan)
" %d descriptor slots", idx);
break;
}
- hw_desc = (char *) mv_chan->device->dma_desc_pool_virt;
+ hw_desc = (char *) mv_chan->dma_desc_pool_virt;
slot->hw_desc = (void *) &hw_desc[idx * MV_XOR_SLOT_SIZE];
dma_async_tx_descriptor_init(&slot->async_tx, chan);
@@ -625,7 +625,7 @@ static int mv_xor_alloc_chan_resources(struct dma_chan *chan)
INIT_LIST_HEAD(&slot->chain_node);
INIT_LIST_HEAD(&slot->slot_node);
INIT_LIST_HEAD(&slot->tx_list);
- hw_desc = (char *) mv_chan->device->dma_desc_pool;
+ hw_desc = (char *) mv_chan->dma_desc_pool;
slot->async_tx.phys =
(dma_addr_t) &hw_desc[idx * MV_XOR_SLOT_SIZE];
slot->idx = idx++;
@@ -641,7 +641,7 @@ static int mv_xor_alloc_chan_resources(struct dma_chan *chan)
struct mv_xor_desc_slot,
slot_node);
- dev_dbg(mv_chan->device->common.dev,
+ dev_dbg(mv_chan_to_devp(mv_chan),
"allocated %d descriptor slots last_used: %p\n",
mv_chan->slots_allocated, mv_chan->last_used);
@@ -656,7 +656,7 @@ mv_xor_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
struct mv_xor_desc_slot *sw_desc, *grp_start;
int slot_cnt;
- dev_dbg(mv_chan->device->common.dev,
+ dev_dbg(mv_chan_to_devp(mv_chan),
"%s dest: %x src %x len: %u flags: %ld\n",
__func__, dest, src, len, flags);
if (unlikely(len < MV_XOR_MIN_BYTE_COUNT))
@@ -680,7 +680,7 @@ mv_xor_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
}
spin_unlock_bh(&mv_chan->lock);
- dev_dbg(mv_chan->device->common.dev,
+ dev_dbg(mv_chan_to_devp(mv_chan),
"%s sw_desc %p async_tx %p\n",
__func__, sw_desc, sw_desc ? &sw_desc->async_tx : 0);
@@ -695,7 +695,7 @@ mv_xor_prep_dma_memset(struct dma_chan *chan, dma_addr_t dest, int value,
struct mv_xor_desc_slot *sw_desc, *grp_start;
int slot_cnt;
- dev_dbg(mv_chan->device->common.dev,
+ dev_dbg(mv_chan_to_devp(mv_chan),
"%s dest: %x len: %u flags: %ld\n",
__func__, dest, len, flags);
if (unlikely(len < MV_XOR_MIN_BYTE_COUNT))
@@ -718,7 +718,7 @@ mv_xor_prep_dma_memset(struct dma_chan *chan, dma_addr_t dest, int value,
sw_desc->unmap_len = len;
}
spin_unlock_bh(&mv_chan->lock);
- dev_dbg(mv_chan->device->common.dev,
+ dev_dbg(mv_chan_to_devp(mv_chan),
"%s sw_desc %p async_tx %p \n",
__func__, sw_desc, &sw_desc->async_tx);
return sw_desc ? &sw_desc->async_tx : NULL;
@@ -737,7 +737,7 @@ mv_xor_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src,
BUG_ON(len > MV_XOR_MAX_BYTE_COUNT);
- dev_dbg(mv_chan->device->common.dev,
+ dev_dbg(mv_chan_to_devp(mv_chan),
"%s src_cnt: %d len: dest %x %u flags: %ld\n",
__func__, src_cnt, len, dest, flags);
@@ -758,7 +758,7 @@ mv_xor_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src,
mv_desc_set_src_addr(grp_start, src_cnt, src[src_cnt]);
}
spin_unlock_bh(&mv_chan->lock);
- dev_dbg(mv_chan->device->common.dev,
+ dev_dbg(mv_chan_to_devp(mv_chan),
"%s sw_desc %p async_tx %p \n",
__func__, sw_desc, &sw_desc->async_tx);
return sw_desc ? &sw_desc->async_tx : NULL;
@@ -791,12 +791,12 @@ static void mv_xor_free_chan_resources(struct dma_chan *chan)
}
mv_chan->last_used = NULL;
- dev_dbg(mv_chan->device->common.dev, "%s slots_allocated %d\n",
+ dev_dbg(mv_chan_to_devp(mv_chan), "%s slots_allocated %d\n",
__func__, mv_chan->slots_allocated);
spin_unlock_bh(&mv_chan->lock);
if (in_use_descs)
- dev_err(mv_chan->device->common.dev,
+ dev_err(mv_chan_to_devp(mv_chan),
"freeing %d in use descriptors!\n", in_use_descs);
}
@@ -828,42 +828,42 @@ static void mv_dump_xor_regs(struct mv_xor_chan *chan)
u32 val;
val = __raw_readl(XOR_CONFIG(chan));
- dev_printk(KERN_ERR, chan->device->common.dev,
- "config 0x%08x.\n", val);
+ dev_err(mv_chan_to_devp(chan),
+ "config 0x%08x.\n", val);
val = __raw_readl(XOR_ACTIVATION(chan));
- dev_printk(KERN_ERR, chan->device->common.dev,
- "activation 0x%08x.\n", val);
+ dev_err(mv_chan_to_devp(chan),
+ "activation 0x%08x.\n", val);
val = __raw_readl(XOR_INTR_CAUSE(chan));
- dev_printk(KERN_ERR, chan->device->common.dev,
- "intr cause 0x%08x.\n", val);
+ dev_err(mv_chan_to_devp(chan),
+ "intr cause 0x%08x.\n", val);
val = __raw_readl(XOR_INTR_MASK(chan));
- dev_printk(KERN_ERR, chan->device->common.dev,
- "intr mask 0x%08x.\n", val);
+ dev_err(mv_chan_to_devp(chan),
+ "intr mask 0x%08x.\n", val);
val = __raw_readl(XOR_ERROR_CAUSE(chan));
- dev_printk(KERN_ERR, chan->device->common.dev,
- "error cause 0x%08x.\n", val);
+ dev_err(mv_chan_to_devp(chan),
+ "error cause 0x%08x.\n", val);
val = __raw_readl(XOR_ERROR_ADDR(chan));
- dev_printk(KERN_ERR, chan->device->common.dev,
- "error addr 0x%08x.\n", val);
+ dev_err(mv_chan_to_devp(chan),
+ "error addr 0x%08x.\n", val);
}
static void mv_xor_err_interrupt_handler(struct mv_xor_chan *chan,
u32 intr_cause)
{
if (intr_cause & (1 << 4)) {
- dev_dbg(chan->device->common.dev,
+ dev_dbg(mv_chan_to_devp(chan),
"ignore this error\n");
return;
}
- dev_printk(KERN_ERR, chan->device->common.dev,
- "error on chan %d. intr cause 0x%08x.\n",
- chan->idx, intr_cause);
+ dev_err(mv_chan_to_devp(chan),
+ "error on chan %d. intr cause 0x%08x.\n",
+ chan->idx, intr_cause);
mv_dump_xor_regs(chan);
BUG();
@@ -874,7 +874,7 @@ static irqreturn_t mv_xor_interrupt_handler(int irq, void *data)
struct mv_xor_chan *chan = data;
u32 intr_cause = mv_chan_get_intr_cause(chan);
- dev_dbg(chan->device->common.dev, "intr cause %x\n", intr_cause);
+ dev_dbg(mv_chan_to_devp(chan), "intr cause %x\n", intr_cause);
if (mv_is_err_intr(intr_cause))
mv_xor_err_interrupt_handler(chan, intr_cause);
@@ -901,7 +901,7 @@ static void mv_xor_issue_pending(struct dma_chan *chan)
*/
#define MV_XOR_TEST_SIZE 2000
-static int mv_xor_memcpy_self_test(struct mv_xor_device *device)
+static int mv_xor_memcpy_self_test(struct mv_xor_chan *mv_chan)
{
int i;
void *src, *dest;
@@ -910,7 +910,6 @@ static int mv_xor_memcpy_self_test(struct mv_xor_device *device)
dma_cookie_t cookie;
struct dma_async_tx_descriptor *tx;
int err = 0;
- struct mv_xor_chan *mv_chan;
src = kmalloc(sizeof(u8) * MV_XOR_TEST_SIZE, GFP_KERNEL);
if (!src)
@@ -926,10 +925,7 @@ static int mv_xor_memcpy_self_test(struct mv_xor_device *device)
for (i = 0; i < MV_XOR_TEST_SIZE; i++)
((u8 *) src)[i] = (u8)i;
- /* Start copy, using first DMA channel */
- dma_chan = container_of(device->common.channels.next,
- struct dma_chan,
- device_node);
+ dma_chan = &mv_chan->dmachan;
if (mv_xor_alloc_chan_resources(dma_chan) < 1) {
err = -ENODEV;
goto out;
@@ -950,18 +946,17 @@ static int mv_xor_memcpy_self_test(struct mv_xor_device *device)
if (mv_xor_status(dma_chan, cookie, NULL) !=
DMA_SUCCESS) {
- dev_printk(KERN_ERR, dma_chan->device->dev,
- "Self-test copy timed out, disabling\n");
+ dev_err(dma_chan->device->dev,
+ "Self-test copy timed out, disabling\n");
err = -ENODEV;
goto free_resources;
}
- mv_chan = to_mv_xor_chan(dma_chan);
- dma_sync_single_for_cpu(&mv_chan->device->pdev->dev, dest_dma,
+ dma_sync_single_for_cpu(dma_chan->device->dev, dest_dma,
MV_XOR_TEST_SIZE, DMA_FROM_DEVICE);
if (memcmp(src, dest, MV_XOR_TEST_SIZE)) {
- dev_printk(KERN_ERR, dma_chan->device->dev,
- "Self-test copy failed compare, disabling\n");
+ dev_err(dma_chan->device->dev,
+ "Self-test copy failed compare, disabling\n");
err = -ENODEV;
goto free_resources;
}
@@ -976,7 +971,7 @@ out:
#define MV_XOR_NUM_SRC_TEST 4 /* must be <= 15 */
static int
-mv_xor_xor_self_test(struct mv_xor_device *device)
+mv_xor_xor_self_test(struct mv_xor_chan *mv_chan)
{
int i, src_idx;
struct page *dest;
@@ -989,7 +984,6 @@ mv_xor_xor_self_test(struct mv_xor_device *device)
u8 cmp_byte = 0;
u32 cmp_word;
int err = 0;
- struct mv_xor_chan *mv_chan;
for (src_idx = 0; src_idx < MV_XOR_NUM_SRC_TEST; src_idx++) {
xor_srcs[src_idx] = alloc_page(GFP_KERNEL);
@@ -1022,9 +1016,7 @@ mv_xor_xor_self_test(struct mv_xor_device *device)
memset(page_address(dest), 0, PAGE_SIZE);
- dma_chan = container_of(device->common.channels.next,
- struct dma_chan,
- device_node);
+ dma_chan = &mv_chan->dmachan;
if (mv_xor_alloc_chan_resources(dma_chan) < 1) {
err = -ENODEV;
goto out;
@@ -1048,22 +1040,21 @@ mv_xor_xor_self_test(struct mv_xor_device *device)
if (mv_xor_status(dma_chan, cookie, NULL) !=
DMA_SUCCESS) {
- dev_printk(KERN_ERR, dma_chan->device->dev,
- "Self-test xor timed out, disabling\n");
+ dev_err(dma_chan->device->dev,
+ "Self-test xor timed out, disabling\n");
err = -ENODEV;
goto free_resources;
}
- mv_chan = to_mv_xor_chan(dma_chan);
- dma_sync_single_for_cpu(&mv_chan->device->pdev->dev, dest_dma,
+ dma_sync_single_for_cpu(dma_chan->device->dev, dest_dma,
PAGE_SIZE, DMA_FROM_DEVICE);
for (i = 0; i < (PAGE_SIZE / sizeof(u32)); i++) {
u32 *ptr = page_address(dest);
if (ptr[i] != cmp_word) {
- dev_printk(KERN_ERR, dma_chan->device->dev,
- "Self-test xor failed compare, disabling."
- " index %d, data %x, expected %x\n", i,
- ptr[i], cmp_word);
+ dev_err(dma_chan->device->dev,
+ "Self-test xor failed compare, disabling."
+ " index %d, data %x, expected %x\n", i,
+ ptr[i], cmp_word);
err = -ENODEV;
goto free_resources;
}
@@ -1079,62 +1070,66 @@ out:
return err;
}
-static int __devexit mv_xor_remove(struct platform_device *dev)
+/* This driver does not implement any of the optional DMA operations. */
+static int
+mv_xor_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+ unsigned long arg)
+{
+ return -ENOSYS;
+}
+
+static int mv_xor_channel_remove(struct mv_xor_chan *mv_chan)
{
- struct mv_xor_device *device = platform_get_drvdata(dev);
struct dma_chan *chan, *_chan;
- struct mv_xor_chan *mv_chan;
- struct mv_xor_platform_data *plat_data = dev->dev.platform_data;
+ struct device *dev = mv_chan->dmadev.dev;
- dma_async_device_unregister(&device->common);
+ dma_async_device_unregister(&mv_chan->dmadev);
- dma_free_coherent(&dev->dev, plat_data->pool_size,
- device->dma_desc_pool_virt, device->dma_desc_pool);
+ dma_free_coherent(dev, MV_XOR_POOL_SIZE,
+ mv_chan->dma_desc_pool_virt, mv_chan->dma_desc_pool);
- list_for_each_entry_safe(chan, _chan, &device->common.channels,
- device_node) {
- mv_chan = to_mv_xor_chan(chan);
+ list_for_each_entry_safe(chan, _chan, &mv_chan->dmadev.channels,
+ device_node) {
list_del(&chan->device_node);
}
+ free_irq(mv_chan->irq, mv_chan);
+
return 0;
}
-static int mv_xor_probe(struct platform_device *pdev)
+static struct mv_xor_chan *
+mv_xor_channel_add(struct mv_xor_device *xordev,
+ struct platform_device *pdev,
+ int idx, dma_cap_mask_t cap_mask, int irq)
{
int ret = 0;
- int irq;
- struct mv_xor_device *adev;
struct mv_xor_chan *mv_chan;
struct dma_device *dma_dev;
- struct mv_xor_platform_data *plat_data = pdev->dev.platform_data;
+ mv_chan = devm_kzalloc(&pdev->dev, sizeof(*mv_chan), GFP_KERNEL);
+ if (!mv_chan) {
+ ret = -ENOMEM;
+ goto err_free_dma;
+ }
- adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL);
- if (!adev)
- return -ENOMEM;
+ mv_chan->idx = idx;
+ mv_chan->irq = irq;
- dma_dev = &adev->common;
+ dma_dev = &mv_chan->dmadev;
/* allocate coherent memory for hardware descriptors
* note: writecombine gives slightly better performance, but
* requires that we explicitly flush the writes
*/
- adev->dma_desc_pool_virt = dma_alloc_writecombine(&pdev->dev,
- plat_data->pool_size,
- &adev->dma_desc_pool,
- GFP_KERNEL);
- if (!adev->dma_desc_pool_virt)
- return -ENOMEM;
-
- adev->id = plat_data->hw_id;
+ mv_chan->dma_desc_pool_virt =
+ dma_alloc_writecombine(&pdev->dev, MV_XOR_POOL_SIZE,
+ &mv_chan->dma_desc_pool, GFP_KERNEL);
+ if (!mv_chan->dma_desc_pool_virt)
+ return ERR_PTR(-ENOMEM);
/* discover transaction capabilites from the platform data */
- dma_dev->cap_mask = plat_data->cap_mask;
- adev->pdev = pdev;
- platform_set_drvdata(pdev, adev);
-
- adev->shared = platform_get_drvdata(plat_data->shared);
+ dma_dev->cap_mask = cap_mask;
INIT_LIST_HEAD(&dma_dev->channels);
@@ -1143,6 +1138,7 @@ static int mv_xor_probe(struct platform_device *pdev)
dma_dev->device_free_chan_resources = mv_xor_free_chan_resources;
dma_dev->device_tx_status = mv_xor_status;
dma_dev->device_issue_pending = mv_xor_issue_pending;
+ dma_dev->device_control = mv_xor_control;
dma_dev->dev = &pdev->dev;
/* set prep routines based on capability */
@@ -1155,15 +1151,7 @@ static int mv_xor_probe(struct platform_device *pdev)
dma_dev->device_prep_dma_xor = mv_xor_prep_dma_xor;
}
- mv_chan = devm_kzalloc(&pdev->dev, sizeof(*mv_chan), GFP_KERNEL);
- if (!mv_chan) {
- ret = -ENOMEM;
- goto err_free_dma;
- }
- mv_chan->device = adev;
- mv_chan->idx = plat_data->hw_id;
- mv_chan->mmr_base = adev->shared->xor_base;
-
+ mv_chan->mmr_base = xordev->xor_base;
if (!mv_chan->mmr_base) {
ret = -ENOMEM;
goto err_free_dma;
@@ -1174,14 +1162,8 @@ static int mv_xor_probe(struct platform_device *pdev)
/* clear errors before enabling interrupts */
mv_xor_device_clear_err_status(mv_chan);
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- ret = irq;
- goto err_free_dma;
- }
- ret = devm_request_irq(&pdev->dev, irq,
- mv_xor_interrupt_handler,
- 0, dev_name(&pdev->dev), mv_chan);
+ ret = request_irq(mv_chan->irq, mv_xor_interrupt_handler,
+ 0, dev_name(&pdev->dev), mv_chan);
if (ret)
goto err_free_dma;
@@ -1193,26 +1175,26 @@ static int mv_xor_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&mv_chan->chain);
INIT_LIST_HEAD(&mv_chan->completed_slots);
INIT_LIST_HEAD(&mv_chan->all_slots);
- mv_chan->common.device = dma_dev;
- dma_cookie_init(&mv_chan->common);
+ mv_chan->dmachan.device = dma_dev;
+ dma_cookie_init(&mv_chan->dmachan);
- list_add_tail(&mv_chan->common.device_node, &dma_dev->channels);
+ list_add_tail(&mv_chan->dmachan.device_node, &dma_dev->channels);
if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) {
- ret = mv_xor_memcpy_self_test(adev);
+ ret = mv_xor_memcpy_self_test(mv_chan);
dev_dbg(&pdev->dev, "memcpy self test returned %d\n", ret);
if (ret)
- goto err_free_dma;
+ goto err_free_irq;
}
if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) {
- ret = mv_xor_xor_self_test(adev);
+ ret = mv_xor_xor_self_test(mv_chan);
dev_dbg(&pdev->dev, "xor self test returned %d\n", ret);
if (ret)
- goto err_free_dma;
+ goto err_free_irq;
}
- dev_printk(KERN_INFO, &pdev->dev, "Marvell XOR: "
+ dev_info(&pdev->dev, "Marvell XOR: "
"( %s%s%s%s)\n",
dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "",
dma_has_cap(DMA_MEMSET, dma_dev->cap_mask) ? "fill " : "",
@@ -1220,20 +1202,21 @@ static int mv_xor_probe(struct platform_device *pdev)
dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : "");
dma_async_device_register(dma_dev);
- goto out;
+ return mv_chan;
+err_free_irq:
+ free_irq(mv_chan->irq, mv_chan);
err_free_dma:
- dma_free_coherent(&adev->pdev->dev, plat_data->pool_size,
- adev->dma_desc_pool_virt, adev->dma_desc_pool);
- out:
- return ret;
+ dma_free_coherent(&pdev->dev, MV_XOR_POOL_SIZE,
+ mv_chan->dma_desc_pool_virt, mv_chan->dma_desc_pool);
+ return ERR_PTR(ret);
}
static void
-mv_xor_conf_mbus_windows(struct mv_xor_shared_private *msp,
+mv_xor_conf_mbus_windows(struct mv_xor_device *xordev,
const struct mbus_dram_target_info *dram)
{
- void __iomem *base = msp->xor_base;
+ void __iomem *base = xordev->xor_base;
u32 win_enable = 0;
int i;
@@ -1258,99 +1241,176 @@ mv_xor_conf_mbus_windows(struct mv_xor_shared_private *msp,
writel(win_enable, base + WINDOW_BAR_ENABLE(0));
writel(win_enable, base + WINDOW_BAR_ENABLE(1));
+ writel(0, base + WINDOW_OVERRIDE_CTRL(0));
+ writel(0, base + WINDOW_OVERRIDE_CTRL(1));
}
-static struct platform_driver mv_xor_driver = {
- .probe = mv_xor_probe,
- .remove = mv_xor_remove,
- .driver = {
- .owner = THIS_MODULE,
- .name = MV_XOR_NAME,
- },
-};
-
-static int mv_xor_shared_probe(struct platform_device *pdev)
+static int mv_xor_probe(struct platform_device *pdev)
{
const struct mbus_dram_target_info *dram;
- struct mv_xor_shared_private *msp;
+ struct mv_xor_device *xordev;
+ struct mv_xor_platform_data *pdata = pdev->dev.platform_data;
struct resource *res;
+ int i, ret;
- dev_printk(KERN_NOTICE, &pdev->dev, "Marvell shared XOR driver\n");
+ dev_notice(&pdev->dev, "Marvell XOR driver\n");
- msp = devm_kzalloc(&pdev->dev, sizeof(*msp), GFP_KERNEL);
- if (!msp)
+ xordev = devm_kzalloc(&pdev->dev, sizeof(*xordev), GFP_KERNEL);
+ if (!xordev)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
- msp->xor_base = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!msp->xor_base)
+ xordev->xor_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!xordev->xor_base)
return -EBUSY;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res)
return -ENODEV;
- msp->xor_high_base = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!msp->xor_high_base)
+ xordev->xor_high_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!xordev->xor_high_base)
return -EBUSY;
- platform_set_drvdata(pdev, msp);
+ platform_set_drvdata(pdev, xordev);
/*
* (Re-)program MBUS remapping windows if we are asked to.
*/
dram = mv_mbus_dram_info();
if (dram)
- mv_xor_conf_mbus_windows(msp, dram);
+ mv_xor_conf_mbus_windows(xordev, dram);
/* Not all platforms can gate the clock, so it is not
* an error if the clock does not exists.
*/
- msp->clk = clk_get(&pdev->dev, NULL);
- if (!IS_ERR(msp->clk))
- clk_prepare_enable(msp->clk);
+ xordev->clk = clk_get(&pdev->dev, NULL);
+ if (!IS_ERR(xordev->clk))
+ clk_prepare_enable(xordev->clk);
+
+ if (pdev->dev.of_node) {
+ struct device_node *np;
+ int i = 0;
+
+ for_each_child_of_node(pdev->dev.of_node, np) {
+ dma_cap_mask_t cap_mask;
+ int irq;
+
+ dma_cap_zero(cap_mask);
+ if (of_property_read_bool(np, "dmacap,memcpy"))
+ dma_cap_set(DMA_MEMCPY, cap_mask);
+ if (of_property_read_bool(np, "dmacap,xor"))
+ dma_cap_set(DMA_XOR, cap_mask);
+ if (of_property_read_bool(np, "dmacap,memset"))
+ dma_cap_set(DMA_MEMSET, cap_mask);
+ if (of_property_read_bool(np, "dmacap,interrupt"))
+ dma_cap_set(DMA_INTERRUPT, cap_mask);
+
+ irq = irq_of_parse_and_map(np, 0);
+ if (!irq) {
+ ret = -ENODEV;
+ goto err_channel_add;
+ }
+
+ xordev->channels[i] =
+ mv_xor_channel_add(xordev, pdev, i,
+ cap_mask, irq);
+ if (IS_ERR(xordev->channels[i])) {
+ ret = PTR_ERR(xordev->channels[i]);
+ xordev->channels[i] = NULL;
+ irq_dispose_mapping(irq);
+ goto err_channel_add;
+ }
+
+ i++;
+ }
+ } else if (pdata && pdata->channels) {
+ for (i = 0; i < MV_XOR_MAX_CHANNELS; i++) {
+ struct mv_xor_channel_data *cd;
+ int irq;
+
+ cd = &pdata->channels[i];
+ if (!cd) {
+ ret = -ENODEV;
+ goto err_channel_add;
+ }
+
+ irq = platform_get_irq(pdev, i);
+ if (irq < 0) {
+ ret = irq;
+ goto err_channel_add;
+ }
+
+ xordev->channels[i] =
+ mv_xor_channel_add(xordev, pdev, i,
+ cd->cap_mask, irq);
+ if (IS_ERR(xordev->channels[i])) {
+ ret = PTR_ERR(xordev->channels[i]);
+ goto err_channel_add;
+ }
+ }
+ }
return 0;
+
+err_channel_add:
+ for (i = 0; i < MV_XOR_MAX_CHANNELS; i++)
+ if (xordev->channels[i]) {
+ if (pdev->dev.of_node)
+ irq_dispose_mapping(xordev->channels[i]->irq);
+ mv_xor_channel_remove(xordev->channels[i]);
+ }
+
+ clk_disable_unprepare(xordev->clk);
+ clk_put(xordev->clk);
+ return ret;
}
-static int mv_xor_shared_remove(struct platform_device *pdev)
+static int mv_xor_remove(struct platform_device *pdev)
{
- struct mv_xor_shared_private *msp = platform_get_drvdata(pdev);
+ struct mv_xor_device *xordev = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < MV_XOR_MAX_CHANNELS; i++) {
+ if (xordev->channels[i])
+ mv_xor_channel_remove(xordev->channels[i]);
+ }
- if (!IS_ERR(msp->clk)) {
- clk_disable_unprepare(msp->clk);
- clk_put(msp->clk);
+ if (!IS_ERR(xordev->clk)) {
+ clk_disable_unprepare(xordev->clk);
+ clk_put(xordev->clk);
}
return 0;
}
-static struct platform_driver mv_xor_shared_driver = {
- .probe = mv_xor_shared_probe,
- .remove = mv_xor_shared_remove,
+#ifdef CONFIG_OF
+static struct of_device_id mv_xor_dt_ids[] = {
+ { .compatible = "marvell,orion-xor", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mv_xor_dt_ids);
+#endif
+
+static struct platform_driver mv_xor_driver = {
+ .probe = mv_xor_probe,
+ .remove = mv_xor_remove,
.driver = {
- .owner = THIS_MODULE,
- .name = MV_XOR_SHARED_NAME,
+ .owner = THIS_MODULE,
+ .name = MV_XOR_NAME,
+ .of_match_table = of_match_ptr(mv_xor_dt_ids),
},
};
static int __init mv_xor_init(void)
{
- int rc;
-
- rc = platform_driver_register(&mv_xor_shared_driver);
- if (!rc) {
- rc = platform_driver_register(&mv_xor_driver);
- if (rc)
- platform_driver_unregister(&mv_xor_shared_driver);
- }
- return rc;
+ return platform_driver_register(&mv_xor_driver);
}
module_init(mv_xor_init);
@@ -1359,7 +1419,6 @@ module_init(mv_xor_init);
static void __exit mv_xor_exit(void)
{
platform_driver_unregister(&mv_xor_driver);
- platform_driver_unregister(&mv_xor_shared_driver);
return;
}
diff --git a/drivers/dma/mv_xor.h b/drivers/dma/mv_xor.h
index a5b422f5a8a..c632a4761fc 100644
--- a/drivers/dma/mv_xor.h
+++ b/drivers/dma/mv_xor.h
@@ -24,8 +24,10 @@
#include <linux/interrupt.h>
#define USE_TIMER
+#define MV_XOR_POOL_SIZE PAGE_SIZE
#define MV_XOR_SLOT_SIZE 64
#define MV_XOR_THRESHOLD 1
+#define MV_XOR_MAX_CHANNELS 2
#define XOR_OPERATION_MODE_XOR 0
#define XOR_OPERATION_MODE_MEMCPY 2
@@ -51,29 +53,13 @@
#define WINDOW_SIZE(w) (0x270 + ((w) << 2))
#define WINDOW_REMAP_HIGH(w) (0x290 + ((w) << 2))
#define WINDOW_BAR_ENABLE(chan) (0x240 + ((chan) << 2))
+#define WINDOW_OVERRIDE_CTRL(chan) (0x2A0 + ((chan) << 2))
-struct mv_xor_shared_private {
- void __iomem *xor_base;
- void __iomem *xor_high_base;
- struct clk *clk;
-};
-
-
-/**
- * struct mv_xor_device - internal representation of a XOR device
- * @pdev: Platform device
- * @id: HW XOR Device selector
- * @dma_desc_pool: base of DMA descriptor region (DMA address)
- * @dma_desc_pool_virt: base of DMA descriptor region (CPU address)
- * @common: embedded struct dma_device
- */
struct mv_xor_device {
- struct platform_device *pdev;
- int id;
- dma_addr_t dma_desc_pool;
- void *dma_desc_pool_virt;
- struct dma_device common;
- struct mv_xor_shared_private *shared;
+ void __iomem *xor_base;
+ void __iomem *xor_high_base;
+ struct clk *clk;
+ struct mv_xor_chan *channels[MV_XOR_MAX_CHANNELS];
};
/**
@@ -96,11 +82,15 @@ struct mv_xor_chan {
spinlock_t lock; /* protects the descriptor slot pool */
void __iomem *mmr_base;
unsigned int idx;
+ int irq;
enum dma_transaction_type current_type;
struct list_head chain;
struct list_head completed_slots;
- struct mv_xor_device *device;
- struct dma_chan common;
+ dma_addr_t dma_desc_pool;
+ void *dma_desc_pool_virt;
+ size_t pool_size;
+ struct dma_device dmadev;
+ struct dma_chan dmachan;
struct mv_xor_desc_slot *last_used;
struct list_head all_slots;
int slots_allocated;
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index bb82d6be793..4c6c876d9dc 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -7,7 +7,7 @@
menuconfig EDAC
bool "EDAC (Error Detection And Correction) reporting"
depends on HAS_IOMEM
- depends on X86 || PPC || TILE || ARM
+ depends on X86 || PPC || TILE || ARM || EDAC_SUPPORT
help
EDAC is designed to report errors in the core system.
These are low-level errors that are reported in the CPU or
@@ -27,6 +27,9 @@ menuconfig EDAC
There is also a mailing list for the EDAC project, which can
be found via the sourceforge page.
+config EDAC_SUPPORT
+ bool
+
if EDAC
comment "Reporting subsystems"
@@ -316,4 +319,32 @@ config EDAC_HIGHBANK_L2
Support for error detection and correction on the
Calxeda Highbank memory controller.
+config EDAC_OCTEON_PC
+ tristate "Cavium Octeon Primary Caches"
+ depends on EDAC_MM_EDAC && CPU_CAVIUM_OCTEON
+ help
+ Support for error detection and correction on the primary caches of
+ the cnMIPS cores of Cavium Octeon family SOCs.
+
+config EDAC_OCTEON_L2C
+ tristate "Cavium Octeon Secondary Caches (L2C)"
+ depends on EDAC_MM_EDAC && CPU_CAVIUM_OCTEON
+ help
+ Support for error detection and correction on the
+ Cavium Octeon family of SOCs.
+
+config EDAC_OCTEON_LMC
+ tristate "Cavium Octeon DRAM Memory Controller (LMC)"
+ depends on EDAC_MM_EDAC && CPU_CAVIUM_OCTEON
+ help
+ Support for error detection and correction on the
+ Cavium Octeon family of SOCs.
+
+config EDAC_OCTEON_PCI
+ tristate "Cavium Octeon PCI Controller"
+ depends on EDAC_MM_EDAC && PCI && CPU_CAVIUM_OCTEON
+ help
+ Support for error detection and correction on the
+ Cavium Octeon family of SOCs.
+
endif # EDAC
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index 7e5129a733f..5608a9ba61b 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -58,3 +58,8 @@ obj-$(CONFIG_EDAC_TILE) += tile_edac.o
obj-$(CONFIG_EDAC_HIGHBANK_MC) += highbank_mc_edac.o
obj-$(CONFIG_EDAC_HIGHBANK_L2) += highbank_l2_edac.o
+
+obj-$(CONFIG_EDAC_OCTEON_PC) += octeon_edac-pc.o
+obj-$(CONFIG_EDAC_OCTEON_L2C) += octeon_edac-l2c.o
+obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o
+obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o
diff --git a/drivers/edac/octeon_edac-l2c.c b/drivers/edac/octeon_edac-l2c.c
new file mode 100644
index 00000000000..40fde6a51ed
--- /dev/null
+++ b/drivers/edac/octeon_edac-l2c.c
@@ -0,0 +1,208 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2012 Cavium, Inc.
+ *
+ * Copyright (C) 2009 Wind River Systems,
+ * written by Ralf Baechle <ralf@linux-mips.org>
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/edac.h>
+
+#include <asm/octeon/cvmx.h>
+
+#include "edac_core.h"
+#include "edac_module.h"
+
+#define EDAC_MOD_STR "octeon-l2c"
+
+static void octeon_l2c_poll_oct1(struct edac_device_ctl_info *l2c)
+{
+ union cvmx_l2t_err l2t_err, l2t_err_reset;
+ union cvmx_l2d_err l2d_err, l2d_err_reset;
+
+ l2t_err_reset.u64 = 0;
+ l2t_err.u64 = cvmx_read_csr(CVMX_L2T_ERR);
+ if (l2t_err.s.sec_err) {
+ edac_device_handle_ce(l2c, 0, 0,
+ "Tag Single bit error (corrected)");
+ l2t_err_reset.s.sec_err = 1;
+ }
+ if (l2t_err.s.ded_err) {
+ edac_device_handle_ue(l2c, 0, 0,
+ "Tag Double bit error (detected)");
+ l2t_err_reset.s.ded_err = 1;
+ }
+ if (l2t_err_reset.u64)
+ cvmx_write_csr(CVMX_L2T_ERR, l2t_err_reset.u64);
+
+ l2d_err_reset.u64 = 0;
+ l2d_err.u64 = cvmx_read_csr(CVMX_L2D_ERR);
+ if (l2d_err.s.sec_err) {
+ edac_device_handle_ce(l2c, 0, 1,
+ "Data Single bit error (corrected)");
+ l2d_err_reset.s.sec_err = 1;
+ }
+ if (l2d_err.s.ded_err) {
+ edac_device_handle_ue(l2c, 0, 1,
+ "Data Double bit error (detected)");
+ l2d_err_reset.s.ded_err = 1;
+ }
+ if (l2d_err_reset.u64)
+ cvmx_write_csr(CVMX_L2D_ERR, l2d_err_reset.u64);
+
+}
+
+static void _octeon_l2c_poll_oct2(struct edac_device_ctl_info *l2c, int tad)
+{
+ union cvmx_l2c_err_tdtx err_tdtx, err_tdtx_reset;
+ union cvmx_l2c_err_ttgx err_ttgx, err_ttgx_reset;
+ char buf1[64];
+ char buf2[80];
+
+ err_tdtx_reset.u64 = 0;
+ err_tdtx.u64 = cvmx_read_csr(CVMX_L2C_ERR_TDTX(tad));
+ if (err_tdtx.s.dbe || err_tdtx.s.sbe ||
+ err_tdtx.s.vdbe || err_tdtx.s.vsbe)
+ snprintf(buf1, sizeof(buf1),
+ "type:%d, syn:0x%x, way:%d",
+ err_tdtx.s.type, err_tdtx.s.syn, err_tdtx.s.wayidx);
+
+ if (err_tdtx.s.dbe) {
+ snprintf(buf2, sizeof(buf2),
+ "L2D Double bit error (detected):%s", buf1);
+ err_tdtx_reset.s.dbe = 1;
+ edac_device_handle_ue(l2c, tad, 1, buf2);
+ }
+ if (err_tdtx.s.sbe) {
+ snprintf(buf2, sizeof(buf2),
+ "L2D Single bit error (corrected):%s", buf1);
+ err_tdtx_reset.s.sbe = 1;
+ edac_device_handle_ce(l2c, tad, 1, buf2);
+ }
+ if (err_tdtx.s.vdbe) {
+ snprintf(buf2, sizeof(buf2),
+ "VBF Double bit error (detected):%s", buf1);
+ err_tdtx_reset.s.vdbe = 1;
+ edac_device_handle_ue(l2c, tad, 1, buf2);
+ }
+ if (err_tdtx.s.vsbe) {
+ snprintf(buf2, sizeof(buf2),
+ "VBF Single bit error (corrected):%s", buf1);
+ err_tdtx_reset.s.vsbe = 1;
+ edac_device_handle_ce(l2c, tad, 1, buf2);
+ }
+ if (err_tdtx_reset.u64)
+ cvmx_write_csr(CVMX_L2C_ERR_TDTX(tad), err_tdtx_reset.u64);
+
+ err_ttgx_reset.u64 = 0;
+ err_ttgx.u64 = cvmx_read_csr(CVMX_L2C_ERR_TTGX(tad));
+
+ if (err_ttgx.s.dbe || err_ttgx.s.sbe)
+ snprintf(buf1, sizeof(buf1),
+ "type:%d, syn:0x%x, way:%d",
+ err_ttgx.s.type, err_ttgx.s.syn, err_ttgx.s.wayidx);
+
+ if (err_ttgx.s.dbe) {
+ snprintf(buf2, sizeof(buf2),
+ "Tag Double bit error (detected):%s", buf1);
+ err_ttgx_reset.s.dbe = 1;
+ edac_device_handle_ue(l2c, tad, 0, buf2);
+ }
+ if (err_ttgx.s.sbe) {
+ snprintf(buf2, sizeof(buf2),
+ "Tag Single bit error (corrected):%s", buf1);
+ err_ttgx_reset.s.sbe = 1;
+ edac_device_handle_ce(l2c, tad, 0, buf2);
+ }
+ if (err_ttgx_reset.u64)
+ cvmx_write_csr(CVMX_L2C_ERR_TTGX(tad), err_ttgx_reset.u64);
+}
+
+static void octeon_l2c_poll_oct2(struct edac_device_ctl_info *l2c)
+{
+ int i;
+ for (i = 0; i < l2c->nr_instances; i++)
+ _octeon_l2c_poll_oct2(l2c, i);
+}
+
+static int __devinit octeon_l2c_probe(struct platform_device *pdev)
+{
+ struct edac_device_ctl_info *l2c;
+
+ int num_tads = OCTEON_IS_MODEL(OCTEON_CN68XX) ? 4 : 1;
+
+ /* 'Tags' are block 0, 'Data' is block 1*/
+ l2c = edac_device_alloc_ctl_info(0, "l2c", num_tads, "l2c", 2, 0,
+ NULL, 0, edac_device_alloc_index());
+ if (!l2c)
+ return -ENOMEM;
+
+ l2c->dev = &pdev->dev;
+ platform_set_drvdata(pdev, l2c);
+ l2c->dev_name = dev_name(&pdev->dev);
+
+ l2c->mod_name = "octeon-l2c";
+ l2c->ctl_name = "octeon_l2c_err";
+
+
+ if (OCTEON_IS_MODEL(OCTEON_FAM_1_PLUS)) {
+ union cvmx_l2t_err l2t_err;
+ union cvmx_l2d_err l2d_err;
+
+ l2t_err.u64 = cvmx_read_csr(CVMX_L2T_ERR);
+ l2t_err.s.sec_intena = 0; /* We poll */
+ l2t_err.s.ded_intena = 0;
+ cvmx_write_csr(CVMX_L2T_ERR, l2t_err.u64);
+
+ l2d_err.u64 = cvmx_read_csr(CVMX_L2D_ERR);
+ l2d_err.s.sec_intena = 0; /* We poll */
+ l2d_err.s.ded_intena = 0;
+ cvmx_write_csr(CVMX_L2T_ERR, l2d_err.u64);
+
+ l2c->edac_check = octeon_l2c_poll_oct1;
+ } else {
+ /* OCTEON II */
+ l2c->edac_check = octeon_l2c_poll_oct2;
+ }
+
+ if (edac_device_add_device(l2c) > 0) {
+ pr_err("%s: edac_device_add_device() failed\n", __func__);
+ goto err;
+ }
+
+
+ return 0;
+
+err:
+ edac_device_free_ctl_info(l2c);
+
+ return -ENXIO;
+}
+
+static int octeon_l2c_remove(struct platform_device *pdev)
+{
+ struct edac_device_ctl_info *l2c = platform_get_drvdata(pdev);
+
+ edac_device_del_device(&pdev->dev);
+ edac_device_free_ctl_info(l2c);
+
+ return 0;
+}
+
+static struct platform_driver octeon_l2c_driver = {
+ .probe = octeon_l2c_probe,
+ .remove = octeon_l2c_remove,
+ .driver = {
+ .name = "octeon_l2c_edac",
+ }
+};
+module_platform_driver(octeon_l2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");
diff --git a/drivers/edac/octeon_edac-lmc.c b/drivers/edac/octeon_edac-lmc.c
new file mode 100644
index 00000000000..33bca766e37
--- /dev/null
+++ b/drivers/edac/octeon_edac-lmc.c
@@ -0,0 +1,186 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2009 Wind River Systems,
+ * written by Ralf Baechle <ralf@linux-mips.org>
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/edac.h>
+
+#include <asm/octeon/octeon.h>
+#include <asm/octeon/cvmx-lmcx-defs.h>
+
+#include "edac_core.h"
+#include "edac_module.h"
+
+#define OCTEON_MAX_MC 4
+
+static void octeon_lmc_edac_poll(struct mem_ctl_info *mci)
+{
+ union cvmx_lmcx_mem_cfg0 cfg0;
+ bool do_clear = false;
+ char msg[64];
+
+ cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mci->mc_idx));
+ if (cfg0.s.sec_err || cfg0.s.ded_err) {
+ union cvmx_lmcx_fadr fadr;
+ fadr.u64 = cvmx_read_csr(CVMX_LMCX_FADR(mci->mc_idx));
+ snprintf(msg, sizeof(msg),
+ "DIMM %d rank %d bank %d row %d col %d",
+ fadr.cn30xx.fdimm, fadr.cn30xx.fbunk,
+ fadr.cn30xx.fbank, fadr.cn30xx.frow, fadr.cn30xx.fcol);
+ }
+
+ if (cfg0.s.sec_err) {
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0,
+ -1, -1, -1, msg, "");
+ cfg0.s.sec_err = -1; /* Done, re-arm */
+ do_clear = true;
+ }
+
+ if (cfg0.s.ded_err) {
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
+ -1, -1, -1, msg, "");
+ cfg0.s.ded_err = -1; /* Done, re-arm */
+ do_clear = true;
+ }
+ if (do_clear)
+ cvmx_write_csr(CVMX_LMCX_MEM_CFG0(mci->mc_idx), cfg0.u64);
+}
+
+static void octeon_lmc_edac_poll_o2(struct mem_ctl_info *mci)
+{
+ union cvmx_lmcx_int int_reg;
+ bool do_clear = false;
+ char msg[64];
+
+ int_reg.u64 = cvmx_read_csr(CVMX_LMCX_INT(mci->mc_idx));
+ if (int_reg.s.sec_err || int_reg.s.ded_err) {
+ union cvmx_lmcx_fadr fadr;
+ fadr.u64 = cvmx_read_csr(CVMX_LMCX_FADR(mci->mc_idx));
+ snprintf(msg, sizeof(msg),
+ "DIMM %d rank %d bank %d row %d col %d",
+ fadr.cn61xx.fdimm, fadr.cn61xx.fbunk,
+ fadr.cn61xx.fbank, fadr.cn61xx.frow, fadr.cn61xx.fcol);
+ }
+
+ if (int_reg.s.sec_err) {
+ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0,
+ -1, -1, -1, msg, "");
+ int_reg.s.sec_err = -1; /* Done, re-arm */
+ do_clear = true;
+ }
+
+ if (int_reg.s.ded_err) {
+ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
+ -1, -1, -1, msg, "");
+ int_reg.s.ded_err = -1; /* Done, re-arm */
+ do_clear = true;
+ }
+ if (do_clear)
+ cvmx_write_csr(CVMX_LMCX_INT(mci->mc_idx), int_reg.u64);
+}
+
+static int __devinit octeon_lmc_edac_probe(struct platform_device *pdev)
+{
+ struct mem_ctl_info *mci;
+ struct edac_mc_layer layers[1];
+ int mc = pdev->id;
+
+ layers[0].type = EDAC_MC_LAYER_CHANNEL;
+ layers[0].size = 1;
+ layers[0].is_virt_csrow = false;
+
+ if (OCTEON_IS_MODEL(OCTEON_FAM_1_PLUS)) {
+ union cvmx_lmcx_mem_cfg0 cfg0;
+
+ cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(0));
+ if (!cfg0.s.ecc_ena) {
+ dev_info(&pdev->dev, "Disabled (ECC not enabled)\n");
+ return 0;
+ }
+
+ mci = edac_mc_alloc(mc, ARRAY_SIZE(layers), layers, 0);
+ if (!mci)
+ return -ENXIO;
+
+ mci->pdev = &pdev->dev;
+ mci->dev_name = dev_name(&pdev->dev);
+
+ mci->mod_name = "octeon-lmc";
+ mci->ctl_name = "octeon-lmc-err";
+ mci->edac_check = octeon_lmc_edac_poll;
+
+ if (edac_mc_add_mc(mci)) {
+ dev_err(&pdev->dev, "edac_mc_add_mc() failed\n");
+ edac_mc_free(mci);
+ return -ENXIO;
+ }
+
+ cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mc));
+ cfg0.s.intr_ded_ena = 0; /* We poll */
+ cfg0.s.intr_sec_ena = 0;
+ cvmx_write_csr(CVMX_LMCX_MEM_CFG0(mc), cfg0.u64);
+ } else {
+ /* OCTEON II */
+ union cvmx_lmcx_int_en en;
+ union cvmx_lmcx_config config;
+
+ config.u64 = cvmx_read_csr(CVMX_LMCX_CONFIG(0));
+ if (!config.s.ecc_ena) {
+ dev_info(&pdev->dev, "Disabled (ECC not enabled)\n");
+ return 0;
+ }
+
+ mci = edac_mc_alloc(mc, ARRAY_SIZE(layers), layers, 0);
+ if (!mci)
+ return -ENXIO;
+
+ mci->pdev = &pdev->dev;
+ mci->dev_name = dev_name(&pdev->dev);
+
+ mci->mod_name = "octeon-lmc";
+ mci->ctl_name = "co_lmc_err";
+ mci->edac_check = octeon_lmc_edac_poll_o2;
+
+ if (edac_mc_add_mc(mci)) {
+ dev_err(&pdev->dev, "edac_mc_add_mc() failed\n");
+ edac_mc_free(mci);
+ return -ENXIO;
+ }
+
+ en.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mc));
+ en.s.intr_ded_ena = 0; /* We poll */
+ en.s.intr_sec_ena = 0;
+ cvmx_write_csr(CVMX_LMCX_MEM_CFG0(mc), en.u64);
+ }
+ platform_set_drvdata(pdev, mci);
+
+ return 0;
+}
+
+static int octeon_lmc_edac_remove(struct platform_device *pdev)
+{
+ struct mem_ctl_info *mci = platform_get_drvdata(pdev);
+
+ edac_mc_del_mc(&pdev->dev);
+ edac_mc_free(mci);
+ return 0;
+}
+
+static struct platform_driver octeon_lmc_edac_driver = {
+ .probe = octeon_lmc_edac_probe,
+ .remove = octeon_lmc_edac_remove,
+ .driver = {
+ .name = "octeon_lmc_edac",
+ }
+};
+module_platform_driver(octeon_lmc_edac_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");
diff --git a/drivers/edac/octeon_edac-pc.c b/drivers/edac/octeon_edac-pc.c
new file mode 100644
index 00000000000..14a5e57f2b3
--- /dev/null
+++ b/drivers/edac/octeon_edac-pc.c
@@ -0,0 +1,143 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2012 Cavium, Inc.
+ *
+ * Copyright (C) 2009 Wind River Systems,
+ * written by Ralf Baechle <ralf@linux-mips.org>
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/edac.h>
+
+#include "edac_core.h"
+#include "edac_module.h"
+
+#include <asm/octeon/cvmx.h>
+#include <asm/mipsregs.h>
+
+extern int register_co_cache_error_notifier(struct notifier_block *nb);
+extern int unregister_co_cache_error_notifier(struct notifier_block *nb);
+
+extern unsigned long long cache_err_dcache[NR_CPUS];
+
+struct co_cache_error {
+ struct notifier_block notifier;
+ struct edac_device_ctl_info *ed;
+};
+
+/**
+ * EDAC CPU cache error callback
+ *
+ * @event: non-zero if unrecoverable.
+ */
+static int co_cache_error_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct co_cache_error *p = container_of(this, struct co_cache_error,
+ notifier);
+
+ unsigned int core = cvmx_get_core_num();
+ unsigned int cpu = smp_processor_id();
+ u64 icache_err = read_octeon_c0_icacheerr();
+ u64 dcache_err;
+
+ if (event) {
+ dcache_err = cache_err_dcache[core];
+ cache_err_dcache[core] = 0;
+ } else {
+ dcache_err = read_octeon_c0_dcacheerr();
+ }
+
+ if (icache_err & 1) {
+ edac_device_printk(p->ed, KERN_ERR,
+ "CacheErr (Icache):%llx, core %d/cpu %d, cp0_errorepc == %lx\n",
+ (unsigned long long)icache_err, core, cpu,
+ read_c0_errorepc());
+ write_octeon_c0_icacheerr(0);
+ edac_device_handle_ce(p->ed, cpu, 1, "icache");
+ }
+ if (dcache_err & 1) {
+ edac_device_printk(p->ed, KERN_ERR,
+ "CacheErr (Dcache):%llx, core %d/cpu %d, cp0_errorepc == %lx\n",
+ (unsigned long long)dcache_err, core, cpu,
+ read_c0_errorepc());
+ if (event)
+ edac_device_handle_ue(p->ed, cpu, 0, "dcache");
+ else
+ edac_device_handle_ce(p->ed, cpu, 0, "dcache");
+
+ /* Clear the error indication */
+ if (OCTEON_IS_MODEL(OCTEON_FAM_2))
+ write_octeon_c0_dcacheerr(1);
+ else
+ write_octeon_c0_dcacheerr(0);
+ }
+
+ return NOTIFY_STOP;
+}
+
+static int __devinit co_cache_error_probe(struct platform_device *pdev)
+{
+ struct co_cache_error *p = devm_kzalloc(&pdev->dev, sizeof(*p),
+ GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ p->notifier.notifier_call = co_cache_error_event;
+ platform_set_drvdata(pdev, p);
+
+ p->ed = edac_device_alloc_ctl_info(0, "cpu", num_possible_cpus(),
+ "cache", 2, 0, NULL, 0,
+ edac_device_alloc_index());
+ if (!p->ed)
+ goto err;
+
+ p->ed->dev = &pdev->dev;
+
+ p->ed->dev_name = dev_name(&pdev->dev);
+
+ p->ed->mod_name = "octeon-cpu";
+ p->ed->ctl_name = "cache";
+
+ if (edac_device_add_device(p->ed)) {
+ pr_err("%s: edac_device_add_device() failed\n", __func__);
+ goto err1;
+ }
+
+ register_co_cache_error_notifier(&p->notifier);
+
+ return 0;
+
+err1:
+ edac_device_free_ctl_info(p->ed);
+err:
+ return -ENXIO;
+}
+
+static int co_cache_error_remove(struct platform_device *pdev)
+{
+ struct co_cache_error *p = platform_get_drvdata(pdev);
+
+ unregister_co_cache_error_notifier(&p->notifier);
+ edac_device_del_device(&pdev->dev);
+ edac_device_free_ctl_info(p->ed);
+ return 0;
+}
+
+static struct platform_driver co_cache_error_driver = {
+ .probe = co_cache_error_probe,
+ .remove = co_cache_error_remove,
+ .driver = {
+ .name = "octeon_pc_edac",
+ }
+};
+module_platform_driver(co_cache_error_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");
diff --git a/drivers/edac/octeon_edac-pci.c b/drivers/edac/octeon_edac-pci.c
new file mode 100644
index 00000000000..758c1ef5fc9
--- /dev/null
+++ b/drivers/edac/octeon_edac-pci.c
@@ -0,0 +1,111 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2012 Cavium, Inc.
+ * Copyright (C) 2009 Wind River Systems,
+ * written by Ralf Baechle <ralf@linux-mips.org>
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/edac.h>
+
+#include <asm/octeon/cvmx.h>
+#include <asm/octeon/cvmx-npi-defs.h>
+#include <asm/octeon/cvmx-pci-defs.h>
+#include <asm/octeon/octeon.h>
+
+#include "edac_core.h"
+#include "edac_module.h"
+
+static void octeon_pci_poll(struct edac_pci_ctl_info *pci)
+{
+ union cvmx_pci_cfg01 cfg01;
+
+ cfg01.u32 = octeon_npi_read32(CVMX_NPI_PCI_CFG01);
+ if (cfg01.s.dpe) { /* Detected parity error */
+ edac_pci_handle_pe(pci, pci->ctl_name);
+ cfg01.s.dpe = 1; /* Reset */
+ octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32);
+ }
+ if (cfg01.s.sse) {
+ edac_pci_handle_npe(pci, "Signaled System Error");
+ cfg01.s.sse = 1; /* Reset */
+ octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32);
+ }
+ if (cfg01.s.rma) {
+ edac_pci_handle_npe(pci, "Received Master Abort");
+ cfg01.s.rma = 1; /* Reset */
+ octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32);
+ }
+ if (cfg01.s.rta) {
+ edac_pci_handle_npe(pci, "Received Target Abort");
+ cfg01.s.rta = 1; /* Reset */
+ octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32);
+ }
+ if (cfg01.s.sta) {
+ edac_pci_handle_npe(pci, "Signaled Target Abort");
+ cfg01.s.sta = 1; /* Reset */
+ octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32);
+ }
+ if (cfg01.s.mdpe) {
+ edac_pci_handle_npe(pci, "Master Data Parity Error");
+ cfg01.s.mdpe = 1; /* Reset */
+ octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32);
+ }
+}
+
+static int __devinit octeon_pci_probe(struct platform_device *pdev)
+{
+ struct edac_pci_ctl_info *pci;
+ int res = 0;
+
+ pci = edac_pci_alloc_ctl_info(0, "octeon_pci_err");
+ if (!pci)
+ return -ENOMEM;
+
+ pci->dev = &pdev->dev;
+ platform_set_drvdata(pdev, pci);
+ pci->dev_name = dev_name(&pdev->dev);
+
+ pci->mod_name = "octeon-pci";
+ pci->ctl_name = "octeon_pci_err";
+ pci->edac_check = octeon_pci_poll;
+
+ if (edac_pci_add_device(pci, 0) > 0) {
+ pr_err("%s: edac_pci_add_device() failed\n", __func__);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ edac_pci_free_ctl_info(pci);
+
+ return res;
+}
+
+static int octeon_pci_remove(struct platform_device *pdev)
+{
+ struct edac_pci_ctl_info *pci = platform_get_drvdata(pdev);
+
+ edac_pci_del_device(&pdev->dev);
+ edac_pci_free_ctl_info(pci);
+
+ return 0;
+}
+
+static struct platform_driver octeon_pci_driver = {
+ .probe = octeon_pci_probe,
+ .remove = octeon_pci_remove,
+ .driver = {
+ .name = "octeon_pci_edac",
+ }
+};
+module_platform_driver(octeon_pci_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");
diff --git a/drivers/eisa/eisa.ids b/drivers/eisa/eisa.ids
index 6cbb7a51443..2d864b48a48 100644
--- a/drivers/eisa/eisa.ids
+++ b/drivers/eisa/eisa.ids
@@ -985,8 +985,8 @@ ISABD00 "Racal-Interlan NP600A Ethernet 16bit"
ISABD02 "Racal-Interlan NI5210/8 Ethernet"
ISABD03 "Racal-Interlan NI5210/16 Ethernet"
ISABE00 "Qua Tech PXB-1608 Parallel Expansion Board"
-ISABE01 "Qua Tech ES-100 8 Channel Asyncronous"
-ISABE02 "Qua Tech QS-100M 4 Channel Asyncronous"
+ISABE01 "Qua Tech ES-100 8 Channel Asynchronous"
+ISABE02 "Qua Tech QS-100M 4 Channel Asynchronous"
ISABE03 "Qua Tech MXI-100 IEEE 488 GPIB"
ISABE04 "Qua Tech DS-201 Dual Channel RS-422"
ISABE05 "Qua Tech PXB-721 Parallel Expansion"
diff --git a/drivers/firewire/init_ohci1394_dma.c b/drivers/firewire/init_ohci1394_dma.c
index a9a347adb35..2cc89ce745c 100644
--- a/drivers/firewire/init_ohci1394_dma.c
+++ b/drivers/firewire/init_ohci1394_dma.c
@@ -149,10 +149,10 @@ static inline void __init init_ohci1394_initialize(struct ohci *ohci)
reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff);
reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff);
- /* Accept asyncronous transfer requests from all nodes for now */
+ /* Accept asynchronous transfer requests from all nodes for now */
reg_write(ohci, OHCI1394_AsReqFilterHiSet, 0x80000000);
- /* Specify asyncronous transfer retries */
+ /* Specify asynchronous transfer retries */
reg_write(ohci, OHCI1394_ATRetries,
OHCI1394_MAX_AT_REQ_RETRIES |
(OHCI1394_MAX_AT_RESP_RETRIES<<4) |
diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c
index 08c674957af..e7a711f53a6 100644
--- a/drivers/firewire/net.c
+++ b/drivers/firewire/net.c
@@ -828,7 +828,6 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context,
{
struct fwnet_device *dev;
struct fw_iso_packet packet;
- struct fw_card *card;
__be16 *hdr_ptr;
__be32 *buf_ptr;
int retval;
@@ -840,7 +839,6 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context,
unsigned long flags;
dev = data;
- card = dev->card;
hdr_ptr = header;
length = be16_to_cpup(hdr_ptr);
@@ -861,8 +859,8 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context,
if (specifier_id == IANA_SPECIFIER_ID && ver == RFC2734_SW_VERSION) {
buf_ptr += 2;
length -= IEEE1394_GASP_HDR_SIZE;
- fwnet_incoming_packet(dev, buf_ptr, length,
- source_node_id, -1, true);
+ fwnet_incoming_packet(dev, buf_ptr, length, source_node_id,
+ context->card->generation, true);
}
packet.payload_length = dev->rcv_buffer_size;
@@ -958,7 +956,12 @@ static void fwnet_transmit_packet_done(struct fwnet_packet_task *ptask)
break;
}
- skb_pull(skb, ptask->max_payload);
+ if (ptask->dest_node == IEEE1394_ALL_NODES) {
+ skb_pull(skb,
+ ptask->max_payload + IEEE1394_GASP_HDR_SIZE);
+ } else {
+ skb_pull(skb, ptask->max_payload);
+ }
if (ptask->outstanding_pkts > 1) {
fwnet_make_sf_hdr(&ptask->hdr, RFC2374_HDR_INTFRAG,
dg_size, fg_off, datagram_label);
@@ -1062,7 +1065,7 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask)
smp_rmb();
node_id = dev->card->node_id;
- p = skb_push(ptask->skb, 8);
+ p = skb_push(ptask->skb, IEEE1394_GASP_HDR_SIZE);
put_unaligned_be32(node_id << 16 | IANA_SPECIFIER_ID >> 8, p);
put_unaligned_be32((IANA_SPECIFIER_ID & 0xff) << 24
| RFC2734_SW_VERSION, &p[4]);
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index f25610bb314..6ce6e07c38c 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -1281,7 +1281,7 @@ static int at_context_queue_packet(struct context *ctx,
d[0].res_count = cpu_to_le16(packet->timestamp);
/*
- * The DMA format for asyncronous link packets is different
+ * The DMA format for asynchronous link packets is different
* from the IEEE1394 layout, so shift the fields around
* accordingly.
*/
diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c
index bb1b392f5cd..1162d6b3bf8 100644
--- a/drivers/firewire/sbp2.c
+++ b/drivers/firewire/sbp2.c
@@ -1546,8 +1546,6 @@ static int sbp2_scsi_slave_configure(struct scsi_device *sdev)
struct sbp2_logical_unit *lu = sdev->hostdata;
sdev->use_10_for_rw = 1;
- sdev->no_report_opcodes = 1;
- sdev->no_write_same = 1;
if (sbp2_param_exclusive_login)
sdev->manage_start_stop = 1;
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index 6e51c1e81f1..52c5d8956d7 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -80,6 +80,10 @@
#include <linux/slab.h>
#include <linux/pstore.h>
+#include <linux/fs.h>
+#include <linux/ramfs.h>
+#include <linux/pagemap.h>
+
#include <asm/uaccess.h>
#define EFIVARS_VERSION "0.08"
@@ -93,6 +97,12 @@ MODULE_VERSION(EFIVARS_VERSION);
#define DUMP_NAME_LEN 52
/*
+ * Length of a GUID string (strlen("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"))
+ * not including trailing NUL
+ */
+#define GUID_LEN 36
+
+/*
* The maximum size of VariableName + Data = 1024
* Therefore, it's reasonable to save that much
* space in each part of the structure,
@@ -108,7 +118,6 @@ struct efi_variable {
__u32 Attributes;
} __attribute__((packed));
-
struct efivar_entry {
struct efivars *efivars;
struct efi_variable var;
@@ -122,6 +131,9 @@ struct efivar_attribute {
ssize_t (*store)(struct efivar_entry *entry, const char *buf, size_t count);
};
+static struct efivars __efivars;
+static struct efivar_operations ops;
+
#define PSTORE_EFI_ATTRIBUTES \
(EFI_VARIABLE_NON_VOLATILE | \
EFI_VARIABLE_BOOTSERVICE_ACCESS | \
@@ -629,14 +641,482 @@ static struct kobj_type efivar_ktype = {
.default_attrs = def_attrs,
};
-static struct pstore_info efi_pstore_info;
-
static inline void
efivar_unregister(struct efivar_entry *var)
{
kobject_put(&var->kobj);
}
+static int efivarfs_file_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static int efi_status_to_err(efi_status_t status)
+{
+ int err;
+
+ switch (status) {
+ case EFI_INVALID_PARAMETER:
+ err = -EINVAL;
+ break;
+ case EFI_OUT_OF_RESOURCES:
+ err = -ENOSPC;
+ break;
+ case EFI_DEVICE_ERROR:
+ err = -EIO;
+ break;
+ case EFI_WRITE_PROTECTED:
+ err = -EROFS;
+ break;
+ case EFI_SECURITY_VIOLATION:
+ err = -EACCES;
+ break;
+ case EFI_NOT_FOUND:
+ err = -ENOENT;
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+static ssize_t efivarfs_file_write(struct file *file,
+ const char __user *userbuf, size_t count, loff_t *ppos)
+{
+ struct efivar_entry *var = file->private_data;
+ struct efivars *efivars;
+ efi_status_t status;
+ void *data;
+ u32 attributes;
+ struct inode *inode = file->f_mapping->host;
+ unsigned long datasize = count - sizeof(attributes);
+ unsigned long newdatasize;
+ u64 storage_size, remaining_size, max_size;
+ ssize_t bytes = 0;
+
+ if (count < sizeof(attributes))
+ return -EINVAL;
+
+ if (copy_from_user(&attributes, userbuf, sizeof(attributes)))
+ return -EFAULT;
+
+ if (attributes & ~(EFI_VARIABLE_MASK))
+ return -EINVAL;
+
+ efivars = var->efivars;
+
+ /*
+ * Ensure that the user can't allocate arbitrarily large
+ * amounts of memory. Pick a default size of 64K if
+ * QueryVariableInfo() isn't supported by the firmware.
+ */
+ spin_lock(&efivars->lock);
+
+ if (!efivars->ops->query_variable_info)
+ status = EFI_UNSUPPORTED;
+ else {
+ const struct efivar_operations *fops = efivars->ops;
+ status = fops->query_variable_info(attributes, &storage_size,
+ &remaining_size, &max_size);
+ }
+
+ spin_unlock(&efivars->lock);
+
+ if (status != EFI_SUCCESS) {
+ if (status != EFI_UNSUPPORTED)
+ return efi_status_to_err(status);
+
+ remaining_size = 65536;
+ }
+
+ if (datasize > remaining_size)
+ return -ENOSPC;
+
+ data = kmalloc(datasize, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (copy_from_user(data, userbuf + sizeof(attributes), datasize)) {
+ bytes = -EFAULT;
+ goto out;
+ }
+
+ if (validate_var(&var->var, data, datasize) == false) {
+ bytes = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * The lock here protects the get_variable call, the conditional
+ * set_variable call, and removal of the variable from the efivars
+ * list (in the case of an authenticated delete).
+ */
+ spin_lock(&efivars->lock);
+
+ status = efivars->ops->set_variable(var->var.VariableName,
+ &var->var.VendorGuid,
+ attributes, datasize,
+ data);
+
+ if (status != EFI_SUCCESS) {
+ spin_unlock(&efivars->lock);
+ kfree(data);
+
+ return efi_status_to_err(status);
+ }
+
+ bytes = count;
+
+ /*
+ * Writing to the variable may have caused a change in size (which
+ * could either be an append or an overwrite), or the variable to be
+ * deleted. Perform a GetVariable() so we can tell what actually
+ * happened.
+ */
+ newdatasize = 0;
+ status = efivars->ops->get_variable(var->var.VariableName,
+ &var->var.VendorGuid,
+ NULL, &newdatasize,
+ NULL);
+
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ spin_unlock(&efivars->lock);
+ mutex_lock(&inode->i_mutex);
+ i_size_write(inode, newdatasize + sizeof(attributes));
+ mutex_unlock(&inode->i_mutex);
+
+ } else if (status == EFI_NOT_FOUND) {
+ list_del(&var->list);
+ spin_unlock(&efivars->lock);
+ efivar_unregister(var);
+ drop_nlink(inode);
+ dput(file->f_dentry);
+
+ } else {
+ spin_unlock(&efivars->lock);
+ pr_warn("efivarfs: inconsistent EFI variable implementation? "
+ "status = %lx\n", status);
+ }
+
+out:
+ kfree(data);
+
+ return bytes;
+}
+
+static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct efivar_entry *var = file->private_data;
+ struct efivars *efivars = var->efivars;
+ efi_status_t status;
+ unsigned long datasize = 0;
+ u32 attributes;
+ void *data;
+ ssize_t size = 0;
+
+ spin_lock(&efivars->lock);
+ status = efivars->ops->get_variable(var->var.VariableName,
+ &var->var.VendorGuid,
+ &attributes, &datasize, NULL);
+ spin_unlock(&efivars->lock);
+
+ if (status != EFI_BUFFER_TOO_SMALL)
+ return efi_status_to_err(status);
+
+ data = kmalloc(datasize + sizeof(attributes), GFP_KERNEL);
+
+ if (!data)
+ return -ENOMEM;
+
+ spin_lock(&efivars->lock);
+ status = efivars->ops->get_variable(var->var.VariableName,
+ &var->var.VendorGuid,
+ &attributes, &datasize,
+ (data + sizeof(attributes)));
+ spin_unlock(&efivars->lock);
+
+ if (status != EFI_SUCCESS) {
+ size = efi_status_to_err(status);
+ goto out_free;
+ }
+
+ memcpy(data, &attributes, sizeof(attributes));
+ size = simple_read_from_buffer(userbuf, count, ppos,
+ data, datasize + sizeof(attributes));
+out_free:
+ kfree(data);
+
+ return size;
+}
+
+static void efivarfs_evict_inode(struct inode *inode)
+{
+ clear_inode(inode);
+}
+
+static const struct super_operations efivarfs_ops = {
+ .statfs = simple_statfs,
+ .drop_inode = generic_delete_inode,
+ .evict_inode = efivarfs_evict_inode,
+ .show_options = generic_show_options,
+};
+
+static struct super_block *efivarfs_sb;
+
+static const struct inode_operations efivarfs_dir_inode_operations;
+
+static const struct file_operations efivarfs_file_operations = {
+ .open = efivarfs_file_open,
+ .read = efivarfs_file_read,
+ .write = efivarfs_file_write,
+ .llseek = no_llseek,
+};
+
+static struct inode *efivarfs_get_inode(struct super_block *sb,
+ const struct inode *dir, int mode, dev_t dev)
+{
+ struct inode *inode = new_inode(sb);
+
+ if (inode) {
+ inode->i_ino = get_next_ino();
+ inode->i_uid = inode->i_gid = 0;
+ inode->i_mode = mode;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ switch (mode & S_IFMT) {
+ case S_IFREG:
+ inode->i_fop = &efivarfs_file_operations;
+ break;
+ case S_IFDIR:
+ inode->i_op = &efivarfs_dir_inode_operations;
+ inode->i_fop = &simple_dir_operations;
+ inc_nlink(inode);
+ break;
+ }
+ }
+ return inode;
+}
+
+static void efivarfs_hex_to_guid(const char *str, efi_guid_t *guid)
+{
+ guid->b[0] = hex_to_bin(str[6]) << 4 | hex_to_bin(str[7]);
+ guid->b[1] = hex_to_bin(str[4]) << 4 | hex_to_bin(str[5]);
+ guid->b[2] = hex_to_bin(str[2]) << 4 | hex_to_bin(str[3]);
+ guid->b[3] = hex_to_bin(str[0]) << 4 | hex_to_bin(str[1]);
+ guid->b[4] = hex_to_bin(str[11]) << 4 | hex_to_bin(str[12]);
+ guid->b[5] = hex_to_bin(str[9]) << 4 | hex_to_bin(str[10]);
+ guid->b[6] = hex_to_bin(str[16]) << 4 | hex_to_bin(str[17]);
+ guid->b[7] = hex_to_bin(str[14]) << 4 | hex_to_bin(str[15]);
+ guid->b[8] = hex_to_bin(str[19]) << 4 | hex_to_bin(str[20]);
+ guid->b[9] = hex_to_bin(str[21]) << 4 | hex_to_bin(str[22]);
+ guid->b[10] = hex_to_bin(str[24]) << 4 | hex_to_bin(str[25]);
+ guid->b[11] = hex_to_bin(str[26]) << 4 | hex_to_bin(str[27]);
+ guid->b[12] = hex_to_bin(str[28]) << 4 | hex_to_bin(str[29]);
+ guid->b[13] = hex_to_bin(str[30]) << 4 | hex_to_bin(str[31]);
+ guid->b[14] = hex_to_bin(str[32]) << 4 | hex_to_bin(str[33]);
+ guid->b[15] = hex_to_bin(str[34]) << 4 | hex_to_bin(str[35]);
+}
+
+static int efivarfs_create(struct inode *dir, struct dentry *dentry,
+ umode_t mode, bool excl)
+{
+ struct inode *inode;
+ struct efivars *efivars = &__efivars;
+ struct efivar_entry *var;
+ int namelen, i = 0, err = 0;
+
+ /*
+ * We need a GUID, plus at least one letter for the variable name,
+ * plus the '-' separator
+ */
+ if (dentry->d_name.len < GUID_LEN + 2)
+ return -EINVAL;
+
+ inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0);
+ if (!inode)
+ return -ENOMEM;
+
+ var = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL);
+ if (!var) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /* length of the variable name itself: remove GUID and separator */
+ namelen = dentry->d_name.len - GUID_LEN - 1;
+
+ efivarfs_hex_to_guid(dentry->d_name.name + namelen + 1,
+ &var->var.VendorGuid);
+
+ for (i = 0; i < namelen; i++)
+ var->var.VariableName[i] = dentry->d_name.name[i];
+
+ var->var.VariableName[i] = '\0';
+
+ inode->i_private = var;
+ var->efivars = efivars;
+ var->kobj.kset = efivars->kset;
+
+ err = kobject_init_and_add(&var->kobj, &efivar_ktype, NULL, "%s",
+ dentry->d_name.name);
+ if (err)
+ goto out;
+
+ kobject_uevent(&var->kobj, KOBJ_ADD);
+ spin_lock(&efivars->lock);
+ list_add(&var->list, &efivars->list);
+ spin_unlock(&efivars->lock);
+ d_instantiate(dentry, inode);
+ dget(dentry);
+out:
+ if (err) {
+ kfree(var);
+ iput(inode);
+ }
+ return err;
+}
+
+static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct efivar_entry *var = dentry->d_inode->i_private;
+ struct efivars *efivars = var->efivars;
+ efi_status_t status;
+
+ spin_lock(&efivars->lock);
+
+ status = efivars->ops->set_variable(var->var.VariableName,
+ &var->var.VendorGuid,
+ 0, 0, NULL);
+
+ if (status == EFI_SUCCESS || status == EFI_NOT_FOUND) {
+ list_del(&var->list);
+ spin_unlock(&efivars->lock);
+ efivar_unregister(var);
+ drop_nlink(dir);
+ dput(dentry);
+ return 0;
+ }
+
+ spin_unlock(&efivars->lock);
+ return -EINVAL;
+};
+
+static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+ struct inode *inode = NULL;
+ struct dentry *root;
+ struct efivar_entry *entry, *n;
+ struct efivars *efivars = &__efivars;
+ char *name;
+
+ efivarfs_sb = sb;
+
+ sb->s_maxbytes = MAX_LFS_FILESIZE;
+ sb->s_blocksize = PAGE_CACHE_SIZE;
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ sb->s_magic = EFIVARFS_MAGIC;
+ sb->s_op = &efivarfs_ops;
+ sb->s_time_gran = 1;
+
+ inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0);
+ if (!inode)
+ return -ENOMEM;
+ inode->i_op = &efivarfs_dir_inode_operations;
+
+ root = d_make_root(inode);
+ sb->s_root = root;
+ if (!root)
+ return -ENOMEM;
+
+ list_for_each_entry_safe(entry, n, &efivars->list, list) {
+ struct dentry *dentry, *root = efivarfs_sb->s_root;
+ unsigned long size = 0;
+ int len, i;
+
+ inode = NULL;
+
+ len = utf16_strlen(entry->var.VariableName);
+
+ /* name, plus '-', plus GUID, plus NUL*/
+ name = kmalloc(len + 1 + GUID_LEN + 1, GFP_ATOMIC);
+ if (!name)
+ goto fail;
+
+ for (i = 0; i < len; i++)
+ name[i] = entry->var.VariableName[i] & 0xFF;
+
+ name[len] = '-';
+
+ efi_guid_unparse(&entry->var.VendorGuid, name + len + 1);
+
+ name[len+GUID_LEN+1] = '\0';
+
+ inode = efivarfs_get_inode(efivarfs_sb, root->d_inode,
+ S_IFREG | 0644, 0);
+ if (!inode)
+ goto fail_name;
+
+ dentry = d_alloc_name(root, name);
+ if (!dentry)
+ goto fail_inode;
+
+ /* copied by the above to local storage in the dentry. */
+ kfree(name);
+
+ spin_lock(&efivars->lock);
+ efivars->ops->get_variable(entry->var.VariableName,
+ &entry->var.VendorGuid,
+ &entry->var.Attributes,
+ &size,
+ NULL);
+ spin_unlock(&efivars->lock);
+
+ mutex_lock(&inode->i_mutex);
+ inode->i_private = entry;
+ i_size_write(inode, size+4);
+ mutex_unlock(&inode->i_mutex);
+ d_add(dentry, inode);
+ }
+
+ return 0;
+
+fail_inode:
+ iput(inode);
+fail_name:
+ kfree(name);
+fail:
+ return -ENOMEM;
+}
+
+static struct dentry *efivarfs_mount(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data)
+{
+ return mount_single(fs_type, flags, data, efivarfs_fill_super);
+}
+
+static void efivarfs_kill_sb(struct super_block *sb)
+{
+ kill_litter_super(sb);
+ efivarfs_sb = NULL;
+}
+
+static struct file_system_type efivarfs_type = {
+ .name = "efivarfs",
+ .mount = efivarfs_mount,
+ .kill_sb = efivarfs_kill_sb,
+};
+
+static const struct inode_operations efivarfs_dir_inode_operations = {
+ .lookup = simple_lookup,
+ .unlink = efivarfs_unlink,
+ .create = efivarfs_create,
+};
+
+static struct pstore_info efi_pstore_info;
+
#ifdef CONFIG_PSTORE
static int efi_pstore_open(struct pstore_info *psi)
@@ -1065,11 +1545,18 @@ efivar_create_sysfs_entry(struct efivars *efivars,
efi_char16_t *variable_name,
efi_guid_t *vendor_guid)
{
- int i, short_name_size = variable_name_size / sizeof(efi_char16_t) + 38;
+ int i, short_name_size;
char *short_name;
struct efivar_entry *new_efivar;
- short_name = kzalloc(short_name_size + 1, GFP_KERNEL);
+ /*
+ * Length of the variable bytes in ASCII, plus the '-' separator,
+ * plus the GUID, plus trailing NUL
+ */
+ short_name_size = variable_name_size / sizeof(efi_char16_t)
+ + 1 + GUID_LEN + 1;
+
+ short_name = kzalloc(short_name_size, GFP_KERNEL);
new_efivar = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL);
if (!short_name || !new_efivar) {
@@ -1189,6 +1676,7 @@ void unregister_efivars(struct efivars *efivars)
sysfs_remove_bin_file(&efivars->kset->kobj, efivars->del_var);
kfree(efivars->new_var);
kfree(efivars->del_var);
+ kobject_put(efivars->kobject);
kset_unregister(efivars->kset);
}
EXPORT_SYMBOL_GPL(unregister_efivars);
@@ -1220,6 +1708,14 @@ int register_efivars(struct efivars *efivars,
goto out;
}
+ efivars->kobject = kobject_create_and_add("efivars", parent_kobj);
+ if (!efivars->kobject) {
+ pr_err("efivars: Subsystem registration failed.\n");
+ error = -ENOMEM;
+ kset_unregister(efivars->kset);
+ goto out;
+ }
+
/*
* Per EFI spec, the maximum storage allocated for both
* the variable name and variable data is 1024 bytes.
@@ -1262,6 +1758,8 @@ int register_efivars(struct efivars *efivars,
pstore_register(&efivars->efi_pstore_info);
}
+ register_filesystem(&efivarfs_type);
+
out:
kfree(variable_name);
@@ -1269,9 +1767,6 @@ out:
}
EXPORT_SYMBOL_GPL(register_efivars);
-static struct efivars __efivars;
-static struct efivar_operations ops;
-
/*
* For now we register the efi subsystem with the firmware subsystem
* and the vars subsystem with the efi subsystem. In the future, it
@@ -1302,6 +1797,7 @@ efivars_init(void)
ops.set_variable = efi.set_variable;
ops.get_next_variable = efi.get_next_variable;
ops.query_variable_info = efi.query_variable_info;
+
error = register_efivars(&__efivars, &ops, efi_kobj);
if (error)
goto err_put;
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index a5cbeec6f6d..bf892bd68c1 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -504,7 +504,7 @@ config GPIO_ADNP
help
This option enables support for N GPIOs found on Avionic Design
I2C GPIO expanders. The register space will be extended by powers
- of two, so the controller will need to accomodate for that. For
+ of two, so the controller will need to accommodate for that. For
example: if a controller provides 48 pins, 6 registers will be
enough to represent all pins, but the driver will assume a
register layout for 64 pins (8 registers).
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index 219942c660d..5d1d21a6dcd 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -1650,7 +1650,7 @@ static int evergreen_cp_resume(struct radeon_device *rdev)
ring->wptr = 0;
WREG32(CP_RB_WPTR, ring->wptr);
- /* set the wb address wether it's enabled or not */
+ /* set the wb address whether it's enabled or not */
WREG32(CP_RB_RPTR_ADDR,
((rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFFFFFFFC));
WREG32(CP_RB_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFF);
diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c
index 81e6a568c29..cda01f808f1 100644
--- a/drivers/gpu/drm/radeon/ni.c
+++ b/drivers/gpu/drm/radeon/ni.c
@@ -1059,7 +1059,7 @@ static int cayman_cp_resume(struct radeon_device *rdev)
WREG32(CP_DEBUG, (1 << 27));
- /* set the wb address wether it's enabled or not */
+ /* set the wb address whether it's enabled or not */
WREG32(SCRATCH_ADDR, ((rdev->wb.gpu_addr + RADEON_WB_SCRATCH_OFFSET) >> 8) & 0xFFFFFFFF);
WREG32(SCRATCH_UMSK, 0xff);
@@ -1076,7 +1076,7 @@ static int cayman_cp_resume(struct radeon_device *rdev)
#endif
WREG32(cp_rb_cntl[i], rb_cntl);
- /* set the wb address wether it's enabled or not */
+ /* set the wb address whether it's enabled or not */
addr = rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET;
WREG32(cp_rb_rptr_addr[i], addr & 0xFFFFFFFC);
WREG32(cp_rb_rptr_addr_hi[i], upper_32_bits(addr) & 0xFF);
diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c
index 47634f27f2e..ebd69562ef6 100644
--- a/drivers/gpu/drm/radeon/radeon_ring.c
+++ b/drivers/gpu/drm/radeon/radeon_ring.c
@@ -459,7 +459,7 @@ void radeon_ring_unlock_commit(struct radeon_device *rdev, struct radeon_ring *r
*
* @ring: radeon_ring structure holding ring information
*
- * Reset the driver's copy of the wtpr (all asics).
+ * Reset the driver's copy of the wptr (all asics).
*/
void radeon_ring_undo(struct radeon_ring *ring)
{
@@ -503,7 +503,7 @@ void radeon_ring_force_activity(struct radeon_device *rdev, struct radeon_ring *
}
/**
- * radeon_ring_force_activity - update lockup variables
+ * radeon_ring_lockup_update - update lockup variables
*
* @ring: radeon_ring structure holding ring information
*
diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c
index 4422d630b33..010156dd949 100644
--- a/drivers/gpu/drm/radeon/si.c
+++ b/drivers/gpu/drm/radeon/si.c
@@ -2007,7 +2007,7 @@ static int si_cp_resume(struct radeon_device *rdev)
ring->wptr = 0;
WREG32(CP_RB0_WPTR, ring->wptr);
- /* set the wb address wether it's enabled or not */
+ /* set the wb address whether it's enabled or not */
WREG32(CP_RB0_RPTR_ADDR, (rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFFFFFFFC);
WREG32(CP_RB0_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFF);
@@ -2040,7 +2040,7 @@ static int si_cp_resume(struct radeon_device *rdev)
ring->wptr = 0;
WREG32(CP_RB1_WPTR, ring->wptr);
- /* set the wb address wether it's enabled or not */
+ /* set the wb address whether it's enabled or not */
WREG32(CP_RB1_RPTR_ADDR, (rdev->wb.gpu_addr + RADEON_WB_CP1_RPTR_OFFSET) & 0xFFFFFFFC);
WREG32(CP_RB1_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP1_RPTR_OFFSET) & 0xFF);
@@ -2066,7 +2066,7 @@ static int si_cp_resume(struct radeon_device *rdev)
ring->wptr = 0;
WREG32(CP_RB2_WPTR, ring->wptr);
- /* set the wb address wether it's enabled or not */
+ /* set the wb address whether it's enabled or not */
WREG32(CP_RB2_RPTR_ADDR, (rdev->wb.gpu_addr + RADEON_WB_CP2_RPTR_OFFSET) & 0xFFFFFFFC);
WREG32(CP_RB2_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP2_RPTR_OFFSET) & 0xFF);
diff --git a/drivers/gpu/vga/Kconfig b/drivers/gpu/vga/Kconfig
index f34838839b0..29437eabe09 100644
--- a/drivers/gpu/vga/Kconfig
+++ b/drivers/gpu/vga/Kconfig
@@ -1,7 +1,7 @@
config VGA_ARB
bool "VGA Arbitration" if EXPERT
default y
- depends on PCI
+ depends on (PCI && !S390)
help
Some "legacy" VGA devices implemented on PCI typically have the same
hard-decoded addresses as they did on ISA. When multiple PCI devices
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 1630150ad2b..e7d6a13ec6a 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -265,6 +265,15 @@ config HID_GYRATION
---help---
Support for Gyration remote control.
+config HID_ICADE
+ tristate "ION iCade arcade controller"
+ depends on BT_HIDP
+ ---help---
+ Support for the ION iCade arcade controller to work as a joystick.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hid-icade.
+
config HID_TWINHAN
tristate "Twinhan IR remote control"
depends on USB_HID
@@ -728,4 +737,6 @@ endif # HID
source "drivers/hid/usbhid/Kconfig"
+source "drivers/hid/i2c-hid/Kconfig"
+
endmenu
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index cef68ca859d..b62215716b2 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtekff.o
obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o
+obj-$(CONFIG_HID_ICADE) += hid-icade.o
obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o
obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o
obj-$(CONFIG_HID_KYE) += hid-kye.o
@@ -93,8 +94,8 @@ obj-$(CONFIG_HID_PRIMAX) += hid-primax.o
obj-$(CONFIG_HID_PS3REMOTE) += hid-ps3remote.o
obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \
hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \
- hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-pyra.o \
- hid-roccat-savu.o
+ hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-lua.o \
+ hid-roccat-pyra.o hid-roccat-savu.o
obj-$(CONFIG_HID_SAITEK) += hid-saitek.o
obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o
obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o
@@ -118,3 +119,4 @@ obj-$(CONFIG_USB_HID) += usbhid/
obj-$(CONFIG_USB_MOUSE) += usbhid/
obj-$(CONFIG_USB_KBD) += usbhid/
+obj-$(CONFIG_I2C_HID) += i2c-hid/
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index fd7722aecf7..d0f7662aacc 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -439,7 +439,8 @@ static const struct hid_device_id apple_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO),
- .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
+ .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+ APPLE_ISO_KEYBOARD },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
APPLE_RDESC_JIS },
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index f4109fd657f..eb2ee11b641 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -92,6 +92,7 @@ EXPORT_SYMBOL_GPL(hid_register_report);
static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values)
{
struct hid_field *field;
+ int i;
if (report->maxfield == HID_MAX_FIELDS) {
hid_err(report->device, "too many fields in report\n");
@@ -110,6 +111,9 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned
field->value = (s32 *)(field->usage + usages);
field->report = report;
+ for (i = 0; i < usages; i++)
+ field->usage[i].usage_index = i;
+
return field;
}
@@ -315,6 +319,7 @@ static s32 item_sdata(struct hid_item *item)
static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
{
+ __u32 raw_value;
switch (item->tag) {
case HID_GLOBAL_ITEM_TAG_PUSH:
@@ -365,7 +370,14 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
return 0;
case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:
- parser->global.unit_exponent = item_sdata(item);
+ /* Units exponent negative numbers are given through a
+ * two's complement.
+ * See "6.2.2.7 Global Items" for more information. */
+ raw_value = item_udata(item);
+ if (!(raw_value & 0xfffffff0))
+ parser->global.unit_exponent = hid_snto32(raw_value, 4);
+ else
+ parser->global.unit_exponent = raw_value;
return 0;
case HID_GLOBAL_ITEM_TAG_UNIT:
@@ -713,7 +725,12 @@ static int hid_scan_report(struct hid_device *hid)
hid_scan_usage(hid, u);
break;
}
- }
+ } else if (page == HID_UP_SENSOR &&
+ item.type == HID_ITEM_TYPE_MAIN &&
+ item.tag == HID_MAIN_ITEM_TAG_BEGIN_COLLECTION &&
+ (item_udata(&item) & 0xff) == HID_COLLECTION_PHYSICAL &&
+ hid->bus == BUS_USB)
+ hid->group = HID_GROUP_SENSOR_HUB;
}
return 0;
@@ -865,6 +882,12 @@ static s32 snto32(__u32 value, unsigned n)
return value & (1 << (n - 1)) ? value | (-1 << n) : value;
}
+s32 hid_snto32(__u32 value, unsigned n)
+{
+ return snto32(value, n);
+}
+EXPORT_SYMBOL_GPL(hid_snto32);
+
/*
* Convert a signed 32-bit integer to a signed n-bit integer.
*/
@@ -1465,6 +1488,10 @@ EXPORT_SYMBOL_GPL(hid_disconnect);
* there is a proper autodetection and autoloading in place (based on presence
* of HID_DG_CONTACTID), so those devices don't need to be added to this list,
* as we are doing the right thing in hid_scan_usage().
+ *
+ * Autodetection for (USB) HID sensor hubs exists too. If a collection of type
+ * physical is found inside a usage page of type sensor, hid-sensor-hub will be
+ * used as a driver. See hid_scan_report().
*/
static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
@@ -1538,6 +1565,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
@@ -1571,10 +1599,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) },
- { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, USB_DEVICE_ID_SENSOR_HUB_1020) },
- { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, USB_DEVICE_ID_SENSOR_HUB_09FA) },
- { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, USB_DEVICE_ID_SENSOR_HUB_1020) },
- { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, USB_DEVICE_ID_SENSOR_HUB_09FA) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
@@ -1658,6 +1683,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_LUA) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) },
@@ -1672,7 +1698,6 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
- { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, USB_DEVICE_ID_SENSOR_HUB_7014) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323) },
@@ -2150,8 +2175,13 @@ static const struct hid_device_id hid_mouse_ignore_list[] = {
{ }
};
-static bool hid_ignore(struct hid_device *hdev)
+bool hid_ignore(struct hid_device *hdev)
{
+ if (hdev->quirks & HID_QUIRK_NO_IGNORE)
+ return false;
+ if (hdev->quirks & HID_QUIRK_IGNORE)
+ return true;
+
switch (hdev->vendor) {
case USB_VENDOR_ID_CODEMERCS:
/* ignore all Code Mercenaries IOWarrior devices */
@@ -2188,7 +2218,16 @@ static bool hid_ignore(struct hid_device *hdev)
if (hdev->product == USB_DEVICE_ID_JESS_YUREX &&
hdev->type == HID_TYPE_USBNONE)
return true;
- break;
+ break;
+ case USB_VENDOR_ID_DWAV:
+ /* These are handled by usbtouchscreen. hdev->type is probably
+ * HID_TYPE_USBNONE, but we say !HID_TYPE_USBMOUSE to match
+ * usbtouchscreen. */
+ if ((hdev->product == USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER ||
+ hdev->product == USB_DEVICE_ID_DWAV_TOUCHCONTROLLER) &&
+ hdev->type != HID_TYPE_USBMOUSE)
+ return true;
+ break;
}
if (hdev->type == HID_TYPE_USBMOUSE &&
@@ -2197,6 +2236,7 @@ static bool hid_ignore(struct hid_device *hdev)
return !!hid_match_id(hdev, hid_ignore_list);
}
+EXPORT_SYMBOL_GPL(hid_ignore);
int hid_add_device(struct hid_device *hdev)
{
@@ -2208,8 +2248,7 @@ int hid_add_device(struct hid_device *hdev)
/* we need to kill them here, otherwise they will stay allocated to
* wait for coming driver */
- if (!(hdev->quirks & HID_QUIRK_NO_IGNORE)
- && (hid_ignore(hdev) || (hdev->quirks & HID_QUIRK_IGNORE)))
+ if (hid_ignore(hdev))
return -ENODEV;
/*
diff --git a/drivers/hid/hid-icade.c b/drivers/hid/hid-icade.c
new file mode 100644
index 00000000000..1d6565e37ba
--- /dev/null
+++ b/drivers/hid/hid-icade.c
@@ -0,0 +1,259 @@
+/*
+ * ION iCade input driver
+ *
+ * Copyright (c) 2012 Bastien Nocera <hadess@hadess.net>
+ * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+/*
+ * ↑ A C Y L
+ * ← →
+ * ↓ B X Z R
+ *
+ *
+ * UP ON,OFF = w,e
+ * RT ON,OFF = d,c
+ * DN ON,OFF = x,z
+ * LT ON,OFF = a,q
+ * A ON,OFF = y,t
+ * B ON,OFF = h,r
+ * C ON,OFF = u,f
+ * X ON,OFF = j,n
+ * Y ON,OFF = i,m
+ * Z ON,OFF = k,p
+ * L ON,OFF = o,g
+ * R ON,OFF = l,v
+ */
+
+/* The translation code uses HID usage instead of input layer
+ * keys. This code generates a lookup table that makes
+ * translation quick.
+ *
+ * #include <linux/input.h>
+ * #include <stdio.h>
+ * #include <assert.h>
+ *
+ * #define unk KEY_UNKNOWN
+ *
+ * < copy of hid_keyboard[] from hid-input.c >
+ *
+ * struct icade_key_translation {
+ * int from;
+ * const char *to;
+ * int press;
+ * };
+ *
+ * static const struct icade_key_translation icade_keys[] = {
+ * { KEY_W, "KEY_UP", 1 },
+ * { KEY_E, "KEY_UP", 0 },
+ * { KEY_D, "KEY_RIGHT", 1 },
+ * { KEY_C, "KEY_RIGHT", 0 },
+ * { KEY_X, "KEY_DOWN", 1 },
+ * { KEY_Z, "KEY_DOWN", 0 },
+ * { KEY_A, "KEY_LEFT", 1 },
+ * { KEY_Q, "KEY_LEFT", 0 },
+ * { KEY_Y, "BTN_A", 1 },
+ * { KEY_T, "BTN_A", 0 },
+ * { KEY_H, "BTN_B", 1 },
+ * { KEY_R, "BTN_B", 0 },
+ * { KEY_U, "BTN_C", 1 },
+ * { KEY_F, "BTN_C", 0 },
+ * { KEY_J, "BTN_X", 1 },
+ * { KEY_N, "BTN_X", 0 },
+ * { KEY_I, "BTN_Y", 1 },
+ * { KEY_M, "BTN_Y", 0 },
+ * { KEY_K, "BTN_Z", 1 },
+ * { KEY_P, "BTN_Z", 0 },
+ * { KEY_O, "BTN_THUMBL", 1 },
+ * { KEY_G, "BTN_THUMBL", 0 },
+ * { KEY_L, "BTN_THUMBR", 1 },
+ * { KEY_V, "BTN_THUMBR", 0 },
+ *
+ * { }
+ * };
+ *
+ * static int
+ * usage_for_key (int key)
+ * {
+ * int i;
+ * for (i = 0; i < 256; i++) {
+ * if (hid_keyboard[i] == key)
+ * return i;
+ * }
+ * assert(0);
+ * }
+ *
+ * int main (int argc, char **argv)
+ * {
+ * const struct icade_key_translation *trans;
+ * int max_usage = 0;
+ *
+ * for (trans = icade_keys; trans->from; trans++) {
+ * int usage = usage_for_key (trans->from);
+ * max_usage = usage > max_usage ? usage : max_usage;
+ * }
+ *
+ * printf ("#define ICADE_MAX_USAGE %d\n\n", max_usage);
+ * printf ("struct icade_key {\n");
+ * printf ("\tu16 to;\n");
+ * printf ("\tu8 press:1;\n");
+ * printf ("};\n\n");
+ * printf ("static const struct icade_key "
+ * "icade_usage_table[%d] = {\n", max_usage + 1);
+ * for (trans = icade_keys; trans->from; trans++) {
+ * printf ("\t[%d] = { %s, %d },\n",
+ * usage_for_key (trans->from), trans->to, trans->press);
+ * }
+ * printf ("};\n");
+ *
+ * return 0;
+ * }
+ */
+
+#define ICADE_MAX_USAGE 29
+
+struct icade_key {
+ u16 to;
+ u8 press:1;
+};
+
+static const struct icade_key icade_usage_table[30] = {
+ [26] = { KEY_UP, 1 },
+ [8] = { KEY_UP, 0 },
+ [7] = { KEY_RIGHT, 1 },
+ [6] = { KEY_RIGHT, 0 },
+ [27] = { KEY_DOWN, 1 },
+ [29] = { KEY_DOWN, 0 },
+ [4] = { KEY_LEFT, 1 },
+ [20] = { KEY_LEFT, 0 },
+ [28] = { BTN_A, 1 },
+ [23] = { BTN_A, 0 },
+ [11] = { BTN_B, 1 },
+ [21] = { BTN_B, 0 },
+ [24] = { BTN_C, 1 },
+ [9] = { BTN_C, 0 },
+ [13] = { BTN_X, 1 },
+ [17] = { BTN_X, 0 },
+ [12] = { BTN_Y, 1 },
+ [16] = { BTN_Y, 0 },
+ [14] = { BTN_Z, 1 },
+ [19] = { BTN_Z, 0 },
+ [18] = { BTN_THUMBL, 1 },
+ [10] = { BTN_THUMBL, 0 },
+ [15] = { BTN_THUMBR, 1 },
+ [25] = { BTN_THUMBR, 0 },
+};
+
+static const struct icade_key *icade_find_translation(u16 from)
+{
+ if (from < 0 || from > ICADE_MAX_USAGE)
+ return NULL;
+ return &icade_usage_table[from];
+}
+
+static int icade_event(struct hid_device *hdev, struct hid_field *field,
+ struct hid_usage *usage, __s32 value)
+{
+ const struct icade_key *trans;
+
+ if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
+ !usage->type)
+ return 0;
+
+ /* We ignore the fake key up, and act only on key down */
+ if (!value)
+ return 1;
+
+ trans = icade_find_translation(usage->hid & HID_USAGE);
+
+ if (!trans)
+ return 1;
+
+ input_event(field->hidinput->input, usage->type,
+ trans->to, trans->press);
+
+ return 1;
+}
+
+static int icade_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ const struct icade_key *trans;
+
+ if ((usage->hid & HID_USAGE_PAGE) == HID_UP_KEYBOARD) {
+ trans = icade_find_translation(usage->hid & HID_USAGE);
+
+ if (!trans)
+ return -1;
+
+ hid_map_usage(hi, usage, bit, max, EV_KEY, trans->to);
+ set_bit(trans->to, hi->input->keybit);
+
+ return 1;
+ }
+
+ /* ignore others */
+ return -1;
+
+}
+
+static int icade_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ if (usage->type == EV_KEY)
+ set_bit(usage->type, hi->input->evbit);
+
+ return -1;
+}
+
+static const struct hid_device_id icade_devices[] = {
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) },
+
+ { }
+};
+MODULE_DEVICE_TABLE(hid, icade_devices);
+
+static struct hid_driver icade_driver = {
+ .name = "icade",
+ .id_table = icade_devices,
+ .event = icade_event,
+ .input_mapped = icade_input_mapped,
+ .input_mapping = icade_input_mapping,
+};
+
+static int __init icade_init(void)
+{
+ int ret;
+
+ ret = hid_register_driver(&icade_driver);
+ if (ret)
+ pr_err("can't register icade driver\n");
+
+ return ret;
+}
+
+static void __exit icade_exit(void)
+{
+ hid_unregister_driver(&icade_driver);
+}
+
+module_init(icade_init);
+module_exit(icade_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
+MODULE_DESCRIPTION("ION iCade input driver");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 9d7a42857ea..4dfa605e2d1 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -257,6 +257,7 @@
#define USB_VENDOR_ID_DWAV 0x0eef
#define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001
+#define USB_DEVICE_ID_DWAV_TOUCHCONTROLLER 0x0002
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D 0x480d
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E 0x480e
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7207 0x7207
@@ -423,6 +424,9 @@
#define USB_VENDOR_ID_ILITEK 0x222a
#define USB_DEVICE_ID_ILITEK_MULTITOUCH 0x0001
+#define USB_VENDOR_ID_ION 0x15e4
+#define USB_DEVICE_ID_ICADE 0x0132
+
#define USB_VENDOR_ID_HOLTEK 0x1241
#define USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP 0x5015
@@ -432,11 +436,6 @@
#define USB_VENDOR_ID_IMATION 0x0718
#define USB_DEVICE_ID_DISC_STAKKA 0xd000
-#define USB_VENDOR_ID_INTEL_8086 0x8086
-#define USB_VENDOR_ID_INTEL_8087 0x8087
-#define USB_DEVICE_ID_SENSOR_HUB_1020 0x1020
-#define USB_DEVICE_ID_SENSOR_HUB_09FA 0x09FA
-
#define USB_VENDOR_ID_IRTOUCHSYSTEMS 0x6615
#define USB_DEVICE_ID_IRTOUCH_INFRARED_USB 0x0070
@@ -603,6 +602,7 @@
#define USB_VENDOR_ID_NOVATEK 0x0603
#define USB_DEVICE_ID_NOVATEK_PCT 0x0600
+#define USB_DEVICE_ID_NOVATEK_MOUSE 0x1602
#define USB_VENDOR_ID_NTRIG 0x1b96
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN 0x0001
@@ -677,7 +677,9 @@
#define USB_DEVICE_ID_ROCCAT_ISKU 0x319c
#define USB_DEVICE_ID_ROCCAT_KONE 0x2ced
#define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51
+#define USB_DEVICE_ID_ROCCAT_KONEXTD 0x2e22
#define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50
+#define USB_DEVICE_ID_ROCCAT_LUA 0x2c2e
#define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24
#define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6
#define USB_DEVICE_ID_ROCCAT_SAVU 0x2d5a
@@ -696,6 +698,9 @@
#define USB_VENDOR_ID_SIGMA_MICRO 0x1c4f
#define USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD 0x0002
+#define USB_VENDOR_ID_SIGMATEL 0x066F
+#define USB_DEVICE_ID_SIGMATEL_STMP3780 0x3780
+
#define USB_VENDOR_ID_SKYCABLE 0x1223
#define USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER 0x3F07
@@ -714,7 +719,6 @@
#define USB_VENDOR_ID_STANTUM_STM 0x0483
#define USB_DEVICE_ID_MTP_STM 0x3261
-#define USB_DEVICE_ID_SENSOR_HUB_7014 0x7014
#define USB_VENDOR_ID_STANTUM_SITRONIX 0x1403
#define USB_DEVICE_ID_MTP_SITRONIX 0x5001
@@ -762,6 +766,9 @@
#define USB_VENDOR_ID_TOUCHPACK 0x1bfd
#define USB_DEVICE_ID_TOUCHPACK_RTS 0x1688
+#define USB_VENDOR_ID_TPV 0x25aa
+#define USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN 0x8883
+
#define USB_VENDOR_ID_TURBOX 0x062a
#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201
#define USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART 0x7100
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index d917c0d5368..21b196c394b 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -192,7 +192,6 @@ static int hidinput_setkeycode(struct input_dev *dev,
return -EINVAL;
}
-
/**
* hidinput_calc_abs_res - calculate an absolute axis resolution
* @field: the HID report field to calculate resolution for
@@ -208,7 +207,7 @@ static int hidinput_setkeycode(struct input_dev *dev,
* Only exponent 1 length units are processed. Centimeters and inches are
* converted to millimeters. Degrees are converted to radians.
*/
-static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
+__s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
{
__s32 unit_exponent = field->unit_exponent;
__s32 logical_extents = field->logical_maximum -
@@ -229,17 +228,29 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
case ABS_X:
case ABS_Y:
case ABS_Z:
- if (field->unit == 0x11) { /* If centimeters */
+ case ABS_MT_POSITION_X:
+ case ABS_MT_POSITION_Y:
+ case ABS_MT_TOOL_X:
+ case ABS_MT_TOOL_Y:
+ case ABS_MT_TOUCH_MAJOR:
+ case ABS_MT_TOUCH_MINOR:
+ if (field->unit & 0xffffff00) /* Not a length */
+ return 0;
+ unit_exponent += hid_snto32(field->unit >> 4, 4) - 1;
+ switch (field->unit & 0xf) {
+ case 0x1: /* If centimeters */
/* Convert to millimeters */
unit_exponent += 1;
- } else if (field->unit == 0x13) { /* If inches */
+ break;
+ case 0x3: /* If inches */
/* Convert to millimeters */
prev = physical_extents;
physical_extents *= 254;
if (physical_extents < prev)
return 0;
unit_exponent -= 1;
- } else {
+ break;
+ default:
return 0;
}
break;
@@ -281,8 +292,9 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
}
/* Calculate resolution */
- return logical_extents / physical_extents;
+ return DIV_ROUND_CLOSEST(logical_extents, physical_extents);
}
+EXPORT_SYMBOL_GPL(hidinput_calc_abs_res);
#ifdef CONFIG_HID_BATTERY_STRENGTH
static enum power_supply_property hidinput_battery_props[] = {
@@ -299,6 +311,9 @@ static enum power_supply_property hidinput_battery_props[] = {
static const struct hid_device_id hid_battery_quirks[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
+ USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO),
+ HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI),
HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
@@ -502,9 +517,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
if (code <= 0xf)
code += BTN_JOYSTICK;
else
- code += BTN_TRIGGER_HAPPY;
+ code += BTN_TRIGGER_HAPPY - 0x10;
+ break;
+ case HID_GD_GAMEPAD:
+ if (code <= 0xf)
+ code += BTN_GAMEPAD;
+ else
+ code += BTN_TRIGGER_HAPPY - 0x10;
break;
- case HID_GD_GAMEPAD: code += BTN_GAMEPAD; break;
default:
switch (field->physical) {
case HID_GD_MOUSE:
@@ -1146,6 +1166,38 @@ static void report_features(struct hid_device *hid)
}
}
+static struct hid_input *hidinput_allocate(struct hid_device *hid)
+{
+ struct hid_input *hidinput = kzalloc(sizeof(*hidinput), GFP_KERNEL);
+ struct input_dev *input_dev = input_allocate_device();
+ if (!hidinput || !input_dev) {
+ kfree(hidinput);
+ input_free_device(input_dev);
+ hid_err(hid, "Out of memory during hid input probe\n");
+ return NULL;
+ }
+
+ input_set_drvdata(input_dev, hid);
+ input_dev->event = hid->ll_driver->hidinput_input_event;
+ input_dev->open = hidinput_open;
+ input_dev->close = hidinput_close;
+ input_dev->setkeycode = hidinput_setkeycode;
+ input_dev->getkeycode = hidinput_getkeycode;
+
+ input_dev->name = hid->name;
+ input_dev->phys = hid->phys;
+ input_dev->uniq = hid->uniq;
+ input_dev->id.bustype = hid->bus;
+ input_dev->id.vendor = hid->vendor;
+ input_dev->id.product = hid->product;
+ input_dev->id.version = hid->version;
+ input_dev->dev.parent = hid->dev.parent;
+ hidinput->input = input_dev;
+ list_add_tail(&hidinput->list, &hid->inputs);
+
+ return hidinput;
+}
+
/*
* Register the input device; print a message.
* Configure the input layer interface
@@ -1157,7 +1209,6 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
struct hid_driver *drv = hid->driver;
struct hid_report *report;
struct hid_input *hidinput = NULL;
- struct input_dev *input_dev;
int i, j, k;
INIT_LIST_HEAD(&hid->inputs);
@@ -1188,33 +1239,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
continue;
if (!hidinput) {
- hidinput = kzalloc(sizeof(*hidinput), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!hidinput || !input_dev) {
- kfree(hidinput);
- input_free_device(input_dev);
- hid_err(hid, "Out of memory during hid input probe\n");
+ hidinput = hidinput_allocate(hid);
+ if (!hidinput)
goto out_unwind;
- }
-
- input_set_drvdata(input_dev, hid);
- input_dev->event =
- hid->ll_driver->hidinput_input_event;
- input_dev->open = hidinput_open;
- input_dev->close = hidinput_close;
- input_dev->setkeycode = hidinput_setkeycode;
- input_dev->getkeycode = hidinput_getkeycode;
-
- input_dev->name = hid->name;
- input_dev->phys = hid->phys;
- input_dev->uniq = hid->uniq;
- input_dev->id.bustype = hid->bus;
- input_dev->id.vendor = hid->vendor;
- input_dev->id.product = hid->product;
- input_dev->id.version = hid->version;
- input_dev->dev.parent = hid->dev.parent;
- hidinput->input = input_dev;
- list_add_tail(&hidinput->list, &hid->inputs);
}
for (i = 0; i < report->maxfield; i++)
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 7867d69f0ef..61543c02ea0 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -52,11 +52,14 @@ MODULE_LICENSE("GPL");
#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 6)
#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 8)
#define MT_QUIRK_NO_AREA (1 << 9)
+#define MT_QUIRK_IGNORE_DUPLICATES (1 << 10)
+#define MT_QUIRK_HOVERING (1 << 11)
struct mt_slot {
- __s32 x, y, p, w, h;
+ __s32 x, y, cx, cy, p, w, h;
__s32 contactid; /* the device ContactID assigned to this slot */
bool touch_state; /* is the touch valid? */
+ bool inrange_state; /* is the finger in proximity of the sensor? */
};
struct mt_class {
@@ -121,6 +124,7 @@ struct mt_device {
#define MT_CLS_GENERALTOUCH_PWT_TENFINGERS 0x0109
#define MT_DEFAULT_MAXCONTACT 10
+#define MT_MAX_MAXCONTACT 250
#define MT_USB_DEVICE(v, p) HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH, v, p)
#define MT_BT_DEVICE(v, p) HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_MULTITOUCH, v, p)
@@ -282,11 +286,26 @@ static void mt_feature_mapping(struct hid_device *hdev,
case HID_DG_CONTACTMAX:
td->maxcontact_report_id = field->report->id;
td->maxcontacts = field->value[0];
+ if (!td->maxcontacts &&
+ field->logical_maximum <= MT_MAX_MAXCONTACT)
+ td->maxcontacts = field->logical_maximum;
if (td->mtclass.maxcontacts)
/* check if the maxcontacts is given by the class */
td->maxcontacts = td->mtclass.maxcontacts;
break;
+ case 0xff0000c5:
+ if (field->report_count == 256 && field->report_size == 8) {
+ /* Win 8 devices need special quirks */
+ __s32 *quirks = &td->mtclass.quirks;
+ *quirks |= MT_QUIRK_ALWAYS_VALID;
+ *quirks |= MT_QUIRK_IGNORE_DUPLICATES;
+ *quirks |= MT_QUIRK_HOVERING;
+ *quirks &= ~MT_QUIRK_NOT_SEEN_MEANS_UP;
+ *quirks &= ~MT_QUIRK_VALID_IS_INRANGE;
+ *quirks &= ~MT_QUIRK_VALID_IS_CONFIDENCE;
+ }
+ break;
}
}
@@ -297,6 +316,7 @@ static void set_abs(struct input_dev *input, unsigned int code,
int fmax = field->logical_maximum;
int fuzz = snratio ? (fmax - fmin) / snratio : 0;
input_set_abs_params(input, code, fmin, fmax, fuzz, 0);
+ input_abs_set_res(input, code, hidinput_calc_abs_res(field, code));
}
static void mt_store_field(struct hid_usage *usage, struct mt_device *td,
@@ -317,6 +337,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct mt_device *td = hid_get_drvdata(hdev);
struct mt_class *cls = &td->mtclass;
int code;
+ struct hid_usage *prev_usage = NULL;
/* Only map fields from TouchScreen or TouchPad collections.
* We need to ignore fields that belong to other collections
@@ -339,23 +360,42 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
if (field->physical == HID_DG_STYLUS)
return -1;
+ if (usage->usage_index)
+ prev_usage = &field->usage[usage->usage_index - 1];
+
switch (usage->hid & HID_USAGE_PAGE) {
case HID_UP_GENDESK:
switch (usage->hid) {
case HID_GD_X:
- hid_map_usage(hi, usage, bit, max,
+ if (prev_usage && (prev_usage->hid == usage->hid)) {
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_TOOL_X);
+ set_abs(hi->input, ABS_MT_TOOL_X, field,
+ cls->sn_move);
+ } else {
+ hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_POSITION_X);
- set_abs(hi->input, ABS_MT_POSITION_X, field,
- cls->sn_move);
+ set_abs(hi->input, ABS_MT_POSITION_X, field,
+ cls->sn_move);
+ }
+
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
return 1;
case HID_GD_Y:
- hid_map_usage(hi, usage, bit, max,
+ if (prev_usage && (prev_usage->hid == usage->hid)) {
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_TOOL_Y);
+ set_abs(hi->input, ABS_MT_TOOL_Y, field,
+ cls->sn_move);
+ } else {
+ hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_POSITION_Y);
- set_abs(hi->input, ABS_MT_POSITION_Y, field,
- cls->sn_move);
+ set_abs(hi->input, ABS_MT_POSITION_Y, field,
+ cls->sn_move);
+ }
+
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
return 1;
@@ -365,6 +405,12 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
case HID_UP_DIGITIZER:
switch (usage->hid) {
case HID_DG_INRANGE:
+ if (cls->quirks & MT_QUIRK_HOVERING) {
+ hid_map_usage(hi, usage, bit, max,
+ EV_ABS, ABS_MT_DISTANCE);
+ input_set_abs_params(hi->input,
+ ABS_MT_DISTANCE, 0, 1, 0, 0);
+ }
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
return 1;
@@ -477,18 +523,26 @@ static int mt_compute_slot(struct mt_device *td, struct input_dev *input)
*/
static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
{
- if (td->curvalid) {
+ if (td->curvalid || (td->mtclass.quirks & MT_QUIRK_ALWAYS_VALID)) {
int slotnum = mt_compute_slot(td, input);
struct mt_slot *s = &td->curdata;
+ struct input_mt *mt = input->mt;
if (slotnum < 0 || slotnum >= td->maxcontacts)
return;
+ if ((td->mtclass.quirks & MT_QUIRK_IGNORE_DUPLICATES) && mt) {
+ struct input_mt_slot *slot = &mt->slots[slotnum];
+ if (input_mt_is_active(slot) &&
+ input_mt_is_used(mt, slot))
+ return;
+ }
+
input_mt_slot(input, slotnum);
input_mt_report_slot_state(input, MT_TOOL_FINGER,
- s->touch_state);
- if (s->touch_state) {
- /* this finger is on the screen */
+ s->touch_state || s->inrange_state);
+ if (s->touch_state || s->inrange_state) {
+ /* this finger is in proximity of the sensor */
int wide = (s->w > s->h);
/* divided by two to match visual scale of touch */
int major = max(s->w, s->h) >> 1;
@@ -496,6 +550,10 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
input_event(input, EV_ABS, ABS_MT_POSITION_X, s->x);
input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y);
+ input_event(input, EV_ABS, ABS_MT_TOOL_X, s->cx);
+ input_event(input, EV_ABS, ABS_MT_TOOL_Y, s->cy);
+ input_event(input, EV_ABS, ABS_MT_DISTANCE,
+ !s->touch_state);
input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide);
input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p);
input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
@@ -526,10 +584,10 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
if (hid->claimed & HID_CLAIMED_INPUT) {
switch (usage->hid) {
case HID_DG_INRANGE:
- if (quirks & MT_QUIRK_ALWAYS_VALID)
- td->curvalid = true;
- else if (quirks & MT_QUIRK_VALID_IS_INRANGE)
+ if (quirks & MT_QUIRK_VALID_IS_INRANGE)
td->curvalid = value;
+ if (quirks & MT_QUIRK_HOVERING)
+ td->curdata.inrange_state = value;
break;
case HID_DG_TIPSWITCH:
if (quirks & MT_QUIRK_NOT_SEEN_MEANS_UP)
@@ -547,10 +605,16 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
td->curdata.p = value;
break;
case HID_GD_X:
- td->curdata.x = value;
+ if (usage->code == ABS_MT_TOOL_X)
+ td->curdata.cx = value;
+ else
+ td->curdata.x = value;
break;
case HID_GD_Y:
- td->curdata.y = value;
+ if (usage->code == ABS_MT_TOOL_Y)
+ td->curdata.cy = value;
+ else
+ td->curdata.y = value;
break;
case HID_DG_WIDTH:
td->curdata.w = value;
@@ -575,12 +639,15 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
return 0;
}
- if (usage->hid == td->last_slot_field)
- mt_complete_slot(td, field->hidinput->input);
+ if (usage->usage_index + 1 == field->report_count) {
+ /* we only take into account the last report. */
+ if (usage->hid == td->last_slot_field)
+ mt_complete_slot(td, field->hidinput->input);
- if (field->index == td->last_field_index
- && td->num_received >= td->num_expected)
- mt_sync_frame(td, field->hidinput->input);
+ if (field->index == td->last_field_index
+ && td->num_received >= td->num_expected)
+ mt_sync_frame(td, field->hidinput->input);
+ }
}
diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c
index 13ca9191b63..a79e95bb9fb 100644
--- a/drivers/hid/hid-picolcd_cir.c
+++ b/drivers/hid/hid-picolcd_cir.c
@@ -116,7 +116,7 @@ int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)
rdev->priv = data;
rdev->driver_type = RC_DRIVER_IR_RAW;
- rdev->allowed_protos = RC_TYPE_ALL;
+ rdev->allowed_protos = RC_BIT_ALL;
rdev->open = picolcd_cir_open;
rdev->close = picolcd_cir_close;
rdev->input_name = data->hdev->name;
diff --git a/drivers/hid/hid-roccat-isku.c b/drivers/hid/hid-roccat-isku.c
index 5669916c294..1219998a02d 100644
--- a/drivers/hid/hid-roccat-isku.c
+++ b/drivers/hid/hid-roccat-isku.c
@@ -167,7 +167,7 @@ static ssize_t isku_sysfs_write_ ## thingy(struct file *fp, struct kobject *kobj
loff_t off, size_t count) \
{ \
return isku_sysfs_write(fp, kobj, buf, off, count, \
- sizeof(struct isku_ ## thingy), ISKU_COMMAND_ ## THINGY); \
+ ISKU_SIZE_ ## THINGY, ISKU_COMMAND_ ## THINGY); \
}
#define ISKU_SYSFS_R(thingy, THINGY) \
@@ -176,32 +176,32 @@ static ssize_t isku_sysfs_read_ ## thingy(struct file *fp, struct kobject *kobj,
loff_t off, size_t count) \
{ \
return isku_sysfs_read(fp, kobj, buf, off, count, \
- sizeof(struct isku_ ## thingy), ISKU_COMMAND_ ## THINGY); \
+ ISKU_SIZE_ ## THINGY, ISKU_COMMAND_ ## THINGY); \
}
#define ISKU_SYSFS_RW(thingy, THINGY) \
ISKU_SYSFS_R(thingy, THINGY) \
ISKU_SYSFS_W(thingy, THINGY)
-#define ISKU_BIN_ATTR_RW(thingy) \
+#define ISKU_BIN_ATTR_RW(thingy, THINGY) \
{ \
.attr = { .name = #thingy, .mode = 0660 }, \
- .size = sizeof(struct isku_ ## thingy), \
+ .size = ISKU_SIZE_ ## THINGY, \
.read = isku_sysfs_read_ ## thingy, \
.write = isku_sysfs_write_ ## thingy \
}
-#define ISKU_BIN_ATTR_R(thingy) \
+#define ISKU_BIN_ATTR_R(thingy, THINGY) \
{ \
.attr = { .name = #thingy, .mode = 0440 }, \
- .size = sizeof(struct isku_ ## thingy), \
+ .size = ISKU_SIZE_ ## THINGY, \
.read = isku_sysfs_read_ ## thingy, \
}
-#define ISKU_BIN_ATTR_W(thingy) \
+#define ISKU_BIN_ATTR_W(thingy, THINGY) \
{ \
.attr = { .name = #thingy, .mode = 0220 }, \
- .size = sizeof(struct isku_ ## thingy), \
+ .size = ISKU_SIZE_ ## THINGY, \
.write = isku_sysfs_write_ ## thingy \
}
@@ -218,21 +218,23 @@ ISKU_SYSFS_RW(last_set, LAST_SET)
ISKU_SYSFS_W(talk, TALK)
ISKU_SYSFS_R(info, INFO)
ISKU_SYSFS_W(control, CONTROL)
+ISKU_SYSFS_W(reset, RESET)
static struct bin_attribute isku_bin_attributes[] = {
- ISKU_BIN_ATTR_RW(macro),
- ISKU_BIN_ATTR_RW(keys_function),
- ISKU_BIN_ATTR_RW(keys_easyzone),
- ISKU_BIN_ATTR_RW(keys_media),
- ISKU_BIN_ATTR_RW(keys_thumbster),
- ISKU_BIN_ATTR_RW(keys_macro),
- ISKU_BIN_ATTR_RW(keys_capslock),
- ISKU_BIN_ATTR_RW(light),
- ISKU_BIN_ATTR_RW(key_mask),
- ISKU_BIN_ATTR_RW(last_set),
- ISKU_BIN_ATTR_W(talk),
- ISKU_BIN_ATTR_R(info),
- ISKU_BIN_ATTR_W(control),
+ ISKU_BIN_ATTR_RW(macro, MACRO),
+ ISKU_BIN_ATTR_RW(keys_function, KEYS_FUNCTION),
+ ISKU_BIN_ATTR_RW(keys_easyzone, KEYS_EASYZONE),
+ ISKU_BIN_ATTR_RW(keys_media, KEYS_MEDIA),
+ ISKU_BIN_ATTR_RW(keys_thumbster, KEYS_THUMBSTER),
+ ISKU_BIN_ATTR_RW(keys_macro, KEYS_MACRO),
+ ISKU_BIN_ATTR_RW(keys_capslock, KEYS_CAPSLOCK),
+ ISKU_BIN_ATTR_RW(light, LIGHT),
+ ISKU_BIN_ATTR_RW(key_mask, KEY_MASK),
+ ISKU_BIN_ATTR_RW(last_set, LAST_SET),
+ ISKU_BIN_ATTR_W(talk, TALK),
+ ISKU_BIN_ATTR_R(info, INFO),
+ ISKU_BIN_ATTR_W(control, CONTROL),
+ ISKU_BIN_ATTR_W(reset, RESET),
__ATTR_NULL
};
diff --git a/drivers/hid/hid-roccat-isku.h b/drivers/hid/hid-roccat-isku.h
index 605b3ce2163..cf6896c8386 100644
--- a/drivers/hid/hid-roccat-isku.h
+++ b/drivers/hid/hid-roccat-isku.h
@@ -15,76 +15,33 @@
#include <linux/types.h>
enum {
+ ISKU_SIZE_CONTROL = 0x03,
+ ISKU_SIZE_INFO = 0x06,
+ ISKU_SIZE_KEY_MASK = 0x06,
+ ISKU_SIZE_KEYS_FUNCTION = 0x29,
+ ISKU_SIZE_KEYS_EASYZONE = 0x41,
+ ISKU_SIZE_KEYS_MEDIA = 0x1d,
+ ISKU_SIZE_KEYS_THUMBSTER = 0x17,
+ ISKU_SIZE_KEYS_MACRO = 0x23,
+ ISKU_SIZE_KEYS_CAPSLOCK = 0x06,
+ ISKU_SIZE_LAST_SET = 0x14,
+ ISKU_SIZE_LIGHT = 0x0a,
+ ISKU_SIZE_MACRO = 0x823,
+ ISKU_SIZE_RESET = 0x03,
+ ISKU_SIZE_TALK = 0x10,
+};
+
+enum {
ISKU_PROFILE_NUM = 5,
ISKU_USB_INTERFACE_PROTOCOL = 0,
};
-struct isku_control {
- uint8_t command; /* ISKU_COMMAND_CONTROL */
- uint8_t value;
- uint8_t request;
-} __packed;
-
struct isku_actual_profile {
uint8_t command; /* ISKU_COMMAND_ACTUAL_PROFILE */
uint8_t size; /* always 3 */
uint8_t actual_profile;
} __packed;
-struct isku_key_mask {
- uint8_t command; /* ISKU_COMMAND_KEY_MASK */
- uint8_t size; /* 6 */
- uint8_t profile_number; /* 0-4 */
- uint8_t mask;
- uint16_t checksum;
-} __packed;
-
-struct isku_keys_function {
- uint8_t data[0x29];
-} __packed;
-
-struct isku_keys_easyzone {
- uint8_t data[0x41];
-} __packed;
-
-struct isku_keys_media {
- uint8_t data[0x1d];
-} __packed;
-
-struct isku_keys_thumbster {
- uint8_t data[0x17];
-} __packed;
-
-struct isku_keys_macro {
- uint8_t data[0x23];
-} __packed;
-
-struct isku_keys_capslock {
- uint8_t data[0x6];
-} __packed;
-
-struct isku_macro {
- uint8_t data[0x823];
-} __packed;
-
-struct isku_light {
- uint8_t data[0xa];
-} __packed;
-
-struct isku_info {
- uint8_t data[2];
- uint8_t firmware_version;
- uint8_t unknown[3];
-} __packed;
-
-struct isku_talk {
- uint8_t data[0x10];
-} __packed;
-
-struct isku_last_set {
- uint8_t data[0x14];
-} __packed;
-
enum isku_commands {
ISKU_COMMAND_CONTROL = 0x4,
ISKU_COMMAND_ACTUAL_PROFILE = 0x5,
@@ -97,6 +54,7 @@ enum isku_commands {
ISKU_COMMAND_MACRO = 0xe,
ISKU_COMMAND_INFO = 0xf,
ISKU_COMMAND_LIGHT = 0x10,
+ ISKU_COMMAND_RESET = 0x11,
ISKU_COMMAND_KEYS_CAPSLOCK = 0x13,
ISKU_COMMAND_LAST_SET = 0x14,
ISKU_COMMAND_15 = 0x15,
diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c
index f5602fec486..6a48fa3c7da 100644
--- a/drivers/hid/hid-roccat-koneplus.c
+++ b/drivers/hid/hid-roccat-koneplus.c
@@ -14,6 +14,7 @@
/*
* Roccat Kone[+] is an updated/improved version of the Kone with more memory
* and functionality and without the non-standard behaviours the Kone had.
+ * KoneXTD has same capabilities but updated sensor.
*/
#include <linux/device.h>
@@ -55,56 +56,6 @@ static int koneplus_send_control(struct usb_device *usb_dev, uint value,
&control, sizeof(struct roccat_common2_control));
}
-static int koneplus_get_info(struct usb_device *usb_dev,
- struct koneplus_info *buf)
-{
- return roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_INFO,
- buf, sizeof(struct koneplus_info));
-}
-
-static int koneplus_get_profile_settings(struct usb_device *usb_dev,
- struct koneplus_profile_settings *buf, uint number)
-{
- int retval;
-
- retval = koneplus_send_control(usb_dev, number,
- KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS);
- if (retval)
- return retval;
-
- return roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_PROFILE_SETTINGS,
- buf, sizeof(struct koneplus_profile_settings));
-}
-
-static int koneplus_set_profile_settings(struct usb_device *usb_dev,
- struct koneplus_profile_settings const *settings)
-{
- return roccat_common2_send_with_status(usb_dev,
- KONEPLUS_COMMAND_PROFILE_SETTINGS,
- settings, sizeof(struct koneplus_profile_settings));
-}
-
-static int koneplus_get_profile_buttons(struct usb_device *usb_dev,
- struct koneplus_profile_buttons *buf, int number)
-{
- int retval;
-
- retval = koneplus_send_control(usb_dev, number,
- KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS);
- if (retval)
- return retval;
-
- return roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_PROFILE_BUTTONS,
- buf, sizeof(struct koneplus_profile_buttons));
-}
-
-static int koneplus_set_profile_buttons(struct usb_device *usb_dev,
- struct koneplus_profile_buttons const *buttons)
-{
- return roccat_common2_send_with_status(usb_dev,
- KONEPLUS_COMMAND_PROFILE_BUTTONS,
- buttons, sizeof(struct koneplus_profile_buttons));
-}
/* retval is 0-4 on success, < 0 on error */
static int koneplus_get_actual_profile(struct usb_device *usb_dev)
@@ -113,7 +64,7 @@ static int koneplus_get_actual_profile(struct usb_device *usb_dev)
int retval;
retval = roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_ACTUAL_PROFILE,
- &buf, sizeof(struct koneplus_actual_profile));
+ &buf, KONEPLUS_SIZE_ACTUAL_PROFILE);
return retval ? retval : buf.actual_profile;
}
@@ -124,12 +75,12 @@ static int koneplus_set_actual_profile(struct usb_device *usb_dev,
struct koneplus_actual_profile buf;
buf.command = KONEPLUS_COMMAND_ACTUAL_PROFILE;
- buf.size = sizeof(struct koneplus_actual_profile);
+ buf.size = KONEPLUS_SIZE_ACTUAL_PROFILE;
buf.actual_profile = new_profile;
return roccat_common2_send_with_status(usb_dev,
KONEPLUS_COMMAND_ACTUAL_PROFILE,
- &buf, sizeof(struct koneplus_actual_profile));
+ &buf, KONEPLUS_SIZE_ACTUAL_PROFILE);
}
static ssize_t koneplus_sysfs_read(struct file *fp, struct kobject *kobj,
@@ -182,111 +133,77 @@ static ssize_t koneplus_sysfs_write(struct file *fp, struct kobject *kobj,
return real_size;
}
-static ssize_t koneplus_sysfs_write_talk(struct file *fp,
- struct kobject *kobj, struct bin_attribute *attr, char *buf,
- loff_t off, size_t count)
-{
- return koneplus_sysfs_write(fp, kobj, buf, off, count,
- sizeof(struct koneplus_talk), KONEPLUS_COMMAND_TALK);
+#define KONEPLUS_SYSFS_W(thingy, THINGY) \
+static ssize_t koneplus_sysfs_write_ ## thingy(struct file *fp, \
+ struct kobject *kobj, struct bin_attribute *attr, char *buf, \
+ loff_t off, size_t count) \
+{ \
+ return koneplus_sysfs_write(fp, kobj, buf, off, count, \
+ KONEPLUS_SIZE_ ## THINGY, KONEPLUS_COMMAND_ ## THINGY); \
}
-static ssize_t koneplus_sysfs_write_macro(struct file *fp,
- struct kobject *kobj, struct bin_attribute *attr, char *buf,
- loff_t off, size_t count)
-{
- return koneplus_sysfs_write(fp, kobj, buf, off, count,
- sizeof(struct koneplus_macro), KONEPLUS_COMMAND_MACRO);
+#define KONEPLUS_SYSFS_R(thingy, THINGY) \
+static ssize_t koneplus_sysfs_read_ ## thingy(struct file *fp, \
+ struct kobject *kobj, struct bin_attribute *attr, char *buf, \
+ loff_t off, size_t count) \
+{ \
+ return koneplus_sysfs_read(fp, kobj, buf, off, count, \
+ KONEPLUS_SIZE_ ## THINGY, KONEPLUS_COMMAND_ ## THINGY); \
}
-static ssize_t koneplus_sysfs_read_sensor(struct file *fp,
- struct kobject *kobj, struct bin_attribute *attr, char *buf,
- loff_t off, size_t count)
-{
- return koneplus_sysfs_read(fp, kobj, buf, off, count,
- sizeof(struct koneplus_sensor), KONEPLUS_COMMAND_SENSOR);
-}
+#define KONEPLUS_SYSFS_RW(thingy, THINGY) \
+KONEPLUS_SYSFS_W(thingy, THINGY) \
+KONEPLUS_SYSFS_R(thingy, THINGY)
-static ssize_t koneplus_sysfs_write_sensor(struct file *fp,
- struct kobject *kobj, struct bin_attribute *attr, char *buf,
- loff_t off, size_t count)
-{
- return koneplus_sysfs_write(fp, kobj, buf, off, count,
- sizeof(struct koneplus_sensor), KONEPLUS_COMMAND_SENSOR);
+#define KONEPLUS_BIN_ATTRIBUTE_RW(thingy, THINGY) \
+{ \
+ .attr = { .name = #thingy, .mode = 0660 }, \
+ .size = KONEPLUS_SIZE_ ## THINGY, \
+ .read = koneplus_sysfs_read_ ## thingy, \
+ .write = koneplus_sysfs_write_ ## thingy \
}
-static ssize_t koneplus_sysfs_write_tcu(struct file *fp,
- struct kobject *kobj, struct bin_attribute *attr, char *buf,
- loff_t off, size_t count)
-{
- return koneplus_sysfs_write(fp, kobj, buf, off, count,
- sizeof(struct koneplus_tcu), KONEPLUS_COMMAND_TCU);
+#define KONEPLUS_BIN_ATTRIBUTE_R(thingy, THINGY) \
+{ \
+ .attr = { .name = #thingy, .mode = 0440 }, \
+ .size = KONEPLUS_SIZE_ ## THINGY, \
+ .read = koneplus_sysfs_read_ ## thingy, \
}
-static ssize_t koneplus_sysfs_read_tcu_image(struct file *fp,
- struct kobject *kobj, struct bin_attribute *attr, char *buf,
- loff_t off, size_t count)
-{
- return koneplus_sysfs_read(fp, kobj, buf, off, count,
- sizeof(struct koneplus_tcu_image), KONEPLUS_COMMAND_TCU);
+#define KONEPLUS_BIN_ATTRIBUTE_W(thingy, THINGY) \
+{ \
+ .attr = { .name = #thingy, .mode = 0220 }, \
+ .size = KONEPLUS_SIZE_ ## THINGY, \
+ .write = koneplus_sysfs_write_ ## thingy \
}
-static ssize_t koneplus_sysfs_read_profilex_settings(struct file *fp,
- struct kobject *kobj, struct bin_attribute *attr, char *buf,
- loff_t off, size_t count)
-{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
- struct koneplus_device *koneplus = hid_get_drvdata(dev_get_drvdata(dev));
-
- if (off >= sizeof(struct koneplus_profile_settings))
- return 0;
+KONEPLUS_SYSFS_W(control, CONTROL)
+KONEPLUS_SYSFS_RW(info, INFO)
+KONEPLUS_SYSFS_W(talk, TALK)
+KONEPLUS_SYSFS_W(macro, MACRO)
+KONEPLUS_SYSFS_RW(sensor, SENSOR)
+KONEPLUS_SYSFS_RW(tcu, TCU)
+KONEPLUS_SYSFS_R(tcu_image, TCU_IMAGE)
+KONEPLUS_SYSFS_RW(profile_settings, PROFILE_SETTINGS)
+KONEPLUS_SYSFS_RW(profile_buttons, PROFILE_BUTTONS)
- if (off + count > sizeof(struct koneplus_profile_settings))
- count = sizeof(struct koneplus_profile_settings) - off;
-
- mutex_lock(&koneplus->koneplus_lock);
- memcpy(buf, ((char const *)&koneplus->profile_settings[*(uint *)(attr->private)]) + off,
- count);
- mutex_unlock(&koneplus->koneplus_lock);
-
- return count;
-}
-
-static ssize_t koneplus_sysfs_write_profile_settings(struct file *fp,
+static ssize_t koneplus_sysfs_read_profilex_settings(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
struct device *dev =
container_of(kobj, struct device, kobj)->parent->parent;
- struct koneplus_device *koneplus = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
- int retval = 0;
- int difference;
- int profile_number;
- struct koneplus_profile_settings *profile_settings;
-
- if (off != 0 || count != sizeof(struct koneplus_profile_settings))
- return -EINVAL;
-
- profile_number = ((struct koneplus_profile_settings const *)buf)->number;
- profile_settings = &koneplus->profile_settings[profile_number];
-
- mutex_lock(&koneplus->koneplus_lock);
- difference = memcmp(buf, profile_settings,
- sizeof(struct koneplus_profile_settings));
- if (difference) {
- retval = koneplus_set_profile_settings(usb_dev,
- (struct koneplus_profile_settings const *)buf);
- if (!retval)
- memcpy(profile_settings, buf,
- sizeof(struct koneplus_profile_settings));
- }
- mutex_unlock(&koneplus->koneplus_lock);
+ ssize_t retval;
+ retval = koneplus_send_control(usb_dev, *(uint *)(attr->private),
+ KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS);
if (retval)
return retval;
- return sizeof(struct koneplus_profile_settings);
+ return koneplus_sysfs_read(fp, kobj, buf, off, count,
+ KONEPLUS_SIZE_PROFILE_SETTINGS,
+ KONEPLUS_COMMAND_PROFILE_SETTINGS);
}
static ssize_t koneplus_sysfs_read_profilex_buttons(struct file *fp,
@@ -295,57 +212,17 @@ static ssize_t koneplus_sysfs_read_profilex_buttons(struct file *fp,
{
struct device *dev =
container_of(kobj, struct device, kobj)->parent->parent;
- struct koneplus_device *koneplus = hid_get_drvdata(dev_get_drvdata(dev));
-
- if (off >= sizeof(struct koneplus_profile_buttons))
- return 0;
-
- if (off + count > sizeof(struct koneplus_profile_buttons))
- count = sizeof(struct koneplus_profile_buttons) - off;
-
- mutex_lock(&koneplus->koneplus_lock);
- memcpy(buf, ((char const *)&koneplus->profile_buttons[*(uint *)(attr->private)]) + off,
- count);
- mutex_unlock(&koneplus->koneplus_lock);
-
- return count;
-}
-
-static ssize_t koneplus_sysfs_write_profile_buttons(struct file *fp,
- struct kobject *kobj, struct bin_attribute *attr, char *buf,
- loff_t off, size_t count)
-{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
- struct koneplus_device *koneplus = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
- int retval = 0;
- int difference;
- uint profile_number;
- struct koneplus_profile_buttons *profile_buttons;
-
- if (off != 0 || count != sizeof(struct koneplus_profile_buttons))
- return -EINVAL;
-
- profile_number = ((struct koneplus_profile_buttons const *)buf)->number;
- profile_buttons = &koneplus->profile_buttons[profile_number];
-
- mutex_lock(&koneplus->koneplus_lock);
- difference = memcmp(buf, profile_buttons,
- sizeof(struct koneplus_profile_buttons));
- if (difference) {
- retval = koneplus_set_profile_buttons(usb_dev,
- (struct koneplus_profile_buttons const *)buf);
- if (!retval)
- memcpy(profile_buttons, buf,
- sizeof(struct koneplus_profile_buttons));
- }
- mutex_unlock(&koneplus->koneplus_lock);
+ ssize_t retval;
+ retval = koneplus_send_control(usb_dev, *(uint *)(attr->private),
+ KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS);
if (retval)
return retval;
- return sizeof(struct koneplus_profile_buttons);
+ return koneplus_sysfs_read(fp, kobj, buf, off, count,
+ KONEPLUS_SIZE_PROFILE_BUTTONS,
+ KONEPLUS_COMMAND_PROFILE_BUTTONS);
}
static ssize_t koneplus_sysfs_show_actual_profile(struct device *dev,
@@ -401,9 +278,20 @@ static ssize_t koneplus_sysfs_set_actual_profile(struct device *dev,
static ssize_t koneplus_sysfs_show_firmware_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct koneplus_device *koneplus =
- hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
- return snprintf(buf, PAGE_SIZE, "%d\n", koneplus->info.firmware_version);
+ struct koneplus_device *koneplus;
+ struct usb_device *usb_dev;
+ struct koneplus_info info;
+
+ dev = dev->parent->parent;
+ koneplus = hid_get_drvdata(dev_get_drvdata(dev));
+ usb_dev = interface_to_usbdev(to_usb_interface(dev));
+
+ mutex_lock(&koneplus->koneplus_lock);
+ roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_INFO,
+ &info, KONEPLUS_SIZE_INFO);
+ mutex_unlock(&koneplus->koneplus_lock);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version);
}
static struct device_attribute koneplus_attributes[] = {
@@ -419,132 +307,85 @@ static struct device_attribute koneplus_attributes[] = {
};
static struct bin_attribute koneplus_bin_attributes[] = {
- {
- .attr = { .name = "sensor", .mode = 0660 },
- .size = sizeof(struct koneplus_sensor),
- .read = koneplus_sysfs_read_sensor,
- .write = koneplus_sysfs_write_sensor
- },
- {
- .attr = { .name = "tcu", .mode = 0220 },
- .size = sizeof(struct koneplus_tcu),
- .write = koneplus_sysfs_write_tcu
- },
- {
- .attr = { .name = "tcu_image", .mode = 0440 },
- .size = sizeof(struct koneplus_tcu_image),
- .read = koneplus_sysfs_read_tcu_image
- },
- {
- .attr = { .name = "profile_settings", .mode = 0220 },
- .size = sizeof(struct koneplus_profile_settings),
- .write = koneplus_sysfs_write_profile_settings
- },
+ KONEPLUS_BIN_ATTRIBUTE_W(control, CONTROL),
+ KONEPLUS_BIN_ATTRIBUTE_RW(info, INFO),
+ KONEPLUS_BIN_ATTRIBUTE_W(talk, TALK),
+ KONEPLUS_BIN_ATTRIBUTE_W(macro, MACRO),
+ KONEPLUS_BIN_ATTRIBUTE_RW(sensor, SENSOR),
+ KONEPLUS_BIN_ATTRIBUTE_RW(tcu, TCU),
+ KONEPLUS_BIN_ATTRIBUTE_R(tcu_image, TCU_IMAGE),
+ KONEPLUS_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS),
+ KONEPLUS_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS),
{
.attr = { .name = "profile1_settings", .mode = 0440 },
- .size = sizeof(struct koneplus_profile_settings),
+ .size = KONEPLUS_SIZE_PROFILE_SETTINGS,
.read = koneplus_sysfs_read_profilex_settings,
.private = &profile_numbers[0]
},
{
.attr = { .name = "profile2_settings", .mode = 0440 },
- .size = sizeof(struct koneplus_profile_settings),
+ .size = KONEPLUS_SIZE_PROFILE_SETTINGS,
.read = koneplus_sysfs_read_profilex_settings,
.private = &profile_numbers[1]
},
{
.attr = { .name = "profile3_settings", .mode = 0440 },
- .size = sizeof(struct koneplus_profile_settings),
+ .size = KONEPLUS_SIZE_PROFILE_SETTINGS,
.read = koneplus_sysfs_read_profilex_settings,
.private = &profile_numbers[2]
},
{
.attr = { .name = "profile4_settings", .mode = 0440 },
- .size = sizeof(struct koneplus_profile_settings),
+ .size = KONEPLUS_SIZE_PROFILE_SETTINGS,
.read = koneplus_sysfs_read_profilex_settings,
.private = &profile_numbers[3]
},
{
.attr = { .name = "profile5_settings", .mode = 0440 },
- .size = sizeof(struct koneplus_profile_settings),
+ .size = KONEPLUS_SIZE_PROFILE_SETTINGS,
.read = koneplus_sysfs_read_profilex_settings,
.private = &profile_numbers[4]
},
{
- .attr = { .name = "profile_buttons", .mode = 0220 },
- .size = sizeof(struct koneplus_profile_buttons),
- .write = koneplus_sysfs_write_profile_buttons
- },
- {
.attr = { .name = "profile1_buttons", .mode = 0440 },
- .size = sizeof(struct koneplus_profile_buttons),
+ .size = KONEPLUS_SIZE_PROFILE_BUTTONS,
.read = koneplus_sysfs_read_profilex_buttons,
.private = &profile_numbers[0]
},
{
.attr = { .name = "profile2_buttons", .mode = 0440 },
- .size = sizeof(struct koneplus_profile_buttons),
+ .size = KONEPLUS_SIZE_PROFILE_BUTTONS,
.read = koneplus_sysfs_read_profilex_buttons,
.private = &profile_numbers[1]
},
{
.attr = { .name = "profile3_buttons", .mode = 0440 },
- .size = sizeof(struct koneplus_profile_buttons),
+ .size = KONEPLUS_SIZE_PROFILE_BUTTONS,
.read = koneplus_sysfs_read_profilex_buttons,
.private = &profile_numbers[2]
},
{
.attr = { .name = "profile4_buttons", .mode = 0440 },
- .size = sizeof(struct koneplus_profile_buttons),
+ .size = KONEPLUS_SIZE_PROFILE_BUTTONS,
.read = koneplus_sysfs_read_profilex_buttons,
.private = &profile_numbers[3]
},
{
.attr = { .name = "profile5_buttons", .mode = 0440 },
- .size = sizeof(struct koneplus_profile_buttons),
+ .size = KONEPLUS_SIZE_PROFILE_BUTTONS,
.read = koneplus_sysfs_read_profilex_buttons,
.private = &profile_numbers[4]
},
- {
- .attr = { .name = "macro", .mode = 0220 },
- .size = sizeof(struct koneplus_macro),
- .write = koneplus_sysfs_write_macro
- },
- {
- .attr = { .name = "talk", .mode = 0220 },
- .size = sizeof(struct koneplus_talk),
- .write = koneplus_sysfs_write_talk
- },
__ATTR_NULL
};
static int koneplus_init_koneplus_device_struct(struct usb_device *usb_dev,
struct koneplus_device *koneplus)
{
- int retval, i;
- static uint wait = 200;
+ int retval;
mutex_init(&koneplus->koneplus_lock);
- retval = koneplus_get_info(usb_dev, &koneplus->info);
- if (retval)
- return retval;
-
- for (i = 0; i < 5; ++i) {
- msleep(wait);
- retval = koneplus_get_profile_settings(usb_dev,
- &koneplus->profile_settings[i], i);
- if (retval)
- return retval;
-
- msleep(wait);
- retval = koneplus_get_profile_buttons(usb_dev,
- &koneplus->profile_buttons[i], i);
- if (retval)
- return retval;
- }
-
- msleep(wait);
retval = koneplus_get_actual_profile(usb_dev);
if (retval < 0)
return retval;
@@ -709,6 +550,7 @@ static int koneplus_raw_event(struct hid_device *hdev,
static const struct hid_device_id koneplus_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEXTD) },
{ }
};
@@ -749,5 +591,5 @@ module_init(koneplus_init);
module_exit(koneplus_exit);
MODULE_AUTHOR("Stefan Achatz");
-MODULE_DESCRIPTION("USB Roccat Kone[+] driver");
+MODULE_DESCRIPTION("USB Roccat Kone[+]/XTD driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/hid/hid-roccat-koneplus.h b/drivers/hid/hid-roccat-koneplus.h
index 7074b2a4b94..af7f57e8cf3 100644
--- a/drivers/hid/hid-roccat-koneplus.h
+++ b/drivers/hid/hid-roccat-koneplus.h
@@ -14,11 +14,19 @@
#include <linux/types.h>
-struct koneplus_talk {
- uint8_t command; /* KONEPLUS_COMMAND_TALK */
- uint8_t size; /* always 0x10 */
- uint8_t data[14];
-} __packed;
+enum {
+ KONEPLUS_SIZE_ACTUAL_PROFILE = 0x03,
+ KONEPLUS_SIZE_CONTROL = 0x03,
+ KONEPLUS_SIZE_FIRMWARE_WRITE = 0x0402,
+ KONEPLUS_SIZE_INFO = 0x06,
+ KONEPLUS_SIZE_MACRO = 0x0822,
+ KONEPLUS_SIZE_PROFILE_SETTINGS = 0x2b,
+ KONEPLUS_SIZE_PROFILE_BUTTONS = 0x4d,
+ KONEPLUS_SIZE_SENSOR = 0x06,
+ KONEPLUS_SIZE_TALK = 0x10,
+ KONEPLUS_SIZE_TCU = 0x04,
+ KONEPLUS_SIZE_TCU_IMAGE = 0x0404,
+};
enum koneplus_control_requests {
KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS = 0x80,
@@ -31,45 +39,6 @@ struct koneplus_actual_profile {
uint8_t actual_profile; /* Range 0-4! */
} __attribute__ ((__packed__));
-struct koneplus_profile_settings {
- uint8_t command; /* KONEPLUS_COMMAND_PROFILE_SETTINGS */
- uint8_t size; /* always 43 */
- uint8_t number; /* range 0-4 */
- uint8_t advanced_sensitivity;
- uint8_t sensitivity_x;
- uint8_t sensitivity_y;
- uint8_t cpi_levels_enabled;
- uint8_t cpi_levels_x[5];
- uint8_t cpi_startup_level; /* range 0-4 */
- uint8_t cpi_levels_y[5]; /* range 1-60 means 100-6000 cpi */
- uint8_t unknown1;
- uint8_t polling_rate;
- uint8_t lights_enabled;
- uint8_t light_effect_mode;
- uint8_t color_flow_effect;
- uint8_t light_effect_type;
- uint8_t light_effect_speed;
- uint8_t lights[16];
- uint16_t checksum;
-} __attribute__ ((__packed__));
-
-struct koneplus_profile_buttons {
- uint8_t command; /* KONEPLUS_COMMAND_PROFILE_BUTTONS */
- uint8_t size; /* always 77 */
- uint8_t number; /* range 0-4 */
- uint8_t data[72];
- uint16_t checksum;
-} __attribute__ ((__packed__));
-
-struct koneplus_macro {
- uint8_t command; /* KONEPLUS_COMMAND_MACRO */
- uint16_t size; /* always 0x822 little endian */
- uint8_t profile; /* range 0-4 */
- uint8_t button; /* range 0-23 */
- uint8_t data[2075];
- uint16_t checksum;
-} __attribute__ ((__packed__));
-
struct koneplus_info {
uint8_t command; /* KONEPLUS_COMMAND_INFO */
uint8_t size; /* always 6 */
@@ -77,51 +46,15 @@ struct koneplus_info {
uint8_t unknown[3];
} __attribute__ ((__packed__));
-struct koneplus_e {
- uint8_t command; /* KONEPLUS_COMMAND_E */
- uint8_t size; /* always 3 */
- uint8_t unknown; /* TODO 1; 0 before firmware update */
-} __attribute__ ((__packed__));
-
-struct koneplus_sensor {
- uint8_t command; /* KONEPLUS_COMMAND_SENSOR */
- uint8_t size; /* always 6 */
- uint8_t data[4];
-} __attribute__ ((__packed__));
-
-struct koneplus_firmware_write {
- uint8_t command; /* KONEPLUS_COMMAND_FIRMWARE_WRITE */
- uint8_t unknown[1025];
-} __attribute__ ((__packed__));
-
-struct koneplus_firmware_write_control {
- uint8_t command; /* KONEPLUS_COMMAND_FIRMWARE_WRITE_CONTROL */
- /*
- * value is 1 on success
- * 3 means "not finished yet"
- */
- uint8_t value;
- uint8_t unknown; /* always 0x75 */
-} __attribute__ ((__packed__));
-
-struct koneplus_tcu {
- uint16_t usb_command; /* KONEPLUS_USB_COMMAND_TCU */
- uint8_t data[2];
-} __attribute__ ((__packed__));
-
-struct koneplus_tcu_image {
- uint16_t usb_command; /* KONEPLUS_USB_COMMAND_TCU */
- uint8_t data[1024];
- uint16_t checksum;
-} __attribute__ ((__packed__));
-
enum koneplus_commands {
KONEPLUS_COMMAND_ACTUAL_PROFILE = 0x5,
+ KONEPLUS_COMMAND_CONTROL = 0x4,
KONEPLUS_COMMAND_PROFILE_SETTINGS = 0x6,
KONEPLUS_COMMAND_PROFILE_BUTTONS = 0x7,
KONEPLUS_COMMAND_MACRO = 0x8,
KONEPLUS_COMMAND_INFO = 0x9,
KONEPLUS_COMMAND_TCU = 0xc,
+ KONEPLUS_COMMAND_TCU_IMAGE = 0xc,
KONEPLUS_COMMAND_E = 0xe,
KONEPLUS_COMMAND_SENSOR = 0xf,
KONEPLUS_COMMAND_TALK = 0x10,
@@ -187,10 +120,6 @@ struct koneplus_device {
int chrdev_minor;
struct mutex koneplus_lock;
-
- struct koneplus_info info;
- struct koneplus_profile_settings profile_settings[5];
- struct koneplus_profile_buttons profile_buttons[5];
};
#endif
diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c
index ca6527ac655..b8b37789b86 100644
--- a/drivers/hid/hid-roccat-kovaplus.c
+++ b/drivers/hid/hid-roccat-kovaplus.c
@@ -70,13 +70,6 @@ static int kovaplus_select_profile(struct usb_device *usb_dev, uint number,
return kovaplus_send_control(usb_dev, number, request);
}
-static int kovaplus_get_info(struct usb_device *usb_dev,
- struct kovaplus_info *buf)
-{
- return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_INFO,
- buf, sizeof(struct kovaplus_info));
-}
-
static int kovaplus_get_profile_settings(struct usb_device *usb_dev,
struct kovaplus_profile_settings *buf, uint number)
{
@@ -88,15 +81,7 @@ static int kovaplus_get_profile_settings(struct usb_device *usb_dev,
return retval;
return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_SETTINGS,
- buf, sizeof(struct kovaplus_profile_settings));
-}
-
-static int kovaplus_set_profile_settings(struct usb_device *usb_dev,
- struct kovaplus_profile_settings const *settings)
-{
- return roccat_common2_send_with_status(usb_dev,
- KOVAPLUS_COMMAND_PROFILE_SETTINGS,
- settings, sizeof(struct kovaplus_profile_settings));
+ buf, KOVAPLUS_SIZE_PROFILE_SETTINGS);
}
static int kovaplus_get_profile_buttons(struct usb_device *usb_dev,
@@ -110,15 +95,7 @@ static int kovaplus_get_profile_buttons(struct usb_device *usb_dev,
return retval;
return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_BUTTONS,
- buf, sizeof(struct kovaplus_profile_buttons));
-}
-
-static int kovaplus_set_profile_buttons(struct usb_device *usb_dev,
- struct kovaplus_profile_buttons const *buttons)
-{
- return roccat_common2_send_with_status(usb_dev,
- KOVAPLUS_COMMAND_PROFILE_BUTTONS,
- buttons, sizeof(struct kovaplus_profile_buttons));
+ buf, KOVAPLUS_SIZE_PROFILE_BUTTONS);
}
/* retval is 0-4 on success, < 0 on error */
@@ -147,122 +124,141 @@ static int kovaplus_set_actual_profile(struct usb_device *usb_dev,
&buf, sizeof(struct kovaplus_actual_profile));
}
-static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp,
- struct kobject *kobj, struct bin_attribute *attr, char *buf,
- loff_t off, size_t count)
+static ssize_t kovaplus_sysfs_read(struct file *fp, struct kobject *kobj,
+ char *buf, loff_t off, size_t count,
+ size_t real_size, uint command)
{
struct device *dev =
container_of(kobj, struct device, kobj)->parent->parent;
struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
+ struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
+ int retval;
- if (off >= sizeof(struct kovaplus_profile_settings))
+ if (off >= real_size)
return 0;
- if (off + count > sizeof(struct kovaplus_profile_settings))
- count = sizeof(struct kovaplus_profile_settings) - off;
+ if (off != 0 || count != real_size)
+ return -EINVAL;
mutex_lock(&kovaplus->kovaplus_lock);
- memcpy(buf, ((char const *)&kovaplus->profile_settings[*(uint *)(attr->private)]) + off,
- count);
+ retval = roccat_common2_receive(usb_dev, command, buf, real_size);
mutex_unlock(&kovaplus->kovaplus_lock);
- return count;
+ if (retval)
+ return retval;
+
+ return real_size;
}
-static ssize_t kovaplus_sysfs_write_profile_settings(struct file *fp,
- struct kobject *kobj, struct bin_attribute *attr, char *buf,
- loff_t off, size_t count)
+static ssize_t kovaplus_sysfs_write(struct file *fp, struct kobject *kobj,
+ void const *buf, loff_t off, size_t count,
+ size_t real_size, uint command)
{
struct device *dev =
container_of(kobj, struct device, kobj)->parent->parent;
struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
- int retval = 0;
- int difference;
- int profile_index;
- struct kovaplus_profile_settings *profile_settings;
+ int retval;
- if (off != 0 || count != sizeof(struct kovaplus_profile_settings))
+ if (off != 0 || count != real_size)
return -EINVAL;
- profile_index = ((struct kovaplus_profile_settings const *)buf)->profile_index;
- profile_settings = &kovaplus->profile_settings[profile_index];
-
mutex_lock(&kovaplus->kovaplus_lock);
- difference = memcmp(buf, profile_settings,
- sizeof(struct kovaplus_profile_settings));
- if (difference) {
- retval = kovaplus_set_profile_settings(usb_dev,
- (struct kovaplus_profile_settings const *)buf);
- if (!retval)
- memcpy(profile_settings, buf,
- sizeof(struct kovaplus_profile_settings));
- }
+ retval = roccat_common2_send_with_status(usb_dev, command,
+ buf, real_size);
mutex_unlock(&kovaplus->kovaplus_lock);
if (retval)
return retval;
- return sizeof(struct kovaplus_profile_settings);
+ return real_size;
}
-static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp,
- struct kobject *kobj, struct bin_attribute *attr, char *buf,
- loff_t off, size_t count)
-{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
- struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
+#define KOVAPLUS_SYSFS_W(thingy, THINGY) \
+static ssize_t kovaplus_sysfs_write_ ## thingy(struct file *fp, \
+ struct kobject *kobj, struct bin_attribute *attr, char *buf, \
+ loff_t off, size_t count) \
+{ \
+ return kovaplus_sysfs_write(fp, kobj, buf, off, count, \
+ KOVAPLUS_SIZE_ ## THINGY, KOVAPLUS_COMMAND_ ## THINGY); \
+}
- if (off >= sizeof(struct kovaplus_profile_buttons))
- return 0;
+#define KOVAPLUS_SYSFS_R(thingy, THINGY) \
+static ssize_t kovaplus_sysfs_read_ ## thingy(struct file *fp, \
+ struct kobject *kobj, struct bin_attribute *attr, char *buf, \
+ loff_t off, size_t count) \
+{ \
+ return kovaplus_sysfs_read(fp, kobj, buf, off, count, \
+ KOVAPLUS_SIZE_ ## THINGY, KOVAPLUS_COMMAND_ ## THINGY); \
+}
- if (off + count > sizeof(struct kovaplus_profile_buttons))
- count = sizeof(struct kovaplus_profile_buttons) - off;
+#define KOVAPLUS_SYSFS_RW(thingy, THINGY) \
+KOVAPLUS_SYSFS_W(thingy, THINGY) \
+KOVAPLUS_SYSFS_R(thingy, THINGY)
- mutex_lock(&kovaplus->kovaplus_lock);
- memcpy(buf, ((char const *)&kovaplus->profile_buttons[*(uint *)(attr->private)]) + off,
- count);
- mutex_unlock(&kovaplus->kovaplus_lock);
+#define KOVAPLUS_BIN_ATTRIBUTE_RW(thingy, THINGY) \
+{ \
+ .attr = { .name = #thingy, .mode = 0660 }, \
+ .size = KOVAPLUS_SIZE_ ## THINGY, \
+ .read = kovaplus_sysfs_read_ ## thingy, \
+ .write = kovaplus_sysfs_write_ ## thingy \
+}
+
+#define KOVAPLUS_BIN_ATTRIBUTE_R(thingy, THINGY) \
+{ \
+ .attr = { .name = #thingy, .mode = 0440 }, \
+ .size = KOVAPLUS_SIZE_ ## THINGY, \
+ .read = kovaplus_sysfs_read_ ## thingy, \
+}
- return count;
+#define KOVAPLUS_BIN_ATTRIBUTE_W(thingy, THINGY) \
+{ \
+ .attr = { .name = #thingy, .mode = 0220 }, \
+ .size = KOVAPLUS_SIZE_ ## THINGY, \
+ .write = kovaplus_sysfs_write_ ## thingy \
}
-static ssize_t kovaplus_sysfs_write_profile_buttons(struct file *fp,
+KOVAPLUS_SYSFS_W(control, CONTROL)
+KOVAPLUS_SYSFS_RW(info, INFO)
+KOVAPLUS_SYSFS_RW(profile_settings, PROFILE_SETTINGS)
+KOVAPLUS_SYSFS_RW(profile_buttons, PROFILE_BUTTONS)
+
+static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
struct device *dev =
container_of(kobj, struct device, kobj)->parent->parent;
- struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
- int retval = 0;
- int difference;
- uint profile_index;
- struct kovaplus_profile_buttons *profile_buttons;
+ ssize_t retval;
- if (off != 0 || count != sizeof(struct kovaplus_profile_buttons))
- return -EINVAL;
+ retval = kovaplus_select_profile(usb_dev, *(uint *)(attr->private),
+ KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS);
+ if (retval)
+ return retval;
- profile_index = ((struct kovaplus_profile_buttons const *)buf)->profile_index;
- profile_buttons = &kovaplus->profile_buttons[profile_index];
+ return kovaplus_sysfs_read(fp, kobj, buf, off, count,
+ KOVAPLUS_SIZE_PROFILE_SETTINGS,
+ KOVAPLUS_COMMAND_PROFILE_SETTINGS);
+}
- mutex_lock(&kovaplus->kovaplus_lock);
- difference = memcmp(buf, profile_buttons,
- sizeof(struct kovaplus_profile_buttons));
- if (difference) {
- retval = kovaplus_set_profile_buttons(usb_dev,
- (struct kovaplus_profile_buttons const *)buf);
- if (!retval)
- memcpy(profile_buttons, buf,
- sizeof(struct kovaplus_profile_buttons));
- }
- mutex_unlock(&kovaplus->kovaplus_lock);
+static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp,
+ struct kobject *kobj, struct bin_attribute *attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct device *dev =
+ container_of(kobj, struct device, kobj)->parent->parent;
+ struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
+ ssize_t retval;
+ retval = kovaplus_select_profile(usb_dev, *(uint *)(attr->private),
+ KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS);
if (retval)
return retval;
- return sizeof(struct kovaplus_profile_buttons);
+ return kovaplus_sysfs_read(fp, kobj, buf, off, count,
+ KOVAPLUS_SIZE_PROFILE_BUTTONS,
+ KOVAPLUS_COMMAND_PROFILE_BUTTONS);
}
static ssize_t kovaplus_sysfs_show_actual_profile(struct device *dev,
@@ -342,9 +338,20 @@ static ssize_t kovaplus_sysfs_show_actual_sensitivity_y(struct device *dev,
static ssize_t kovaplus_sysfs_show_firmware_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct kovaplus_device *kovaplus =
- hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
- return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->info.firmware_version);
+ struct kovaplus_device *kovaplus;
+ struct usb_device *usb_dev;
+ struct kovaplus_info info;
+
+ dev = dev->parent->parent;
+ kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
+ usb_dev = interface_to_usbdev(to_usb_interface(dev));
+
+ mutex_lock(&kovaplus->kovaplus_lock);
+ roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_INFO,
+ &info, KOVAPLUS_SIZE_INFO);
+ mutex_unlock(&kovaplus->kovaplus_lock);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version);
}
static struct device_attribute kovaplus_attributes[] = {
@@ -363,73 +370,67 @@ static struct device_attribute kovaplus_attributes[] = {
};
static struct bin_attribute kovaplus_bin_attributes[] = {
- {
- .attr = { .name = "profile_settings", .mode = 0220 },
- .size = sizeof(struct kovaplus_profile_settings),
- .write = kovaplus_sysfs_write_profile_settings
- },
+ KOVAPLUS_BIN_ATTRIBUTE_W(control, CONTROL),
+ KOVAPLUS_BIN_ATTRIBUTE_RW(info, INFO),
+ KOVAPLUS_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS),
+ KOVAPLUS_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS),
{
.attr = { .name = "profile1_settings", .mode = 0440 },
- .size = sizeof(struct kovaplus_profile_settings),
+ .size = KOVAPLUS_SIZE_PROFILE_SETTINGS,
.read = kovaplus_sysfs_read_profilex_settings,
.private = &profile_numbers[0]
},
{
.attr = { .name = "profile2_settings", .mode = 0440 },
- .size = sizeof(struct kovaplus_profile_settings),
+ .size = KOVAPLUS_SIZE_PROFILE_SETTINGS,
.read = kovaplus_sysfs_read_profilex_settings,
.private = &profile_numbers[1]
},
{
.attr = { .name = "profile3_settings", .mode = 0440 },
- .size = sizeof(struct kovaplus_profile_settings),
+ .size = KOVAPLUS_SIZE_PROFILE_SETTINGS,
.read = kovaplus_sysfs_read_profilex_settings,
.private = &profile_numbers[2]
},
{
.attr = { .name = "profile4_settings", .mode = 0440 },
- .size = sizeof(struct kovaplus_profile_settings),
+ .size = KOVAPLUS_SIZE_PROFILE_SETTINGS,
.read = kovaplus_sysfs_read_profilex_settings,
.private = &profile_numbers[3]
},
{
.attr = { .name = "profile5_settings", .mode = 0440 },
- .size = sizeof(struct kovaplus_profile_settings),
+ .size = KOVAPLUS_SIZE_PROFILE_SETTINGS,
.read = kovaplus_sysfs_read_profilex_settings,
.private = &profile_numbers[4]
},
{
- .attr = { .name = "profile_buttons", .mode = 0220 },
- .size = sizeof(struct kovaplus_profile_buttons),
- .write = kovaplus_sysfs_write_profile_buttons
- },
- {
.attr = { .name = "profile1_buttons", .mode = 0440 },
- .size = sizeof(struct kovaplus_profile_buttons),
+ .size = KOVAPLUS_SIZE_PROFILE_BUTTONS,
.read = kovaplus_sysfs_read_profilex_buttons,
.private = &profile_numbers[0]
},
{
.attr = { .name = "profile2_buttons", .mode = 0440 },
- .size = sizeof(struct kovaplus_profile_buttons),
+ .size = KOVAPLUS_SIZE_PROFILE_BUTTONS,
.read = kovaplus_sysfs_read_profilex_buttons,
.private = &profile_numbers[1]
},
{
.attr = { .name = "profile3_buttons", .mode = 0440 },
- .size = sizeof(struct kovaplus_profile_buttons),
+ .size = KOVAPLUS_SIZE_PROFILE_BUTTONS,
.read = kovaplus_sysfs_read_profilex_buttons,
.private = &profile_numbers[2]
},
{
.attr = { .name = "profile4_buttons", .mode = 0440 },
- .size = sizeof(struct kovaplus_profile_buttons),
+ .size = KOVAPLUS_SIZE_PROFILE_BUTTONS,
.read = kovaplus_sysfs_read_profilex_buttons,
.private = &profile_numbers[3]
},
{
.attr = { .name = "profile5_buttons", .mode = 0440 },
- .size = sizeof(struct kovaplus_profile_buttons),
+ .size = KOVAPLUS_SIZE_PROFILE_BUTTONS,
.read = kovaplus_sysfs_read_profilex_buttons,
.private = &profile_numbers[4]
},
@@ -444,10 +445,6 @@ static int kovaplus_init_kovaplus_device_struct(struct usb_device *usb_dev,
mutex_init(&kovaplus->kovaplus_lock);
- retval = kovaplus_get_info(usb_dev, &kovaplus->info);
- if (retval)
- return retval;
-
for (i = 0; i < 5; ++i) {
msleep(wait);
retval = kovaplus_get_profile_settings(usb_dev,
diff --git a/drivers/hid/hid-roccat-kovaplus.h b/drivers/hid/hid-roccat-kovaplus.h
index f82daa1cdcb..fbb7a16a7e5 100644
--- a/drivers/hid/hid-roccat-kovaplus.h
+++ b/drivers/hid/hid-roccat-kovaplus.h
@@ -14,6 +14,13 @@
#include <linux/types.h>
+enum {
+ KOVAPLUS_SIZE_CONTROL = 0x03,
+ KOVAPLUS_SIZE_INFO = 0x06,
+ KOVAPLUS_SIZE_PROFILE_SETTINGS = 0x10,
+ KOVAPLUS_SIZE_PROFILE_BUTTONS = 0x17,
+};
+
enum kovaplus_control_requests {
/* write; value = profile number range 0-4 */
KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10,
@@ -53,15 +60,9 @@ struct kovaplus_info {
uint8_t unknown[3];
} __packed;
-/* writes 1 on plugin */
-struct kovaplus_a {
- uint8_t command; /* KOVAPLUS_COMMAND_A */
- uint8_t size; /* 3 */
- uint8_t unknown;
-} __packed;
-
enum kovaplus_commands {
KOVAPLUS_COMMAND_ACTUAL_PROFILE = 0x5,
+ KOVAPLUS_COMMAND_CONTROL = 0x4,
KOVAPLUS_COMMAND_PROFILE_SETTINGS = 0x6,
KOVAPLUS_COMMAND_PROFILE_BUTTONS = 0x7,
KOVAPLUS_COMMAND_INFO = 0x9,
@@ -125,7 +126,6 @@ struct kovaplus_device {
int roccat_claimed;
int chrdev_minor;
struct mutex kovaplus_lock;
- struct kovaplus_info info;
struct kovaplus_profile_settings profile_settings[5];
struct kovaplus_profile_buttons profile_buttons[5];
};
diff --git a/drivers/hid/hid-roccat-lua.c b/drivers/hid/hid-roccat-lua.c
new file mode 100644
index 00000000000..5084fb4b7e9
--- /dev/null
+++ b/drivers/hid/hid-roccat-lua.c
@@ -0,0 +1,227 @@
+/*
+ * Roccat Lua driver for Linux
+ *
+ * Copyright (c) 2012 Stefan Achatz <erazor_de@users.sourceforge.net>
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * Roccat Lua is a gamer mouse which cpi, button and light settings can be
+ * configured.
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/hid-roccat.h>
+#include "hid-ids.h"
+#include "hid-roccat-common.h"
+#include "hid-roccat-lua.h"
+
+static ssize_t lua_sysfs_read(struct file *fp, struct kobject *kobj,
+ char *buf, loff_t off, size_t count,
+ size_t real_size, uint command)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct lua_device *lua = hid_get_drvdata(dev_get_drvdata(dev));
+ struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
+ int retval;
+
+ if (off >= real_size)
+ return 0;
+
+ if (off != 0 || count != real_size)
+ return -EINVAL;
+
+ mutex_lock(&lua->lua_lock);
+ retval = roccat_common2_receive(usb_dev, command, buf, real_size);
+ mutex_unlock(&lua->lua_lock);
+
+ return retval ? retval : real_size;
+}
+
+static ssize_t lua_sysfs_write(struct file *fp, struct kobject *kobj,
+ void const *buf, loff_t off, size_t count,
+ size_t real_size, uint command)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct lua_device *lua = hid_get_drvdata(dev_get_drvdata(dev));
+ struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
+ int retval;
+
+ if (off != 0 || count != real_size)
+ return -EINVAL;
+
+ mutex_lock(&lua->lua_lock);
+ retval = roccat_common2_send(usb_dev, command, (void *)buf, real_size);
+ mutex_unlock(&lua->lua_lock);
+
+ return retval ? retval : real_size;
+}
+
+#define LUA_SYSFS_W(thingy, THINGY) \
+static ssize_t lua_sysfs_write_ ## thingy(struct file *fp, \
+ struct kobject *kobj, struct bin_attribute *attr, \
+ char *buf, loff_t off, size_t count) \
+{ \
+ return lua_sysfs_write(fp, kobj, buf, off, count, \
+ LUA_SIZE_ ## THINGY, LUA_COMMAND_ ## THINGY); \
+}
+
+#define LUA_SYSFS_R(thingy, THINGY) \
+static ssize_t lua_sysfs_read_ ## thingy(struct file *fp, \
+ struct kobject *kobj, struct bin_attribute *attr, \
+ char *buf, loff_t off, size_t count) \
+{ \
+ return lua_sysfs_read(fp, kobj, buf, off, count, \
+ LUA_SIZE_ ## THINGY, LUA_COMMAND_ ## THINGY); \
+}
+
+#define LUA_BIN_ATTRIBUTE_RW(thingy, THINGY) \
+LUA_SYSFS_W(thingy, THINGY) \
+LUA_SYSFS_R(thingy, THINGY) \
+static struct bin_attribute lua_ ## thingy ## _attr = { \
+ .attr = { .name = #thingy, .mode = 0660 }, \
+ .size = LUA_SIZE_ ## THINGY, \
+ .read = lua_sysfs_read_ ## thingy, \
+ .write = lua_sysfs_write_ ## thingy \
+};
+
+LUA_BIN_ATTRIBUTE_RW(control, CONTROL)
+
+static int lua_create_sysfs_attributes(struct usb_interface *intf)
+{
+ return sysfs_create_bin_file(&intf->dev.kobj, &lua_control_attr);
+}
+
+static void lua_remove_sysfs_attributes(struct usb_interface *intf)
+{
+ sysfs_remove_bin_file(&intf->dev.kobj, &lua_control_attr);
+}
+
+static int lua_init_lua_device_struct(struct usb_device *usb_dev,
+ struct lua_device *lua)
+{
+ mutex_init(&lua->lua_lock);
+
+ return 0;
+}
+
+static int lua_init_specials(struct hid_device *hdev)
+{
+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+ struct usb_device *usb_dev = interface_to_usbdev(intf);
+ struct lua_device *lua;
+ int retval;
+
+ lua = kzalloc(sizeof(*lua), GFP_KERNEL);
+ if (!lua) {
+ hid_err(hdev, "can't alloc device descriptor\n");
+ return -ENOMEM;
+ }
+ hid_set_drvdata(hdev, lua);
+
+ retval = lua_init_lua_device_struct(usb_dev, lua);
+ if (retval) {
+ hid_err(hdev, "couldn't init struct lua_device\n");
+ goto exit;
+ }
+
+ retval = lua_create_sysfs_attributes(intf);
+ if (retval) {
+ hid_err(hdev, "cannot create sysfs files\n");
+ goto exit;
+ }
+
+ return 0;
+exit:
+ kfree(lua);
+ return retval;
+}
+
+static void lua_remove_specials(struct hid_device *hdev)
+{
+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+ struct lua_device *lua;
+
+ lua_remove_sysfs_attributes(intf);
+
+ lua = hid_get_drvdata(hdev);
+ kfree(lua);
+}
+
+static int lua_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ int retval;
+
+ retval = hid_parse(hdev);
+ if (retval) {
+ hid_err(hdev, "parse failed\n");
+ goto exit;
+ }
+
+ retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ if (retval) {
+ hid_err(hdev, "hw start failed\n");
+ goto exit;
+ }
+
+ retval = lua_init_specials(hdev);
+ if (retval) {
+ hid_err(hdev, "couldn't install mouse\n");
+ goto exit_stop;
+ }
+
+ return 0;
+
+exit_stop:
+ hid_hw_stop(hdev);
+exit:
+ return retval;
+}
+
+static void lua_remove(struct hid_device *hdev)
+{
+ lua_remove_specials(hdev);
+ hid_hw_stop(hdev);
+}
+
+static const struct hid_device_id lua_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_LUA) },
+ { }
+};
+
+MODULE_DEVICE_TABLE(hid, lua_devices);
+
+static struct hid_driver lua_driver = {
+ .name = "lua",
+ .id_table = lua_devices,
+ .probe = lua_probe,
+ .remove = lua_remove
+};
+
+static int __init lua_init(void)
+{
+ return hid_register_driver(&lua_driver);
+}
+
+static void __exit lua_exit(void)
+{
+ hid_unregister_driver(&lua_driver);
+}
+
+module_init(lua_init);
+module_exit(lua_exit);
+
+MODULE_AUTHOR("Stefan Achatz");
+MODULE_DESCRIPTION("USB Roccat Lua driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hid/hid-roccat-lua.h b/drivers/hid/hid-roccat-lua.h
new file mode 100644
index 00000000000..547d77a375d
--- /dev/null
+++ b/drivers/hid/hid-roccat-lua.h
@@ -0,0 +1,29 @@
+#ifndef __HID_ROCCAT_LUA_H
+#define __HID_ROCCAT_LUA_H
+
+/*
+ * Copyright (c) 2012 Stefan Achatz <erazor_de@users.sourceforge.net>
+ */
+
+/*
+ * 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/types.h>
+
+enum {
+ LUA_SIZE_CONTROL = 8,
+};
+
+enum lua_commands {
+ LUA_COMMAND_CONTROL = 3,
+};
+
+struct lua_device {
+ struct mutex lua_lock;
+};
+
+#endif
diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c
index 1317c177a3e..d4f1e3bee59 100644
--- a/drivers/hid/hid-roccat-pyra.c
+++ b/drivers/hid/hid-roccat-pyra.c
@@ -66,48 +66,14 @@ static int pyra_get_profile_settings(struct usb_device *usb_dev,
if (retval)
return retval;
return roccat_common2_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS,
- buf, sizeof(struct pyra_profile_settings));
-}
-
-static int pyra_get_profile_buttons(struct usb_device *usb_dev,
- struct pyra_profile_buttons *buf, int number)
-{
- int retval;
- retval = pyra_send_control(usb_dev, number,
- PYRA_CONTROL_REQUEST_PROFILE_BUTTONS);
- if (retval)
- return retval;
- return roccat_common2_receive(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS,
- buf, sizeof(struct pyra_profile_buttons));
+ buf, PYRA_SIZE_PROFILE_SETTINGS);
}
static int pyra_get_settings(struct usb_device *usb_dev,
struct pyra_settings *buf)
{
return roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS,
- buf, sizeof(struct pyra_settings));
-}
-
-static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf)
-{
- return roccat_common2_receive(usb_dev, PYRA_COMMAND_INFO,
- buf, sizeof(struct pyra_info));
-}
-
-static int pyra_set_profile_settings(struct usb_device *usb_dev,
- struct pyra_profile_settings const *settings)
-{
- return roccat_common2_send_with_status(usb_dev,
- PYRA_COMMAND_PROFILE_SETTINGS, settings,
- sizeof(struct pyra_profile_settings));
-}
-
-static int pyra_set_profile_buttons(struct usb_device *usb_dev,
- struct pyra_profile_buttons const *buttons)
-{
- return roccat_common2_send_with_status(usb_dev,
- PYRA_COMMAND_PROFILE_BUTTONS, buttons,
- sizeof(struct pyra_profile_buttons));
+ buf, PYRA_SIZE_SETTINGS);
}
static int pyra_set_settings(struct usb_device *usb_dev,
@@ -115,146 +81,144 @@ static int pyra_set_settings(struct usb_device *usb_dev,
{
return roccat_common2_send_with_status(usb_dev,
PYRA_COMMAND_SETTINGS, settings,
- sizeof(struct pyra_settings));
+ PYRA_SIZE_SETTINGS);
}
-static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp,
- struct kobject *kobj, struct bin_attribute *attr, char *buf,
- loff_t off, size_t count)
+static ssize_t pyra_sysfs_read(struct file *fp, struct kobject *kobj,
+ char *buf, loff_t off, size_t count,
+ size_t real_size, uint command)
{
struct device *dev =
container_of(kobj, struct device, kobj)->parent->parent;
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
+ struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
+ int retval;
- if (off >= sizeof(struct pyra_profile_settings))
+ if (off >= real_size)
return 0;
- if (off + count > sizeof(struct pyra_profile_settings))
- count = sizeof(struct pyra_profile_settings) - off;
+ if (off != 0 || count != real_size)
+ return -EINVAL;
mutex_lock(&pyra->pyra_lock);
- memcpy(buf, ((char const *)&pyra->profile_settings[*(uint *)(attr->private)]) + off,
- count);
+ retval = roccat_common2_receive(usb_dev, command, buf, real_size);
mutex_unlock(&pyra->pyra_lock);
- return count;
+ if (retval)
+ return retval;
+
+ return real_size;
}
-static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp,
- struct kobject *kobj, struct bin_attribute *attr, char *buf,
- loff_t off, size_t count)
+static ssize_t pyra_sysfs_write(struct file *fp, struct kobject *kobj,
+ void const *buf, loff_t off, size_t count,
+ size_t real_size, uint command)
{
struct device *dev =
container_of(kobj, struct device, kobj)->parent->parent;
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
+ struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
+ int retval;
- if (off >= sizeof(struct pyra_profile_buttons))
- return 0;
-
- if (off + count > sizeof(struct pyra_profile_buttons))
- count = sizeof(struct pyra_profile_buttons) - off;
+ if (off != 0 || count != real_size)
+ return -EINVAL;
mutex_lock(&pyra->pyra_lock);
- memcpy(buf, ((char const *)&pyra->profile_buttons[*(uint *)(attr->private)]) + off,
- count);
+ retval = roccat_common2_send_with_status(usb_dev, command, (void *)buf, real_size);
mutex_unlock(&pyra->pyra_lock);
- return count;
+ if (retval)
+ return retval;
+
+ return real_size;
}
-static ssize_t pyra_sysfs_write_profile_settings(struct file *fp,
- struct kobject *kobj, struct bin_attribute *attr, char *buf,
- loff_t off, size_t count)
-{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
- struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
- struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
- int retval = 0;
- int difference;
- int profile_number;
- struct pyra_profile_settings *profile_settings;
+#define PYRA_SYSFS_W(thingy, THINGY) \
+static ssize_t pyra_sysfs_write_ ## thingy(struct file *fp, \
+ struct kobject *kobj, struct bin_attribute *attr, char *buf, \
+ loff_t off, size_t count) \
+{ \
+ return pyra_sysfs_write(fp, kobj, buf, off, count, \
+ PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \
+}
- if (off != 0 || count != sizeof(struct pyra_profile_settings))
- return -EINVAL;
+#define PYRA_SYSFS_R(thingy, THINGY) \
+static ssize_t pyra_sysfs_read_ ## thingy(struct file *fp, \
+ struct kobject *kobj, struct bin_attribute *attr, char *buf, \
+ loff_t off, size_t count) \
+{ \
+ return pyra_sysfs_read(fp, kobj, buf, off, count, \
+ PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \
+}
- profile_number = ((struct pyra_profile_settings const *)buf)->number;
- profile_settings = &pyra->profile_settings[profile_number];
+#define PYRA_SYSFS_RW(thingy, THINGY) \
+PYRA_SYSFS_W(thingy, THINGY) \
+PYRA_SYSFS_R(thingy, THINGY)
- mutex_lock(&pyra->pyra_lock);
- difference = memcmp(buf, profile_settings,
- sizeof(struct pyra_profile_settings));
- if (difference) {
- retval = pyra_set_profile_settings(usb_dev,
- (struct pyra_profile_settings const *)buf);
- if (!retval)
- memcpy(profile_settings, buf,
- sizeof(struct pyra_profile_settings));
- }
- mutex_unlock(&pyra->pyra_lock);
+#define PYRA_BIN_ATTRIBUTE_RW(thingy, THINGY) \
+{ \
+ .attr = { .name = #thingy, .mode = 0660 }, \
+ .size = PYRA_SIZE_ ## THINGY, \
+ .read = pyra_sysfs_read_ ## thingy, \
+ .write = pyra_sysfs_write_ ## thingy \
+}
- if (retval)
- return retval;
+#define PYRA_BIN_ATTRIBUTE_R(thingy, THINGY) \
+{ \
+ .attr = { .name = #thingy, .mode = 0440 }, \
+ .size = PYRA_SIZE_ ## THINGY, \
+ .read = pyra_sysfs_read_ ## thingy, \
+}
- return sizeof(struct pyra_profile_settings);
+#define PYRA_BIN_ATTRIBUTE_W(thingy, THINGY) \
+{ \
+ .attr = { .name = #thingy, .mode = 0220 }, \
+ .size = PYRA_SIZE_ ## THINGY, \
+ .write = pyra_sysfs_write_ ## thingy \
}
-static ssize_t pyra_sysfs_write_profile_buttons(struct file *fp,
+PYRA_SYSFS_W(control, CONTROL)
+PYRA_SYSFS_RW(info, INFO)
+PYRA_SYSFS_RW(profile_settings, PROFILE_SETTINGS)
+PYRA_SYSFS_RW(profile_buttons, PROFILE_BUTTONS)
+PYRA_SYSFS_R(settings, SETTINGS)
+
+static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
struct device *dev =
container_of(kobj, struct device, kobj)->parent->parent;
- struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
- int retval = 0;
- int difference;
- int profile_number;
- struct pyra_profile_buttons *profile_buttons;
-
- if (off != 0 || count != sizeof(struct pyra_profile_buttons))
- return -EINVAL;
-
- profile_number = ((struct pyra_profile_buttons const *)buf)->number;
- profile_buttons = &pyra->profile_buttons[profile_number];
-
- mutex_lock(&pyra->pyra_lock);
- difference = memcmp(buf, profile_buttons,
- sizeof(struct pyra_profile_buttons));
- if (difference) {
- retval = pyra_set_profile_buttons(usb_dev,
- (struct pyra_profile_buttons const *)buf);
- if (!retval)
- memcpy(profile_buttons, buf,
- sizeof(struct pyra_profile_buttons));
- }
- mutex_unlock(&pyra->pyra_lock);
+ ssize_t retval;
+ retval = pyra_send_control(usb_dev, *(uint *)(attr->private),
+ PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
if (retval)
return retval;
- return sizeof(struct pyra_profile_buttons);
+ return pyra_sysfs_read(fp, kobj, buf, off, count,
+ PYRA_SIZE_PROFILE_SETTINGS,
+ PYRA_COMMAND_PROFILE_SETTINGS);
}
-static ssize_t pyra_sysfs_read_settings(struct file *fp,
+static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
struct device *dev =
container_of(kobj, struct device, kobj)->parent->parent;
- struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
-
- if (off >= sizeof(struct pyra_settings))
- return 0;
-
- if (off + count > sizeof(struct pyra_settings))
- count = sizeof(struct pyra_settings) - off;
+ struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
+ ssize_t retval;
- mutex_lock(&pyra->pyra_lock);
- memcpy(buf, ((char const *)&pyra->settings) + off, count);
- mutex_unlock(&pyra->pyra_lock);
+ retval = pyra_send_control(usb_dev, *(uint *)(attr->private),
+ PYRA_CONTROL_REQUEST_PROFILE_BUTTONS);
+ if (retval)
+ return retval;
- return count;
+ return pyra_sysfs_read(fp, kobj, buf, off, count,
+ PYRA_SIZE_PROFILE_BUTTONS,
+ PYRA_COMMAND_PROFILE_BUTTONS);
}
static ssize_t pyra_sysfs_write_settings(struct file *fp,
@@ -266,35 +230,32 @@ static ssize_t pyra_sysfs_write_settings(struct file *fp,
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval = 0;
- int difference;
struct pyra_roccat_report roccat_report;
+ struct pyra_settings const *settings;
- if (off != 0 || count != sizeof(struct pyra_settings))
+ if (off != 0 || count != PYRA_SIZE_SETTINGS)
return -EINVAL;
mutex_lock(&pyra->pyra_lock);
- difference = memcmp(buf, &pyra->settings, sizeof(struct pyra_settings));
- if (difference) {
- retval = pyra_set_settings(usb_dev,
- (struct pyra_settings const *)buf);
- if (retval) {
- mutex_unlock(&pyra->pyra_lock);
- return retval;
- }
-
- memcpy(&pyra->settings, buf,
- sizeof(struct pyra_settings));
- profile_activated(pyra, pyra->settings.startup_profile);
+ settings = (struct pyra_settings const *)buf;
- roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2;
- roccat_report.value = pyra->settings.startup_profile + 1;
- roccat_report.key = 0;
- roccat_report_event(pyra->chrdev_minor,
- (uint8_t const *)&roccat_report);
+ retval = pyra_set_settings(usb_dev, settings);
+ if (retval) {
+ mutex_unlock(&pyra->pyra_lock);
+ return retval;
}
+
+ profile_activated(pyra, settings->startup_profile);
+
+ roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2;
+ roccat_report.value = settings->startup_profile + 1;
+ roccat_report.key = 0;
+ roccat_report_event(pyra->chrdev_minor,
+ (uint8_t const *)&roccat_report);
+
mutex_unlock(&pyra->pyra_lock);
- return sizeof(struct pyra_settings);
+ return PYRA_SIZE_SETTINGS;
}
@@ -311,23 +272,34 @@ static ssize_t pyra_sysfs_show_actual_profile(struct device *dev,
{
struct pyra_device *pyra =
hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
- return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_profile);
+ struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
+ struct pyra_settings settings;
+
+ mutex_lock(&pyra->pyra_lock);
+ roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS,
+ &settings, PYRA_SIZE_SETTINGS);
+ mutex_unlock(&pyra->pyra_lock);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", settings.startup_profile);
}
static ssize_t pyra_sysfs_show_firmware_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct pyra_device *pyra =
- hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
- return snprintf(buf, PAGE_SIZE, "%d\n", pyra->firmware_version);
-}
+ struct pyra_device *pyra;
+ struct usb_device *usb_dev;
+ struct pyra_info info;
-static ssize_t pyra_sysfs_show_startup_profile(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct pyra_device *pyra =
- hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
- return snprintf(buf, PAGE_SIZE, "%d\n", pyra->settings.startup_profile);
+ dev = dev->parent->parent;
+ pyra = hid_get_drvdata(dev_get_drvdata(dev));
+ usb_dev = interface_to_usbdev(to_usb_interface(dev));
+
+ mutex_lock(&pyra->pyra_lock);
+ roccat_common2_receive(usb_dev, PYRA_COMMAND_INFO,
+ &info, PYRA_SIZE_INFO);
+ mutex_unlock(&pyra->pyra_lock);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version);
}
static struct device_attribute pyra_attributes[] = {
@@ -336,105 +308,88 @@ static struct device_attribute pyra_attributes[] = {
__ATTR(firmware_version, 0440,
pyra_sysfs_show_firmware_version, NULL),
__ATTR(startup_profile, 0440,
- pyra_sysfs_show_startup_profile, NULL),
+ pyra_sysfs_show_actual_profile, NULL),
__ATTR_NULL
};
static struct bin_attribute pyra_bin_attributes[] = {
- {
- .attr = { .name = "profile_settings", .mode = 0220 },
- .size = sizeof(struct pyra_profile_settings),
- .write = pyra_sysfs_write_profile_settings
- },
+ PYRA_BIN_ATTRIBUTE_W(control, CONTROL),
+ PYRA_BIN_ATTRIBUTE_RW(info, INFO),
+ PYRA_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS),
+ PYRA_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS),
+ PYRA_BIN_ATTRIBUTE_RW(settings, SETTINGS),
{
.attr = { .name = "profile1_settings", .mode = 0440 },
- .size = sizeof(struct pyra_profile_settings),
+ .size = PYRA_SIZE_PROFILE_SETTINGS,
.read = pyra_sysfs_read_profilex_settings,
.private = &profile_numbers[0]
},
{
.attr = { .name = "profile2_settings", .mode = 0440 },
- .size = sizeof(struct pyra_profile_settings),
+ .size = PYRA_SIZE_PROFILE_SETTINGS,
.read = pyra_sysfs_read_profilex_settings,
.private = &profile_numbers[1]
},
{
.attr = { .name = "profile3_settings", .mode = 0440 },
- .size = sizeof(struct pyra_profile_settings),
+ .size = PYRA_SIZE_PROFILE_SETTINGS,
.read = pyra_sysfs_read_profilex_settings,
.private = &profile_numbers[2]
},
{
.attr = { .name = "profile4_settings", .mode = 0440 },
- .size = sizeof(struct pyra_profile_settings),
+ .size = PYRA_SIZE_PROFILE_SETTINGS,
.read = pyra_sysfs_read_profilex_settings,
.private = &profile_numbers[3]
},
{
.attr = { .name = "profile5_settings", .mode = 0440 },
- .size = sizeof(struct pyra_profile_settings),
+ .size = PYRA_SIZE_PROFILE_SETTINGS,
.read = pyra_sysfs_read_profilex_settings,
.private = &profile_numbers[4]
},
{
- .attr = { .name = "profile_buttons", .mode = 0220 },
- .size = sizeof(struct pyra_profile_buttons),
- .write = pyra_sysfs_write_profile_buttons
- },
- {
.attr = { .name = "profile1_buttons", .mode = 0440 },
- .size = sizeof(struct pyra_profile_buttons),
+ .size = PYRA_SIZE_PROFILE_BUTTONS,
.read = pyra_sysfs_read_profilex_buttons,
.private = &profile_numbers[0]
},
{
.attr = { .name = "profile2_buttons", .mode = 0440 },
- .size = sizeof(struct pyra_profile_buttons),
+ .size = PYRA_SIZE_PROFILE_BUTTONS,
.read = pyra_sysfs_read_profilex_buttons,
.private = &profile_numbers[1]
},
{
.attr = { .name = "profile3_buttons", .mode = 0440 },
- .size = sizeof(struct pyra_profile_buttons),
+ .size = PYRA_SIZE_PROFILE_BUTTONS,
.read = pyra_sysfs_read_profilex_buttons,
.private = &profile_numbers[2]
},
{
.attr = { .name = "profile4_buttons", .mode = 0440 },
- .size = sizeof(struct pyra_profile_buttons),
+ .size = PYRA_SIZE_PROFILE_BUTTONS,
.read = pyra_sysfs_read_profilex_buttons,
.private = &profile_numbers[3]
},
{
.attr = { .name = "profile5_buttons", .mode = 0440 },
- .size = sizeof(struct pyra_profile_buttons),
+ .size = PYRA_SIZE_PROFILE_BUTTONS,
.read = pyra_sysfs_read_profilex_buttons,
.private = &profile_numbers[4]
},
- {
- .attr = { .name = "settings", .mode = 0660 },
- .size = sizeof(struct pyra_settings),
- .read = pyra_sysfs_read_settings,
- .write = pyra_sysfs_write_settings
- },
__ATTR_NULL
};
static int pyra_init_pyra_device_struct(struct usb_device *usb_dev,
struct pyra_device *pyra)
{
- struct pyra_info info;
+ struct pyra_settings settings;
int retval, i;
mutex_init(&pyra->pyra_lock);
- retval = pyra_get_info(usb_dev, &info);
- if (retval)
- return retval;
-
- pyra->firmware_version = info.firmware_version;
-
- retval = pyra_get_settings(usb_dev, &pyra->settings);
+ retval = pyra_get_settings(usb_dev, &settings);
if (retval)
return retval;
@@ -443,14 +398,9 @@ static int pyra_init_pyra_device_struct(struct usb_device *usb_dev,
&pyra->profile_settings[i], i);
if (retval)
return retval;
-
- retval = pyra_get_profile_buttons(usb_dev,
- &pyra->profile_buttons[i], i);
- if (retval)
- return retval;
}
- profile_activated(pyra, pyra->settings.startup_profile);
+ profile_activated(pyra, settings.startup_profile);
return 0;
}
diff --git a/drivers/hid/hid-roccat-pyra.h b/drivers/hid/hid-roccat-pyra.h
index eada7830fa9..beedcf001ce 100644
--- a/drivers/hid/hid-roccat-pyra.h
+++ b/drivers/hid/hid-roccat-pyra.h
@@ -14,11 +14,13 @@
#include <linux/types.h>
-struct pyra_b {
- uint8_t command; /* PYRA_COMMAND_B */
- uint8_t size; /* always 3 */
- uint8_t unknown; /* 1 */
-} __attribute__ ((__packed__));
+enum {
+ PYRA_SIZE_CONTROL = 0x03,
+ PYRA_SIZE_INFO = 0x06,
+ PYRA_SIZE_PROFILE_SETTINGS = 0x0d,
+ PYRA_SIZE_PROFILE_BUTTONS = 0x13,
+ PYRA_SIZE_SETTINGS = 0x03,
+};
enum pyra_control_requests {
PYRA_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10,
@@ -46,14 +48,6 @@ struct pyra_profile_settings {
uint16_t checksum; /* byte sum */
} __attribute__ ((__packed__));
-struct pyra_profile_buttons {
- uint8_t command; /* PYRA_COMMAND_PROFILE_BUTTONS */
- uint8_t size; /* always 0x13 */
- uint8_t number; /* Range 0-4 */
- uint8_t buttons[14];
- uint16_t checksum; /* byte sum */
-} __attribute__ ((__packed__));
-
struct pyra_info {
uint8_t command; /* PYRA_COMMAND_INFO */
uint8_t size; /* always 6 */
@@ -64,6 +58,7 @@ struct pyra_info {
} __attribute__ ((__packed__));
enum pyra_commands {
+ PYRA_COMMAND_CONTROL = 0x4,
PYRA_COMMAND_SETTINGS = 0x5,
PYRA_COMMAND_PROFILE_SETTINGS = 0x6,
PYRA_COMMAND_PROFILE_BUTTONS = 0x7,
@@ -148,13 +143,10 @@ struct pyra_roccat_report {
struct pyra_device {
int actual_profile;
int actual_cpi;
- int firmware_version;
int roccat_claimed;
int chrdev_minor;
struct mutex pyra_lock;
- struct pyra_settings settings;
struct pyra_profile_settings profile_settings[5];
- struct pyra_profile_buttons profile_buttons[5];
};
#endif
diff --git a/drivers/hid/hid-roccat-savu.c b/drivers/hid/hid-roccat-savu.c
index 014afba407e..31747a29c09 100644
--- a/drivers/hid/hid-roccat-savu.c
+++ b/drivers/hid/hid-roccat-savu.c
@@ -120,7 +120,7 @@ SAVU_SYSFS_RW(profile, PROFILE)
SAVU_SYSFS_RW(general, GENERAL)
SAVU_SYSFS_RW(buttons, BUTTONS)
SAVU_SYSFS_RW(macro, MACRO)
-SAVU_SYSFS_R(info, INFO)
+SAVU_SYSFS_RW(info, INFO)
SAVU_SYSFS_RW(sensor, SENSOR)
static struct bin_attribute savu_bin_attributes[] = {
@@ -129,7 +129,7 @@ static struct bin_attribute savu_bin_attributes[] = {
SAVU_BIN_ATTRIBUTE_RW(general, GENERAL),
SAVU_BIN_ATTRIBUTE_RW(buttons, BUTTONS),
SAVU_BIN_ATTRIBUTE_RW(macro, MACRO),
- SAVU_BIN_ATTRIBUTE_R(info, INFO),
+ SAVU_BIN_ATTRIBUTE_RW(info, INFO),
SAVU_BIN_ATTRIBUTE_RW(sensor, SENSOR),
__ATTR_NULL
};
diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c
index d9d73e9163e..0bc58bd8d4f 100644
--- a/drivers/hid/hid-sensor-hub.c
+++ b/drivers/hid/hid-sensor-hub.c
@@ -82,23 +82,6 @@ struct hid_sensor_hub_callbacks_list {
void *priv;
};
-static int sensor_hub_check_for_sensor_page(struct hid_device *hdev)
-{
- int i;
- int ret = -EINVAL;
-
- for (i = 0; i < hdev->maxcollection; i++) {
- struct hid_collection *col = &hdev->collection[i];
- if (col->type == HID_COLLECTION_PHYSICAL &&
- (col->usage & HID_USAGE_PAGE) == HID_UP_SENSOR) {
- ret = 0;
- break;
- }
- }
-
- return ret;
-}
-
static struct hid_report *sensor_hub_report(int id, struct hid_device *hdev,
int dir)
{
@@ -437,9 +420,6 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
ptr = raw_data;
ptr++; /*Skip report id*/
- if (!report)
- goto err_report;
-
spin_lock_irqsave(&pdata->lock, flags);
for (i = 0; i < report->maxfield; ++i) {
@@ -485,7 +465,6 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
callback->pdev);
spin_unlock_irqrestore(&pdata->lock, flags);
-err_report:
return 1;
}
@@ -524,10 +503,6 @@ static int sensor_hub_probe(struct hid_device *hdev,
hid_err(hdev, "parse failed\n");
goto err_free;
}
- if (sensor_hub_check_for_sensor_page(hdev) < 0) {
- hid_err(hdev, "sensor page not found\n");
- goto err_free;
- }
INIT_LIST_HEAD(&hdev->inputs);
ret = hid_hw_start(hdev, 0);
@@ -630,16 +605,7 @@ static void sensor_hub_remove(struct hid_device *hdev)
}
static const struct hid_device_id sensor_hub_devices[] = {
- { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086,
- USB_DEVICE_ID_SENSOR_HUB_1020) },
- { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087,
- USB_DEVICE_ID_SENSOR_HUB_1020) },
- { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086,
- USB_DEVICE_ID_SENSOR_HUB_09FA) },
- { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087,
- USB_DEVICE_ID_SENSOR_HUB_09FA) },
- { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM,
- USB_DEVICE_ID_SENSOR_HUB_7014) },
+ { HID_DEVICE(BUS_USB, HID_GROUP_SENSOR_HUB, HID_ANY_ID, HID_ANY_ID) },
{ }
};
MODULE_DEVICE_TABLE(hid, sensor_hub_devices);
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index 7c47fc3f7b2..413a73187d3 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -57,10 +57,6 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count,
set_current_state(TASK_INTERRUPTIBLE);
while (list->head == list->tail) {
- if (file->f_flags & O_NONBLOCK) {
- ret = -EAGAIN;
- break;
- }
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
@@ -69,6 +65,10 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count,
ret = -EIO;
break;
}
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ break;
+ }
/* allow O_NONBLOCK to work well from other threads */
mutex_unlock(&list->read_mutex);
@@ -295,6 +295,13 @@ out:
}
+static int hidraw_fasync(int fd, struct file *file, int on)
+{
+ struct hidraw_list *list = file->private_data;
+
+ return fasync_helper(fd, file, on, &list->fasync);
+}
+
static int hidraw_release(struct inode * inode, struct file * file)
{
unsigned int minor = iminor(inode);
@@ -438,6 +445,7 @@ static const struct file_operations hidraw_ops = {
.open = hidraw_open,
.release = hidraw_release,
.unlocked_ioctl = hidraw_ioctl,
+ .fasync = hidraw_fasync,
#ifdef CONFIG_COMPAT
.compat_ioctl = hidraw_ioctl,
#endif
diff --git a/drivers/hid/i2c-hid/Kconfig b/drivers/hid/i2c-hid/Kconfig
new file mode 100644
index 00000000000..b66617a020b
--- /dev/null
+++ b/drivers/hid/i2c-hid/Kconfig
@@ -0,0 +1,18 @@
+menu "I2C HID support"
+ depends on I2C
+
+config I2C_HID
+ tristate "HID over I2C transport layer"
+ default n
+ depends on I2C && INPUT
+ select HID
+ ---help---
+ Say Y here if you use a keyboard, a touchpad, a touchscreen, or any
+ other HID based devices which is connected to your computer via I2C.
+
+ If unsure, say N.
+
+ This support is also available as a module. If so, the module
+ will be called i2c-hid.
+
+endmenu
diff --git a/drivers/hid/i2c-hid/Makefile b/drivers/hid/i2c-hid/Makefile
new file mode 100644
index 00000000000..832d8f9aaba
--- /dev/null
+++ b/drivers/hid/i2c-hid/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the I2C input drivers
+#
+
+obj-$(CONFIG_I2C_HID) += i2c-hid.o
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
new file mode 100644
index 00000000000..9ef222442ca
--- /dev/null
+++ b/drivers/hid/i2c-hid/i2c-hid.c
@@ -0,0 +1,979 @@
+/*
+ * HID over I2C protocol implementation
+ *
+ * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+ * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
+ * Copyright (c) 2012 Red Hat, Inc
+ *
+ * This code is partly based on "USB HID support for Linux":
+ *
+ * Copyright (c) 1999 Andreas Gal
+ * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ * Copyright (c) 2007-2008 Oliver Neukum
+ * Copyright (c) 2006-2010 Jiri Kosina
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/hid.h>
+#include <linux/mutex.h>
+
+#include <linux/i2c/i2c-hid.h>
+
+/* flags */
+#define I2C_HID_STARTED (1 << 0)
+#define I2C_HID_RESET_PENDING (1 << 1)
+#define I2C_HID_READ_PENDING (1 << 2)
+
+#define I2C_HID_PWR_ON 0x00
+#define I2C_HID_PWR_SLEEP 0x01
+
+/* debug option */
+static bool debug;
+module_param(debug, bool, 0444);
+MODULE_PARM_DESC(debug, "print a lot of debug information");
+
+#define i2c_hid_dbg(ihid, fmt, arg...) \
+do { \
+ if (debug) \
+ dev_printk(KERN_DEBUG, &(ihid)->client->dev, fmt, ##arg); \
+} while (0)
+
+struct i2c_hid_desc {
+ __le16 wHIDDescLength;
+ __le16 bcdVersion;
+ __le16 wReportDescLength;
+ __le16 wReportDescRegister;
+ __le16 wInputRegister;
+ __le16 wMaxInputLength;
+ __le16 wOutputRegister;
+ __le16 wMaxOutputLength;
+ __le16 wCommandRegister;
+ __le16 wDataRegister;
+ __le16 wVendorID;
+ __le16 wProductID;
+ __le16 wVersionID;
+ __le32 reserved;
+} __packed;
+
+struct i2c_hid_cmd {
+ unsigned int registerIndex;
+ __u8 opcode;
+ unsigned int length;
+ bool wait;
+};
+
+union command {
+ u8 data[0];
+ struct cmd {
+ __le16 reg;
+ __u8 reportTypeID;
+ __u8 opcode;
+ } __packed c;
+};
+
+#define I2C_HID_CMD(opcode_) \
+ .opcode = opcode_, .length = 4, \
+ .registerIndex = offsetof(struct i2c_hid_desc, wCommandRegister)
+
+/* fetch HID descriptor */
+static const struct i2c_hid_cmd hid_descr_cmd = { .length = 2 };
+/* fetch report descriptors */
+static const struct i2c_hid_cmd hid_report_descr_cmd = {
+ .registerIndex = offsetof(struct i2c_hid_desc,
+ wReportDescRegister),
+ .opcode = 0x00,
+ .length = 2 };
+/* commands */
+static const struct i2c_hid_cmd hid_reset_cmd = { I2C_HID_CMD(0x01),
+ .wait = true };
+static const struct i2c_hid_cmd hid_get_report_cmd = { I2C_HID_CMD(0x02) };
+static const struct i2c_hid_cmd hid_set_report_cmd = { I2C_HID_CMD(0x03) };
+static const struct i2c_hid_cmd hid_set_power_cmd = { I2C_HID_CMD(0x08) };
+
+/*
+ * These definitions are not used here, but are defined by the spec.
+ * Keeping them here for documentation purposes.
+ *
+ * static const struct i2c_hid_cmd hid_get_idle_cmd = { I2C_HID_CMD(0x04) };
+ * static const struct i2c_hid_cmd hid_set_idle_cmd = { I2C_HID_CMD(0x05) };
+ * static const struct i2c_hid_cmd hid_get_protocol_cmd = { I2C_HID_CMD(0x06) };
+ * static const struct i2c_hid_cmd hid_set_protocol_cmd = { I2C_HID_CMD(0x07) };
+ */
+
+static DEFINE_MUTEX(i2c_hid_open_mut);
+
+/* The main device structure */
+struct i2c_hid {
+ struct i2c_client *client; /* i2c client */
+ struct hid_device *hid; /* pointer to corresponding HID dev */
+ union {
+ __u8 hdesc_buffer[sizeof(struct i2c_hid_desc)];
+ struct i2c_hid_desc hdesc; /* the HID Descriptor */
+ };
+ __le16 wHIDDescRegister; /* location of the i2c
+ * register of the HID
+ * descriptor. */
+ unsigned int bufsize; /* i2c buffer size */
+ char *inbuf; /* Input buffer */
+ char *cmdbuf; /* Command buffer */
+ char *argsbuf; /* Command arguments buffer */
+
+ unsigned long flags; /* device flags */
+
+ wait_queue_head_t wait; /* For waiting the interrupt */
+};
+
+static int __i2c_hid_command(struct i2c_client *client,
+ const struct i2c_hid_cmd *command, u8 reportID,
+ u8 reportType, u8 *args, int args_len,
+ unsigned char *buf_recv, int data_len)
+{
+ struct i2c_hid *ihid = i2c_get_clientdata(client);
+ union command *cmd = (union command *)ihid->cmdbuf;
+ int ret;
+ struct i2c_msg msg[2];
+ int msg_num = 1;
+
+ int length = command->length;
+ bool wait = command->wait;
+ unsigned int registerIndex = command->registerIndex;
+
+ /* special case for hid_descr_cmd */
+ if (command == &hid_descr_cmd) {
+ cmd->c.reg = ihid->wHIDDescRegister;
+ } else {
+ cmd->data[0] = ihid->hdesc_buffer[registerIndex];
+ cmd->data[1] = ihid->hdesc_buffer[registerIndex + 1];
+ }
+
+ if (length > 2) {
+ cmd->c.opcode = command->opcode;
+ cmd->c.reportTypeID = reportID | reportType << 4;
+ }
+
+ memcpy(cmd->data + length, args, args_len);
+ length += args_len;
+
+ i2c_hid_dbg(ihid, "%s: cmd=%*ph\n", __func__, length, cmd->data);
+
+ msg[0].addr = client->addr;
+ msg[0].flags = client->flags & I2C_M_TEN;
+ msg[0].len = length;
+ msg[0].buf = cmd->data;
+ if (data_len > 0) {
+ msg[1].addr = client->addr;
+ msg[1].flags = client->flags & I2C_M_TEN;
+ msg[1].flags |= I2C_M_RD;
+ msg[1].len = data_len;
+ msg[1].buf = buf_recv;
+ msg_num = 2;
+ set_bit(I2C_HID_READ_PENDING, &ihid->flags);
+ }
+
+ if (wait)
+ set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
+
+ ret = i2c_transfer(client->adapter, msg, msg_num);
+
+ if (data_len > 0)
+ clear_bit(I2C_HID_READ_PENDING, &ihid->flags);
+
+ if (ret != msg_num)
+ return ret < 0 ? ret : -EIO;
+
+ ret = 0;
+
+ if (wait) {
+ i2c_hid_dbg(ihid, "%s: waiting...\n", __func__);
+ if (!wait_event_timeout(ihid->wait,
+ !test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
+ msecs_to_jiffies(5000)))
+ ret = -ENODATA;
+ i2c_hid_dbg(ihid, "%s: finished.\n", __func__);
+ }
+
+ return ret;
+}
+
+static int i2c_hid_command(struct i2c_client *client,
+ const struct i2c_hid_cmd *command,
+ unsigned char *buf_recv, int data_len)
+{
+ return __i2c_hid_command(client, command, 0, 0, NULL, 0,
+ buf_recv, data_len);
+}
+
+static int i2c_hid_get_report(struct i2c_client *client, u8 reportType,
+ u8 reportID, unsigned char *buf_recv, int data_len)
+{
+ struct i2c_hid *ihid = i2c_get_clientdata(client);
+ u8 args[3];
+ int ret;
+ int args_len = 0;
+ u16 readRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
+
+ i2c_hid_dbg(ihid, "%s\n", __func__);
+
+ if (reportID >= 0x0F) {
+ args[args_len++] = reportID;
+ reportID = 0x0F;
+ }
+
+ args[args_len++] = readRegister & 0xFF;
+ args[args_len++] = readRegister >> 8;
+
+ ret = __i2c_hid_command(client, &hid_get_report_cmd, reportID,
+ reportType, args, args_len, buf_recv, data_len);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to retrieve report from device.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,
+ u8 reportID, unsigned char *buf, size_t data_len)
+{
+ struct i2c_hid *ihid = i2c_get_clientdata(client);
+ u8 *args = ihid->argsbuf;
+ int ret;
+ u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
+
+ /* hidraw already checked that data_len < HID_MAX_BUFFER_SIZE */
+ u16 size = 2 /* size */ +
+ (reportID ? 1 : 0) /* reportID */ +
+ data_len /* buf */;
+ int args_len = (reportID >= 0x0F ? 1 : 0) /* optional third byte */ +
+ 2 /* dataRegister */ +
+ size /* args */;
+ int index = 0;
+
+ i2c_hid_dbg(ihid, "%s\n", __func__);
+
+ if (reportID >= 0x0F) {
+ args[index++] = reportID;
+ reportID = 0x0F;
+ }
+
+ args[index++] = dataRegister & 0xFF;
+ args[index++] = dataRegister >> 8;
+
+ args[index++] = size & 0xFF;
+ args[index++] = size >> 8;
+
+ if (reportID)
+ args[index++] = reportID;
+
+ memcpy(&args[index], buf, data_len);
+
+ ret = __i2c_hid_command(client, &hid_set_report_cmd, reportID,
+ reportType, args, args_len, NULL, 0);
+ if (ret) {
+ dev_err(&client->dev, "failed to set a report to device.\n");
+ return ret;
+ }
+
+ return data_len;
+}
+
+static int i2c_hid_set_power(struct i2c_client *client, int power_state)
+{
+ struct i2c_hid *ihid = i2c_get_clientdata(client);
+ int ret;
+
+ i2c_hid_dbg(ihid, "%s\n", __func__);
+
+ ret = __i2c_hid_command(client, &hid_set_power_cmd, power_state,
+ 0, NULL, 0, NULL, 0);
+ if (ret)
+ dev_err(&client->dev, "failed to change power setting.\n");
+
+ return ret;
+}
+
+static int i2c_hid_hwreset(struct i2c_client *client)
+{
+ struct i2c_hid *ihid = i2c_get_clientdata(client);
+ int ret;
+
+ i2c_hid_dbg(ihid, "%s\n", __func__);
+
+ ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
+ if (ret)
+ return ret;
+
+ i2c_hid_dbg(ihid, "resetting...\n");
+
+ ret = i2c_hid_command(client, &hid_reset_cmd, NULL, 0);
+ if (ret) {
+ dev_err(&client->dev, "failed to reset device.\n");
+ i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void i2c_hid_get_input(struct i2c_hid *ihid)
+{
+ int ret, ret_size;
+ int size = le16_to_cpu(ihid->hdesc.wMaxInputLength);
+
+ ret = i2c_master_recv(ihid->client, ihid->inbuf, size);
+ if (ret != size) {
+ if (ret < 0)
+ return;
+
+ dev_err(&ihid->client->dev, "%s: got %d data instead of %d\n",
+ __func__, ret, size);
+ return;
+ }
+
+ ret_size = ihid->inbuf[0] | ihid->inbuf[1] << 8;
+
+ if (!ret_size) {
+ /* host or device initiated RESET completed */
+ if (test_and_clear_bit(I2C_HID_RESET_PENDING, &ihid->flags))
+ wake_up(&ihid->wait);
+ return;
+ }
+
+ if (ret_size > size) {
+ dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n",
+ __func__, size, ret_size);
+ return;
+ }
+
+ i2c_hid_dbg(ihid, "input: %*ph\n", ret_size, ihid->inbuf);
+
+ if (test_bit(I2C_HID_STARTED, &ihid->flags))
+ hid_input_report(ihid->hid, HID_INPUT_REPORT, ihid->inbuf + 2,
+ ret_size - 2, 1);
+
+ return;
+}
+
+static irqreturn_t i2c_hid_irq(int irq, void *dev_id)
+{
+ struct i2c_hid *ihid = dev_id;
+
+ if (test_bit(I2C_HID_READ_PENDING, &ihid->flags))
+ return IRQ_HANDLED;
+
+ i2c_hid_get_input(ihid);
+
+ return IRQ_HANDLED;
+}
+
+static int i2c_hid_get_report_length(struct hid_report *report)
+{
+ return ((report->size - 1) >> 3) + 1 +
+ report->device->report_enum[report->type].numbered + 2;
+}
+
+static void i2c_hid_init_report(struct hid_report *report, u8 *buffer,
+ size_t bufsize)
+{
+ struct hid_device *hid = report->device;
+ struct i2c_client *client = hid->driver_data;
+ struct i2c_hid *ihid = i2c_get_clientdata(client);
+ unsigned int size, ret_size;
+
+ size = i2c_hid_get_report_length(report);
+ if (i2c_hid_get_report(client,
+ report->type == HID_FEATURE_REPORT ? 0x03 : 0x01,
+ report->id, buffer, size))
+ return;
+
+ i2c_hid_dbg(ihid, "report (len=%d): %*ph\n", size, size, ihid->inbuf);
+
+ ret_size = buffer[0] | (buffer[1] << 8);
+
+ if (ret_size != size) {
+ dev_err(&client->dev, "error in %s size:%d / ret_size:%d\n",
+ __func__, size, ret_size);
+ return;
+ }
+
+ /* hid->driver_lock is held as we are in probe function,
+ * we just need to setup the input fields, so using
+ * hid_report_raw_event is safe. */
+ hid_report_raw_event(hid, report->type, buffer + 2, size - 2, 1);
+}
+
+/*
+ * Initialize all reports
+ */
+static void i2c_hid_init_reports(struct hid_device *hid)
+{
+ struct hid_report *report;
+ struct i2c_client *client = hid->driver_data;
+ struct i2c_hid *ihid = i2c_get_clientdata(client);
+ u8 *inbuf = kzalloc(ihid->bufsize, GFP_KERNEL);
+
+ if (!inbuf) {
+ dev_err(&client->dev, "can not retrieve initial reports\n");
+ return;
+ }
+
+ list_for_each_entry(report,
+ &hid->report_enum[HID_INPUT_REPORT].report_list, list)
+ i2c_hid_init_report(report, inbuf, ihid->bufsize);
+
+ list_for_each_entry(report,
+ &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
+ i2c_hid_init_report(report, inbuf, ihid->bufsize);
+
+ kfree(inbuf);
+}
+
+/*
+ * Traverse the supplied list of reports and find the longest
+ */
+static void i2c_hid_find_max_report(struct hid_device *hid, unsigned int type,
+ unsigned int *max)
+{
+ struct hid_report *report;
+ unsigned int size;
+
+ /* We should not rely on wMaxInputLength, as some devices may set it to
+ * a wrong length. */
+ list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
+ size = i2c_hid_get_report_length(report);
+ if (*max < size)
+ *max = size;
+ }
+}
+
+static void i2c_hid_free_buffers(struct i2c_hid *ihid)
+{
+ kfree(ihid->inbuf);
+ kfree(ihid->argsbuf);
+ kfree(ihid->cmdbuf);
+ ihid->inbuf = NULL;
+ ihid->cmdbuf = NULL;
+ ihid->argsbuf = NULL;
+ ihid->bufsize = 0;
+}
+
+static int i2c_hid_alloc_buffers(struct i2c_hid *ihid, size_t report_size)
+{
+ /* the worst case is computed from the set_report command with a
+ * reportID > 15 and the maximum report length */
+ int args_len = sizeof(__u8) + /* optional ReportID byte */
+ sizeof(__u16) + /* data register */
+ sizeof(__u16) + /* size of the report */
+ report_size; /* report */
+
+ ihid->inbuf = kzalloc(report_size, GFP_KERNEL);
+ ihid->argsbuf = kzalloc(args_len, GFP_KERNEL);
+ ihid->cmdbuf = kzalloc(sizeof(union command) + args_len, GFP_KERNEL);
+
+ if (!ihid->inbuf || !ihid->argsbuf || !ihid->cmdbuf) {
+ i2c_hid_free_buffers(ihid);
+ return -ENOMEM;
+ }
+
+ ihid->bufsize = report_size;
+
+ return 0;
+}
+
+static int i2c_hid_get_raw_report(struct hid_device *hid,
+ unsigned char report_number, __u8 *buf, size_t count,
+ unsigned char report_type)
+{
+ struct i2c_client *client = hid->driver_data;
+ struct i2c_hid *ihid = i2c_get_clientdata(client);
+ size_t ret_count, ask_count;
+ int ret;
+
+ if (report_type == HID_OUTPUT_REPORT)
+ return -EINVAL;
+
+ /* +2 bytes to include the size of the reply in the query buffer */
+ ask_count = min(count + 2, (size_t)ihid->bufsize);
+
+ ret = i2c_hid_get_report(client,
+ report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
+ report_number, ihid->inbuf, ask_count);
+
+ if (ret < 0)
+ return ret;
+
+ ret_count = ihid->inbuf[0] | (ihid->inbuf[1] << 8);
+
+ if (ret_count <= 2)
+ return 0;
+
+ ret_count = min(ret_count, ask_count);
+
+ /* The query buffer contains the size, dropping it in the reply */
+ count = min(count, ret_count - 2);
+ memcpy(buf, ihid->inbuf + 2, count);
+
+ return count;
+}
+
+static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
+ size_t count, unsigned char report_type)
+{
+ struct i2c_client *client = hid->driver_data;
+ int report_id = buf[0];
+
+ if (report_type == HID_INPUT_REPORT)
+ return -EINVAL;
+
+ return i2c_hid_set_report(client,
+ report_type == HID_FEATURE_REPORT ? 0x03 : 0x02,
+ report_id, buf, count);
+}
+
+static int i2c_hid_parse(struct hid_device *hid)
+{
+ struct i2c_client *client = hid->driver_data;
+ struct i2c_hid *ihid = i2c_get_clientdata(client);
+ struct i2c_hid_desc *hdesc = &ihid->hdesc;
+ unsigned int rsize;
+ char *rdesc;
+ int ret;
+ int tries = 3;
+
+ i2c_hid_dbg(ihid, "entering %s\n", __func__);
+
+ rsize = le16_to_cpu(hdesc->wReportDescLength);
+ if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
+ dbg_hid("weird size of report descriptor (%u)\n", rsize);
+ return -EINVAL;
+ }
+
+ do {
+ ret = i2c_hid_hwreset(client);
+ if (ret)
+ msleep(1000);
+ } while (tries-- > 0 && ret);
+
+ if (ret)
+ return ret;
+
+ rdesc = kzalloc(rsize, GFP_KERNEL);
+
+ if (!rdesc) {
+ dbg_hid("couldn't allocate rdesc memory\n");
+ return -ENOMEM;
+ }
+
+ i2c_hid_dbg(ihid, "asking HID report descriptor\n");
+
+ ret = i2c_hid_command(client, &hid_report_descr_cmd, rdesc, rsize);
+ if (ret) {
+ hid_err(hid, "reading report descriptor failed\n");
+ kfree(rdesc);
+ return -EIO;
+ }
+
+ i2c_hid_dbg(ihid, "Report Descriptor: %*ph\n", rsize, rdesc);
+
+ ret = hid_parse_report(hid, rdesc, rsize);
+ kfree(rdesc);
+ if (ret) {
+ dbg_hid("parsing report descriptor failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int i2c_hid_start(struct hid_device *hid)
+{
+ struct i2c_client *client = hid->driver_data;
+ struct i2c_hid *ihid = i2c_get_clientdata(client);
+ int ret;
+ unsigned int bufsize = HID_MIN_BUFFER_SIZE;
+
+ i2c_hid_find_max_report(hid, HID_INPUT_REPORT, &bufsize);
+ i2c_hid_find_max_report(hid, HID_OUTPUT_REPORT, &bufsize);
+ i2c_hid_find_max_report(hid, HID_FEATURE_REPORT, &bufsize);
+
+ if (bufsize > ihid->bufsize) {
+ i2c_hid_free_buffers(ihid);
+
+ ret = i2c_hid_alloc_buffers(ihid, bufsize);
+
+ if (ret)
+ return ret;
+ }
+
+ if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS))
+ i2c_hid_init_reports(hid);
+
+ return 0;
+}
+
+static void i2c_hid_stop(struct hid_device *hid)
+{
+ struct i2c_client *client = hid->driver_data;
+ struct i2c_hid *ihid = i2c_get_clientdata(client);
+
+ hid->claimed = 0;
+
+ i2c_hid_free_buffers(ihid);
+}
+
+static int i2c_hid_open(struct hid_device *hid)
+{
+ struct i2c_client *client = hid->driver_data;
+ struct i2c_hid *ihid = i2c_get_clientdata(client);
+ int ret = 0;
+
+ mutex_lock(&i2c_hid_open_mut);
+ if (!hid->open++) {
+ ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
+ if (ret) {
+ hid->open--;
+ goto done;
+ }
+ set_bit(I2C_HID_STARTED, &ihid->flags);
+ }
+done:
+ mutex_unlock(&i2c_hid_open_mut);
+ return ret;
+}
+
+static void i2c_hid_close(struct hid_device *hid)
+{
+ struct i2c_client *client = hid->driver_data;
+ struct i2c_hid *ihid = i2c_get_clientdata(client);
+
+ /* protecting hid->open to make sure we don't restart
+ * data acquistion due to a resumption we no longer
+ * care about
+ */
+ mutex_lock(&i2c_hid_open_mut);
+ if (!--hid->open) {
+ clear_bit(I2C_HID_STARTED, &ihid->flags);
+
+ /* Save some power */
+ i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
+ }
+ mutex_unlock(&i2c_hid_open_mut);
+}
+
+static int i2c_hid_power(struct hid_device *hid, int lvl)
+{
+ struct i2c_client *client = hid->driver_data;
+ struct i2c_hid *ihid = i2c_get_clientdata(client);
+ int ret = 0;
+
+ i2c_hid_dbg(ihid, "%s lvl:%d\n", __func__, lvl);
+
+ switch (lvl) {
+ case PM_HINT_FULLON:
+ ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
+ break;
+ case PM_HINT_NORMAL:
+ ret = i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
+ break;
+ }
+ return ret;
+}
+
+static int i2c_hid_hidinput_input_event(struct input_dev *dev,
+ unsigned int type, unsigned int code, int value)
+{
+ struct hid_device *hid = input_get_drvdata(dev);
+ struct hid_field *field;
+ int offset;
+
+ if (type == EV_FF)
+ return input_ff_event(dev, type, code, value);
+
+ if (type != EV_LED)
+ return -1;
+
+ offset = hidinput_find_field(hid, type, code, &field);
+
+ if (offset == -1) {
+ hid_warn(dev, "event field not found\n");
+ return -1;
+ }
+
+ return hid_set_field(field, offset, value);
+}
+
+static struct hid_ll_driver i2c_hid_ll_driver = {
+ .parse = i2c_hid_parse,
+ .start = i2c_hid_start,
+ .stop = i2c_hid_stop,
+ .open = i2c_hid_open,
+ .close = i2c_hid_close,
+ .power = i2c_hid_power,
+ .hidinput_input_event = i2c_hid_hidinput_input_event,
+};
+
+static int __devinit i2c_hid_init_irq(struct i2c_client *client)
+{
+ struct i2c_hid *ihid = i2c_get_clientdata(client);
+ int ret;
+
+ dev_dbg(&client->dev, "Requesting IRQ: %d\n", client->irq);
+
+ ret = request_threaded_irq(client->irq, NULL, i2c_hid_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ client->name, ihid);
+ if (ret < 0) {
+ dev_warn(&client->dev,
+ "Could not register for %s interrupt, irq = %d,"
+ " ret = %d\n",
+ client->name, client->irq, ret);
+
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devinit i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
+{
+ struct i2c_client *client = ihid->client;
+ struct i2c_hid_desc *hdesc = &ihid->hdesc;
+ unsigned int dsize;
+ int ret;
+
+ /* Fetch the length of HID description, retrieve the 4 first bytes:
+ * bytes 0-1 -> length
+ * bytes 2-3 -> bcdVersion (has to be 1.00) */
+ ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer, 4);
+
+ i2c_hid_dbg(ihid, "%s, ihid->hdesc_buffer: %*ph\n",
+ __func__, 4, ihid->hdesc_buffer);
+
+ if (ret) {
+ dev_err(&client->dev,
+ "unable to fetch the size of HID descriptor (ret=%d)\n",
+ ret);
+ return -ENODEV;
+ }
+
+ dsize = le16_to_cpu(hdesc->wHIDDescLength);
+ /*
+ * the size of the HID descriptor should at least contain
+ * its size and the bcdVersion (4 bytes), and should not be greater
+ * than sizeof(struct i2c_hid_desc) as we directly fill this struct
+ * through i2c_hid_command.
+ */
+ if (dsize < 4 || dsize > sizeof(struct i2c_hid_desc)) {
+ dev_err(&client->dev, "weird size of HID descriptor (%u)\n",
+ dsize);
+ return -ENODEV;
+ }
+
+ /* check bcdVersion == 1.0 */
+ if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) {
+ dev_err(&client->dev,
+ "unexpected HID descriptor bcdVersion (0x%04hx)\n",
+ le16_to_cpu(hdesc->bcdVersion));
+ return -ENODEV;
+ }
+
+ i2c_hid_dbg(ihid, "Fetching the HID descriptor\n");
+
+ ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer,
+ dsize);
+ if (ret) {
+ dev_err(&client->dev, "hid_descr_cmd Fail\n");
+ return -ENODEV;
+ }
+
+ i2c_hid_dbg(ihid, "HID Descriptor: %*ph\n", dsize, ihid->hdesc_buffer);
+
+ return 0;
+}
+
+static int __devinit i2c_hid_probe(struct i2c_client *client,
+ const struct i2c_device_id *dev_id)
+{
+ int ret;
+ struct i2c_hid *ihid;
+ struct hid_device *hid;
+ __u16 hidRegister;
+ struct i2c_hid_platform_data *platform_data = client->dev.platform_data;
+
+ dbg_hid("HID probe called for i2c 0x%02x\n", client->addr);
+
+ if (!platform_data) {
+ dev_err(&client->dev, "HID register address not provided\n");
+ return -EINVAL;
+ }
+
+ if (!client->irq) {
+ dev_err(&client->dev,
+ "HID over i2c has not been provided an Int IRQ\n");
+ return -EINVAL;
+ }
+
+ ihid = kzalloc(sizeof(struct i2c_hid), GFP_KERNEL);
+ if (!ihid)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, ihid);
+
+ ihid->client = client;
+
+ hidRegister = platform_data->hid_descriptor_address;
+ ihid->wHIDDescRegister = cpu_to_le16(hidRegister);
+
+ init_waitqueue_head(&ihid->wait);
+
+ /* we need to allocate the command buffer without knowing the maximum
+ * size of the reports. Let's use HID_MIN_BUFFER_SIZE, then we do the
+ * real computation later. */
+ ret = i2c_hid_alloc_buffers(ihid, HID_MIN_BUFFER_SIZE);
+ if (ret < 0)
+ goto err;
+
+ ret = i2c_hid_fetch_hid_descriptor(ihid);
+ if (ret < 0)
+ goto err;
+
+ ret = i2c_hid_init_irq(client);
+ if (ret < 0)
+ goto err;
+
+ hid = hid_allocate_device();
+ if (IS_ERR(hid)) {
+ ret = PTR_ERR(hid);
+ goto err_irq;
+ }
+
+ ihid->hid = hid;
+
+ hid->driver_data = client;
+ hid->ll_driver = &i2c_hid_ll_driver;
+ hid->hid_get_raw_report = i2c_hid_get_raw_report;
+ hid->hid_output_raw_report = i2c_hid_output_raw_report;
+ hid->dev.parent = &client->dev;
+ hid->bus = BUS_I2C;
+ hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);
+ hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID);
+ hid->product = le16_to_cpu(ihid->hdesc.wProductID);
+
+ snprintf(hid->name, sizeof(hid->name), "%s %04hX:%04hX",
+ client->name, hid->vendor, hid->product);
+
+ ret = hid_add_device(hid);
+ if (ret) {
+ if (ret != -ENODEV)
+ hid_err(client, "can't add hid device: %d\n", ret);
+ goto err_mem_free;
+ }
+
+ return 0;
+
+err_mem_free:
+ hid_destroy_device(hid);
+
+err_irq:
+ free_irq(client->irq, ihid);
+
+err:
+ i2c_hid_free_buffers(ihid);
+ kfree(ihid);
+ return ret;
+}
+
+static int __devexit i2c_hid_remove(struct i2c_client *client)
+{
+ struct i2c_hid *ihid = i2c_get_clientdata(client);
+ struct hid_device *hid;
+
+ hid = ihid->hid;
+ hid_destroy_device(hid);
+
+ free_irq(client->irq, ihid);
+
+ if (ihid->bufsize)
+ i2c_hid_free_buffers(ihid);
+
+ kfree(ihid);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int i2c_hid_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(client->irq);
+
+ /* Save some power */
+ i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
+
+ return 0;
+}
+
+static int i2c_hid_resume(struct device *dev)
+{
+ int ret;
+ struct i2c_client *client = to_i2c_client(dev);
+
+ ret = i2c_hid_hwreset(client);
+ if (ret)
+ return ret;
+
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(client->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(i2c_hid_pm, i2c_hid_suspend, i2c_hid_resume);
+
+static const struct i2c_device_id i2c_hid_id_table[] = {
+ { "hid", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, i2c_hid_id_table);
+
+
+static struct i2c_driver i2c_hid_driver = {
+ .driver = {
+ .name = "i2c_hid",
+ .owner = THIS_MODULE,
+ .pm = &i2c_hid_pm,
+ },
+
+ .probe = i2c_hid_probe,
+ .remove = __devexit_p(i2c_hid_remove),
+
+ .id_table = i2c_hid_id_table,
+};
+
+module_i2c_driver(i2c_hid_driver);
+
+MODULE_DESCRIPTION("HID over I2C core driver");
+MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 11c7932dc7e..ac9e3522825 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -72,6 +72,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS },
@@ -79,9 +80,11 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_SIGMATEL, USB_DEVICE_ID_SIGMATEL_STMP3780, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_1, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_2, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_TPV, USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U, HID_QUIRK_MULTI_INPUT },
diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c
index 14599e25679..87bd64959a9 100644
--- a/drivers/hid/usbhid/hiddev.c
+++ b/drivers/hid/usbhid/hiddev.c
@@ -361,10 +361,6 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun
prepare_to_wait(&list->hiddev->wait, &wait, TASK_INTERRUPTIBLE);
while (list->head == list->tail) {
- if (file->f_flags & O_NONBLOCK) {
- retval = -EAGAIN;
- break;
- }
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
@@ -373,6 +369,10 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun
retval = -EIO;
break;
}
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
/* let O_NONBLOCK tasks run */
mutex_unlock(&list->thread_lock);
@@ -625,7 +625,7 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
case HIDIOCAPPLICATION:
- if (arg < 0 || arg >= hid->maxapplication)
+ if (arg >= hid->maxapplication)
break;
for (i = 0; i < hid->maxcollection; i++)
diff --git a/drivers/i2c/busses/i2c-nuc900.c b/drivers/i2c/busses/i2c-nuc900.c
index f41502ef3f5..a23b91b0b73 100644
--- a/drivers/i2c/busses/i2c-nuc900.c
+++ b/drivers/i2c/busses/i2c-nuc900.c
@@ -304,7 +304,7 @@ retry_write:
case STATE_READ:
/* we have a byte of data in the data register, do
- * something with it, and then work out wether we are
+ * something with it, and then work out whether we are
* going to do any more read/write
*/
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index 9d902725bac..b33d95ebc89 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -208,7 +208,7 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
if (msg->flags & I2C_M_REV_DIR_ADDR)
addr ^= 1;
- /* todo - check for wether ack wanted or not */
+ /* todo - check for whether ack wanted or not */
s3c24xx_i2c_enable_ack(i2c);
iiccon = readl(i2c->regs + S3C2410_IICCON);
@@ -397,7 +397,7 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
case STATE_READ:
/* we have a byte of data in the data register, do
- * something with it, and then work out wether we are
+ * something with it, and then work out whether we are
* going to do any more read/write
*/
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index a7568c34a1a..d789eea3216 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -345,17 +345,17 @@ static int find_gid_port(struct ib_device *device, union ib_gid *gid, u8 port_nu
err = ib_query_port(device, port_num, &props);
if (err)
- return 1;
+ return err;
for (i = 0; i < props.gid_tbl_len; ++i) {
err = ib_query_gid(device, port_num, i, &tmp);
if (err)
- return 1;
+ return err;
if (!memcmp(&tmp, gid, sizeof tmp))
return 0;
}
- return -EAGAIN;
+ return -EADDRNOTAVAIL;
}
static int cma_acquire_dev(struct rdma_id_private *id_priv)
@@ -388,8 +388,7 @@ static int cma_acquire_dev(struct rdma_id_private *id_priv)
if (!ret) {
id_priv->id.port_num = port;
goto out;
- } else if (ret == 1)
- break;
+ }
}
}
}
diff --git a/drivers/infiniband/hw/amso1100/c2_ae.c b/drivers/infiniband/hw/amso1100/c2_ae.c
index 32d34e88d5c..706cf97cbe8 100644
--- a/drivers/infiniband/hw/amso1100/c2_ae.c
+++ b/drivers/infiniband/hw/amso1100/c2_ae.c
@@ -311,6 +311,7 @@ void c2_ae_event(struct c2_dev *c2dev, u32 mq_index)
if (cq->ibcq.event_handler)
cq->ibcq.event_handler(&ib_event,
cq->ibcq.cq_context);
+ break;
}
default:
diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c
index aaf88ef9409..3e094cd6a0e 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_cm.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c
@@ -128,9 +128,8 @@ static void stop_ep_timer(struct iwch_ep *ep)
{
PDBG("%s ep %p\n", __func__, ep);
if (!timer_pending(&ep->timer)) {
- printk(KERN_ERR "%s timer stopped when its not running! ep %p state %u\n",
+ WARN(1, "%s timer stopped when its not running! ep %p state %u\n",
__func__, ep, ep->com.state);
- WARN_ON(1);
return;
}
del_timer_sync(&ep->timer);
@@ -1756,9 +1755,8 @@ static void ep_timeout(unsigned long arg)
__state_set(&ep->com, ABORTING);
break;
default:
- printk(KERN_ERR "%s unexpected state ep %p state %u\n",
+ WARN(1, "%s unexpected state ep %p state %u\n",
__func__, ep, ep->com.state);
- WARN_ON(1);
abort = 0;
}
spin_unlock_irqrestore(&ep->com.lock, flags);
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
index 6cfd4d8fd0b..5de86968379 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -151,9 +151,8 @@ static void stop_ep_timer(struct c4iw_ep *ep)
{
PDBG("%s ep %p\n", __func__, ep);
if (!timer_pending(&ep->timer)) {
- printk(KERN_ERR "%s timer stopped when its not running! "
+ WARN(1, "%s timer stopped when its not running! "
"ep %p state %u\n", __func__, ep, ep->com.state);
- WARN_ON(1);
return;
}
del_timer_sync(&ep->timer);
@@ -2551,9 +2550,8 @@ static void process_timeout(struct c4iw_ep *ep)
__state_set(&ep->com, ABORTING);
break;
default:
- printk(KERN_ERR "%s unexpected state ep %p tid %u state %u\n",
+ WARN(1, "%s unexpected state ep %p tid %u state %u\n",
__func__, ep, ep->hwtid, ep->com.state);
- WARN_ON(1);
abort = 0;
}
mutex_unlock(&ep->com.mutex);
diff --git a/drivers/infiniband/hw/ipath/ipath_init_chip.c b/drivers/infiniband/hw/ipath/ipath_init_chip.c
index 49b09c697c7..be2a60e142b 100644
--- a/drivers/infiniband/hw/ipath/ipath_init_chip.c
+++ b/drivers/infiniband/hw/ipath/ipath_init_chip.c
@@ -719,16 +719,6 @@ int ipath_init_chip(struct ipath_devdata *dd, int reinit)
goto done;
/*
- * we ignore most issues after reporting them, but have to specially
- * handle hardware-disabled chips.
- */
- if (ret == 2) {
- /* unique error, known to ipath_init_one */
- ret = -EPERM;
- goto done;
- }
-
- /*
* We could bump this to allow for full rcvegrcnt + rcvtidcnt,
* but then it no longer nicely fits power of two, and since
* we now use routines that backend onto __get_free_pages, the
diff --git a/drivers/infiniband/hw/mlx4/cm.c b/drivers/infiniband/hw/mlx4/cm.c
index 80079e5a2e3..dbc99d41605 100644
--- a/drivers/infiniband/hw/mlx4/cm.c
+++ b/drivers/infiniband/hw/mlx4/cm.c
@@ -268,15 +268,15 @@ static void schedule_delayed(struct ib_device *ibdev, struct id_map_entry *id)
struct mlx4_ib_sriov *sriov = &to_mdev(ibdev)->sriov;
unsigned long flags;
- spin_lock_irqsave(&sriov->going_down_lock, flags);
spin_lock(&sriov->id_map_lock);
+ spin_lock_irqsave(&sriov->going_down_lock, flags);
/*make sure that there is no schedule inside the scheduled work.*/
if (!sriov->is_going_down) {
id->scheduled_delete = 1;
schedule_delayed_work(&id->timeout, CM_CLEANUP_CACHE_TIMEOUT);
}
- spin_unlock(&sriov->id_map_lock);
spin_unlock_irqrestore(&sriov->going_down_lock, flags);
+ spin_unlock(&sriov->id_map_lock);
}
int mlx4_ib_multiplex_cm_handler(struct ib_device *ibdev, int port, int slave_id,
diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c
index c9eb6a6815c..ae67df35dd4 100644
--- a/drivers/infiniband/hw/mlx4/cq.c
+++ b/drivers/infiniband/hw/mlx4/cq.c
@@ -66,7 +66,7 @@ static void mlx4_ib_cq_event(struct mlx4_cq *cq, enum mlx4_event type)
static void *get_cqe_from_buf(struct mlx4_ib_cq_buf *buf, int n)
{
- return mlx4_buf_offset(&buf->buf, n * sizeof (struct mlx4_cqe));
+ return mlx4_buf_offset(&buf->buf, n * buf->entry_size);
}
static void *get_cqe(struct mlx4_ib_cq *cq, int n)
@@ -77,8 +77,9 @@ static void *get_cqe(struct mlx4_ib_cq *cq, int n)
static void *get_sw_cqe(struct mlx4_ib_cq *cq, int n)
{
struct mlx4_cqe *cqe = get_cqe(cq, n & cq->ibcq.cqe);
+ struct mlx4_cqe *tcqe = ((cq->buf.entry_size == 64) ? (cqe + 1) : cqe);
- return (!!(cqe->owner_sr_opcode & MLX4_CQE_OWNER_MASK) ^
+ return (!!(tcqe->owner_sr_opcode & MLX4_CQE_OWNER_MASK) ^
!!(n & (cq->ibcq.cqe + 1))) ? NULL : cqe;
}
@@ -99,12 +100,13 @@ static int mlx4_ib_alloc_cq_buf(struct mlx4_ib_dev *dev, struct mlx4_ib_cq_buf *
{
int err;
- err = mlx4_buf_alloc(dev->dev, nent * sizeof(struct mlx4_cqe),
+ err = mlx4_buf_alloc(dev->dev, nent * dev->dev->caps.cqe_size,
PAGE_SIZE * 2, &buf->buf);
if (err)
goto out;
+ buf->entry_size = dev->dev->caps.cqe_size;
err = mlx4_mtt_init(dev->dev, buf->buf.npages, buf->buf.page_shift,
&buf->mtt);
if (err)
@@ -120,8 +122,7 @@ err_mtt:
mlx4_mtt_cleanup(dev->dev, &buf->mtt);
err_buf:
- mlx4_buf_free(dev->dev, nent * sizeof(struct mlx4_cqe),
- &buf->buf);
+ mlx4_buf_free(dev->dev, nent * buf->entry_size, &buf->buf);
out:
return err;
@@ -129,7 +130,7 @@ out:
static void mlx4_ib_free_cq_buf(struct mlx4_ib_dev *dev, struct mlx4_ib_cq_buf *buf, int cqe)
{
- mlx4_buf_free(dev->dev, (cqe + 1) * sizeof(struct mlx4_cqe), &buf->buf);
+ mlx4_buf_free(dev->dev, (cqe + 1) * buf->entry_size, &buf->buf);
}
static int mlx4_ib_get_cq_umem(struct mlx4_ib_dev *dev, struct ib_ucontext *context,
@@ -137,8 +138,9 @@ static int mlx4_ib_get_cq_umem(struct mlx4_ib_dev *dev, struct ib_ucontext *cont
u64 buf_addr, int cqe)
{
int err;
+ int cqe_size = dev->dev->caps.cqe_size;
- *umem = ib_umem_get(context, buf_addr, cqe * sizeof (struct mlx4_cqe),
+ *umem = ib_umem_get(context, buf_addr, cqe * cqe_size,
IB_ACCESS_LOCAL_WRITE, 1);
if (IS_ERR(*umem))
return PTR_ERR(*umem);
@@ -331,16 +333,23 @@ static void mlx4_ib_cq_resize_copy_cqes(struct mlx4_ib_cq *cq)
{
struct mlx4_cqe *cqe, *new_cqe;
int i;
+ int cqe_size = cq->buf.entry_size;
+ int cqe_inc = cqe_size == 64 ? 1 : 0;
i = cq->mcq.cons_index;
cqe = get_cqe(cq, i & cq->ibcq.cqe);
+ cqe += cqe_inc;
+
while ((cqe->owner_sr_opcode & MLX4_CQE_OPCODE_MASK) != MLX4_CQE_OPCODE_RESIZE) {
new_cqe = get_cqe_from_buf(&cq->resize_buf->buf,
(i + 1) & cq->resize_buf->cqe);
- memcpy(new_cqe, get_cqe(cq, i & cq->ibcq.cqe), sizeof(struct mlx4_cqe));
+ memcpy(new_cqe, get_cqe(cq, i & cq->ibcq.cqe), cqe_size);
+ new_cqe += cqe_inc;
+
new_cqe->owner_sr_opcode = (cqe->owner_sr_opcode & ~MLX4_CQE_OWNER_MASK) |
(((i + 1) & (cq->resize_buf->cqe + 1)) ? MLX4_CQE_OWNER_MASK : 0);
cqe = get_cqe(cq, ++i & cq->ibcq.cqe);
+ cqe += cqe_inc;
}
++cq->mcq.cons_index;
}
@@ -438,6 +447,7 @@ err_buf:
out:
mutex_unlock(&cq->resize_mutex);
+
return err;
}
@@ -586,6 +596,9 @@ repoll:
if (!cqe)
return -EAGAIN;
+ if (cq->buf.entry_size == 64)
+ cqe++;
+
++cq->mcq.cons_index;
/*
@@ -807,6 +820,7 @@ void __mlx4_ib_cq_clean(struct mlx4_ib_cq *cq, u32 qpn, struct mlx4_ib_srq *srq)
int nfreed = 0;
struct mlx4_cqe *cqe, *dest;
u8 owner_bit;
+ int cqe_inc = cq->buf.entry_size == 64 ? 1 : 0;
/*
* First we need to find the current producer index, so we
@@ -825,12 +839,16 @@ void __mlx4_ib_cq_clean(struct mlx4_ib_cq *cq, u32 qpn, struct mlx4_ib_srq *srq)
*/
while ((int) --prod_index - (int) cq->mcq.cons_index >= 0) {
cqe = get_cqe(cq, prod_index & cq->ibcq.cqe);
+ cqe += cqe_inc;
+
if ((be32_to_cpu(cqe->vlan_my_qpn) & MLX4_CQE_QPN_MASK) == qpn) {
if (srq && !(cqe->owner_sr_opcode & MLX4_CQE_IS_SEND_MASK))
mlx4_ib_free_srq_wqe(srq, be16_to_cpu(cqe->wqe_index));
++nfreed;
} else if (nfreed) {
dest = get_cqe(cq, (prod_index + nfreed) & cq->ibcq.cqe);
+ dest += cqe_inc;
+
owner_bit = dest->owner_sr_opcode & MLX4_CQE_OWNER_MASK;
memcpy(dest, cqe, sizeof *cqe);
dest->owner_sr_opcode = owner_bit |
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index 718ec6b2bad..e7d81c0d1ac 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -563,15 +563,24 @@ static struct ib_ucontext *mlx4_ib_alloc_ucontext(struct ib_device *ibdev,
{
struct mlx4_ib_dev *dev = to_mdev(ibdev);
struct mlx4_ib_ucontext *context;
+ struct mlx4_ib_alloc_ucontext_resp_v3 resp_v3;
struct mlx4_ib_alloc_ucontext_resp resp;
int err;
if (!dev->ib_active)
return ERR_PTR(-EAGAIN);
- resp.qp_tab_size = dev->dev->caps.num_qps;
- resp.bf_reg_size = dev->dev->caps.bf_reg_size;
- resp.bf_regs_per_page = dev->dev->caps.bf_regs_per_page;
+ if (ibdev->uverbs_abi_ver == MLX4_IB_UVERBS_NO_DEV_CAPS_ABI_VERSION) {
+ resp_v3.qp_tab_size = dev->dev->caps.num_qps;
+ resp_v3.bf_reg_size = dev->dev->caps.bf_reg_size;
+ resp_v3.bf_regs_per_page = dev->dev->caps.bf_regs_per_page;
+ } else {
+ resp.dev_caps = dev->dev->caps.userspace_caps;
+ resp.qp_tab_size = dev->dev->caps.num_qps;
+ resp.bf_reg_size = dev->dev->caps.bf_reg_size;
+ resp.bf_regs_per_page = dev->dev->caps.bf_regs_per_page;
+ resp.cqe_size = dev->dev->caps.cqe_size;
+ }
context = kmalloc(sizeof *context, GFP_KERNEL);
if (!context)
@@ -586,7 +595,11 @@ static struct ib_ucontext *mlx4_ib_alloc_ucontext(struct ib_device *ibdev,
INIT_LIST_HEAD(&context->db_page_list);
mutex_init(&context->db_page_mutex);
- err = ib_copy_to_udata(udata, &resp, sizeof resp);
+ if (ibdev->uverbs_abi_ver == MLX4_IB_UVERBS_NO_DEV_CAPS_ABI_VERSION)
+ err = ib_copy_to_udata(udata, &resp_v3, sizeof(resp_v3));
+ else
+ err = ib_copy_to_udata(udata, &resp, sizeof(resp));
+
if (err) {
mlx4_uar_free(to_mdev(ibdev)->dev, &context->uar);
kfree(context);
@@ -1342,7 +1355,11 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
ibdev->ib_dev.num_comp_vectors = dev->caps.num_comp_vectors;
ibdev->ib_dev.dma_device = &dev->pdev->dev;
- ibdev->ib_dev.uverbs_abi_ver = MLX4_IB_UVERBS_ABI_VERSION;
+ if (dev->caps.userspace_caps)
+ ibdev->ib_dev.uverbs_abi_ver = MLX4_IB_UVERBS_ABI_VERSION;
+ else
+ ibdev->ib_dev.uverbs_abi_ver = MLX4_IB_UVERBS_NO_DEV_CAPS_ABI_VERSION;
+
ibdev->ib_dev.uverbs_cmd_mask =
(1ull << IB_USER_VERBS_CMD_GET_CONTEXT) |
(1ull << IB_USER_VERBS_CMD_QUERY_DEVICE) |
diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h
index e04cbc9a54a..dcd845bc30f 100644
--- a/drivers/infiniband/hw/mlx4/mlx4_ib.h
+++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h
@@ -90,6 +90,7 @@ struct mlx4_ib_xrcd {
struct mlx4_ib_cq_buf {
struct mlx4_buf buf;
struct mlx4_mtt mtt;
+ int entry_size;
};
struct mlx4_ib_cq_resize {
diff --git a/drivers/infiniband/hw/mlx4/user.h b/drivers/infiniband/hw/mlx4/user.h
index 13beedeeef9..07e6769ef43 100644
--- a/drivers/infiniband/hw/mlx4/user.h
+++ b/drivers/infiniband/hw/mlx4/user.h
@@ -40,7 +40,9 @@
* Increment this value if any changes that break userspace ABI
* compatibility are made.
*/
-#define MLX4_IB_UVERBS_ABI_VERSION 3
+
+#define MLX4_IB_UVERBS_NO_DEV_CAPS_ABI_VERSION 3
+#define MLX4_IB_UVERBS_ABI_VERSION 4
/*
* Make sure that all structs defined in this file remain laid out so
@@ -50,10 +52,18 @@
* instead.
*/
+struct mlx4_ib_alloc_ucontext_resp_v3 {
+ __u32 qp_tab_size;
+ __u16 bf_reg_size;
+ __u16 bf_regs_per_page;
+};
+
struct mlx4_ib_alloc_ucontext_resp {
+ __u32 dev_caps;
__u32 qp_tab_size;
__u16 bf_reg_size;
__u16 bf_regs_per_page;
+ __u32 cqe_size;
};
struct mlx4_ib_alloc_pd_resp {
diff --git a/drivers/infiniband/hw/nes/nes.h b/drivers/infiniband/hw/nes/nes.h
index 5cac29e6bc1..33cc58941a3 100644
--- a/drivers/infiniband/hw/nes/nes.h
+++ b/drivers/infiniband/hw/nes/nes.h
@@ -532,6 +532,7 @@ void nes_iwarp_ce_handler(struct nes_device *, struct nes_hw_cq *);
int nes_destroy_cqp(struct nes_device *);
int nes_nic_cm_xmit(struct sk_buff *, struct net_device *);
void nes_recheck_link_status(struct work_struct *work);
+void nes_terminate_timeout(unsigned long context);
/* nes_nic.c */
struct net_device *nes_netdev_init(struct nes_device *, void __iomem *);
diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c
index cfaacaf6bf5..22ea67eea5d 100644
--- a/drivers/infiniband/hw/nes/nes_cm.c
+++ b/drivers/infiniband/hw/nes/nes_cm.c
@@ -629,11 +629,9 @@ static void build_rdma0_msg(struct nes_cm_node *cm_node, struct nes_qp **nesqp_a
case SEND_RDMA_READ_ZERO:
default:
- if (cm_node->send_rdma0_op != SEND_RDMA_READ_ZERO) {
- printk(KERN_ERR "%s[%u]: Unsupported RDMA0 len operation=%u\n",
- __func__, __LINE__, cm_node->send_rdma0_op);
- WARN_ON(1);
- }
+ if (cm_node->send_rdma0_op != SEND_RDMA_READ_ZERO)
+ WARN(1, "Unsupported RDMA0 len operation=%u\n",
+ cm_node->send_rdma0_op);
nes_debug(NES_DBG_CM, "Sending first rdma operation.\n");
wqe->wqe_words[NES_IWARP_SQ_WQE_MISC_IDX] =
cpu_to_le32(NES_IWARP_SQ_OP_RDMAR);
@@ -671,7 +669,6 @@ int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb,
struct nes_cm_core *cm_core = cm_node->cm_core;
struct nes_timer_entry *new_send;
int ret = 0;
- u32 was_timer_set;
new_send = kzalloc(sizeof(*new_send), GFP_ATOMIC);
if (!new_send)
@@ -723,12 +720,8 @@ int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb,
}
}
- was_timer_set = timer_pending(&cm_core->tcp_timer);
-
- if (!was_timer_set) {
- cm_core->tcp_timer.expires = new_send->timetosend;
- add_timer(&cm_core->tcp_timer);
- }
+ if (!timer_pending(&cm_core->tcp_timer))
+ mod_timer(&cm_core->tcp_timer, new_send->timetosend);
return ret;
}
@@ -946,10 +939,8 @@ static void nes_cm_timer_tick(unsigned long pass)
}
if (settimer) {
- if (!timer_pending(&cm_core->tcp_timer)) {
- cm_core->tcp_timer.expires = nexttimeout;
- add_timer(&cm_core->tcp_timer);
- }
+ if (!timer_pending(&cm_core->tcp_timer))
+ mod_timer(&cm_core->tcp_timer, nexttimeout);
}
}
@@ -1314,8 +1305,6 @@ static int mini_cm_del_listen(struct nes_cm_core *cm_core,
static inline int mini_cm_accelerated(struct nes_cm_core *cm_core,
struct nes_cm_node *cm_node)
{
- u32 was_timer_set;
-
cm_node->accelerated = 1;
if (cm_node->accept_pend) {
@@ -1325,11 +1314,8 @@ static inline int mini_cm_accelerated(struct nes_cm_core *cm_core,
BUG_ON(atomic_read(&cm_node->listener->pend_accepts_cnt) < 0);
}
- was_timer_set = timer_pending(&cm_core->tcp_timer);
- if (!was_timer_set) {
- cm_core->tcp_timer.expires = jiffies + NES_SHORT_TIME;
- add_timer(&cm_core->tcp_timer);
- }
+ if (!timer_pending(&cm_core->tcp_timer))
+ mod_timer(&cm_core->tcp_timer, (jiffies + NES_SHORT_TIME));
return 0;
}
diff --git a/drivers/infiniband/hw/nes/nes_hw.c b/drivers/infiniband/hw/nes/nes_hw.c
index fe7965ee409..67647e26461 100644
--- a/drivers/infiniband/hw/nes/nes_hw.c
+++ b/drivers/infiniband/hw/nes/nes_hw.c
@@ -75,7 +75,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
static void process_critical_error(struct nes_device *nesdev);
static void nes_process_mac_intr(struct nes_device *nesdev, u32 mac_number);
static unsigned int nes_reset_adapter_ne020(struct nes_device *nesdev, u8 *OneG_Mode);
-static void nes_terminate_timeout(unsigned long context);
static void nes_terminate_start_timer(struct nes_qp *nesqp);
#ifdef CONFIG_INFINIBAND_NES_DEBUG
@@ -3520,7 +3519,7 @@ static void nes_terminate_received(struct nes_device *nesdev,
}
/* Timeout routine in case terminate fails to complete */
-static void nes_terminate_timeout(unsigned long context)
+void nes_terminate_timeout(unsigned long context)
{
struct nes_qp *nesqp = (struct nes_qp *)(unsigned long)context;
@@ -3530,11 +3529,7 @@ static void nes_terminate_timeout(unsigned long context)
/* Set a timer in case hw cannot complete the terminate sequence */
static void nes_terminate_start_timer(struct nes_qp *nesqp)
{
- init_timer(&nesqp->terminate_timer);
- nesqp->terminate_timer.function = nes_terminate_timeout;
- nesqp->terminate_timer.expires = jiffies + HZ;
- nesqp->terminate_timer.data = (unsigned long)nesqp;
- add_timer(&nesqp->terminate_timer);
+ mod_timer(&nesqp->terminate_timer, (jiffies + HZ));
}
/**
diff --git a/drivers/infiniband/hw/nes/nes_mgt.c b/drivers/infiniband/hw/nes/nes_mgt.c
index 3ba7be36945..416645259b0 100644
--- a/drivers/infiniband/hw/nes/nes_mgt.c
+++ b/drivers/infiniband/hw/nes/nes_mgt.c
@@ -210,6 +210,9 @@ static struct sk_buff *nes_get_next_skb(struct nes_device *nesdev, struct nes_qp
}
while (1) {
+ if (skb_queue_empty(&nesqp->pau_list))
+ goto out;
+
seq = nes_get_seq(skb, ack, wnd, fin_rcvd, rst_rcvd);
if (seq == nextseq) {
if (skb->len || processacks)
@@ -218,14 +221,13 @@ static struct sk_buff *nes_get_next_skb(struct nes_device *nesdev, struct nes_qp
goto out;
}
- if (skb->next == (struct sk_buff *)&nesqp->pau_list)
- goto out;
-
old_skb = skb;
skb = skb->next;
skb_unlink(old_skb, &nesqp->pau_list);
nes_mgt_free_skb(nesdev, old_skb, PCI_DMA_TODEVICE);
nes_rem_ref_cm_node(nesqp->cm_node);
+ if (skb == (struct sk_buff *)&nesqp->pau_list)
+ goto out;
}
return skb;
@@ -245,7 +247,6 @@ static int get_fpdu_info(struct nes_device *nesdev, struct nes_qp *nesqp,
struct nes_rskb_cb *cb;
struct pau_fpdu_info *fpdu_info = NULL;
struct pau_fpdu_frag frags[MAX_FPDU_FRAGS];
- unsigned long flags;
u32 fpdu_len = 0;
u32 tmp_len;
int frag_cnt = 0;
@@ -260,12 +261,10 @@ static int get_fpdu_info(struct nes_device *nesdev, struct nes_qp *nesqp,
*pau_fpdu_info = NULL;
- spin_lock_irqsave(&nesqp->pau_lock, flags);
skb = nes_get_next_skb(nesdev, nesqp, NULL, nesqp->pau_rcv_nxt, &ack, &wnd, &fin_rcvd, &rst_rcvd);
- if (!skb) {
- spin_unlock_irqrestore(&nesqp->pau_lock, flags);
+ if (!skb)
goto out;
- }
+
cb = (struct nes_rskb_cb *)&skb->cb[0];
if (skb->len) {
fpdu_len = be16_to_cpu(*(__be16 *) skb->data) + MPA_FRAMING;
@@ -290,10 +289,9 @@ static int get_fpdu_info(struct nes_device *nesdev, struct nes_qp *nesqp,
skb = nes_get_next_skb(nesdev, nesqp, skb,
nesqp->pau_rcv_nxt + frag_tot, &ack, &wnd, &fin_rcvd, &rst_rcvd);
- if (!skb) {
- spin_unlock_irqrestore(&nesqp->pau_lock, flags);
+ if (!skb)
goto out;
- } else if (rst_rcvd) {
+ if (rst_rcvd) {
/* rst received in the middle of fpdu */
for (; i >= 0; i--) {
skb_unlink(frags[i].skb, &nesqp->pau_list);
@@ -320,8 +318,6 @@ static int get_fpdu_info(struct nes_device *nesdev, struct nes_qp *nesqp,
frag_cnt = 1;
}
- spin_unlock_irqrestore(&nesqp->pau_lock, flags);
-
/* Found one */
fpdu_info = kzalloc(sizeof(*fpdu_info), GFP_ATOMIC);
if (fpdu_info == NULL) {
@@ -383,9 +379,8 @@ static int get_fpdu_info(struct nes_device *nesdev, struct nes_qp *nesqp,
if (frags[i].skb->len == 0) {
/* Pull skb off the list - it will be freed in the callback */
- spin_lock_irqsave(&nesqp->pau_lock, flags);
- skb_unlink(frags[i].skb, &nesqp->pau_list);
- spin_unlock_irqrestore(&nesqp->pau_lock, flags);
+ if (!skb_queue_empty(&nesqp->pau_list))
+ skb_unlink(frags[i].skb, &nesqp->pau_list);
} else {
/* Last skb still has data so update the seq */
iph = (struct iphdr *)(cb->data_start + ETH_HLEN);
@@ -414,14 +409,18 @@ static int forward_fpdus(struct nes_vnic *nesvnic, struct nes_qp *nesqp)
struct pau_fpdu_info *fpdu_info;
struct nes_hw_cqp_wqe *cqp_wqe;
struct nes_cqp_request *cqp_request;
+ unsigned long flags;
u64 u64tmp;
u32 u32tmp;
int rc;
while (1) {
+ spin_lock_irqsave(&nesqp->pau_lock, flags);
rc = get_fpdu_info(nesdev, nesqp, &fpdu_info);
- if (fpdu_info == NULL)
+ if (rc || (fpdu_info == NULL)) {
+ spin_unlock_irqrestore(&nesqp->pau_lock, flags);
return rc;
+ }
cqp_request = fpdu_info->cqp_request;
cqp_wqe = &cqp_request->cqp_wqe;
@@ -447,7 +446,7 @@ static int forward_fpdus(struct nes_vnic *nesvnic, struct nes_qp *nesqp)
set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG0_LOW_IDX,
lower_32_bits(u64tmp));
set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG0_HIGH_IDX,
- upper_32_bits(u64tmp >> 32));
+ upper_32_bits(u64tmp));
set_wqe_32bit_value(cqp_wqe->wqe_words, NES_NIC_SQ_WQE_FRAG1_LOW_IDX,
lower_32_bits(fpdu_info->frags[0].physaddr));
@@ -475,6 +474,7 @@ static int forward_fpdus(struct nes_vnic *nesvnic, struct nes_qp *nesqp)
atomic_set(&cqp_request->refcount, 1);
nes_post_cqp_request(nesdev, cqp_request);
+ spin_unlock_irqrestore(&nesqp->pau_lock, flags);
}
return 0;
@@ -649,11 +649,9 @@ static void nes_chg_qh_handler(struct nes_device *nesdev, struct nes_cqp_request
nesqp = qh_chg->nesqp;
/* Should we handle the bad completion */
- if (cqp_request->major_code) {
- printk(KERN_ERR PFX "Invalid cqp_request major_code=0x%x\n",
+ if (cqp_request->major_code)
+ WARN(1, PFX "Invalid cqp_request major_code=0x%x\n",
cqp_request->major_code);
- WARN_ON(1);
- }
switch (nesqp->pau_state) {
case PAU_DEL_QH:
diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c
index 0564be757d8..9542e1644a5 100644
--- a/drivers/infiniband/hw/nes/nes_nic.c
+++ b/drivers/infiniband/hw/nes/nes_nic.c
@@ -944,12 +944,13 @@ static void nes_netdev_set_multicast_list(struct net_device *netdev)
addr,
perfect_filter_register_address+(mc_index * 8),
mc_nic_index);
- macaddr_high = ((u16) addr[0]) << 8;
- macaddr_high += (u16) addr[1];
- macaddr_low = ((u32) addr[2]) << 24;
- macaddr_low += ((u32) addr[3]) << 16;
- macaddr_low += ((u32) addr[4]) << 8;
- macaddr_low += (u32) addr[5];
+ macaddr_high = ((u8) addr[0]) << 8;
+ macaddr_high += (u8) addr[1];
+ macaddr_low = ((u8) addr[2]) << 24;
+ macaddr_low += ((u8) addr[3]) << 16;
+ macaddr_low += ((u8) addr[4]) << 8;
+ macaddr_low += (u8) addr[5];
+
nes_write_indexed(nesdev,
perfect_filter_register_address+(mc_index * 8),
macaddr_low);
diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c
index cd0ecb215cc..07e4fbad987 100644
--- a/drivers/infiniband/hw/nes/nes_verbs.c
+++ b/drivers/infiniband/hw/nes/nes_verbs.c
@@ -1404,6 +1404,9 @@ static struct ib_qp *nes_create_qp(struct ib_pd *ibpd,
}
nesqp->sig_all = (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR);
+ init_timer(&nesqp->terminate_timer);
+ nesqp->terminate_timer.function = nes_terminate_timeout;
+ nesqp->terminate_timer.data = (unsigned long)nesqp;
/* update the QP table */
nesdev->nesadapter->qp_table[nesqp->hwqp.qp_id-NES_FIRST_QPN] = nesqp;
@@ -1413,7 +1416,6 @@ static struct ib_qp *nes_create_qp(struct ib_pd *ibpd,
return &nesqp->ibqp;
}
-
/**
* nes_clean_cq
*/
@@ -2559,6 +2561,11 @@ static struct ib_mr *nes_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
return ibmr;
case IWNES_MEMREG_TYPE_QP:
case IWNES_MEMREG_TYPE_CQ:
+ if (!region->length) {
+ nes_debug(NES_DBG_MR, "Unable to register zero length region for CQ\n");
+ ib_umem_release(region);
+ return ERR_PTR(-EINVAL);
+ }
nespbl = kzalloc(sizeof(*nespbl), GFP_KERNEL);
if (!nespbl) {
nes_debug(NES_DBG_MR, "Unable to allocate PBL\n");
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index 922d845f76b..d5088ce7829 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -222,27 +222,29 @@ static int srp_new_cm_id(struct srp_target_port *target)
static int srp_create_target_ib(struct srp_target_port *target)
{
struct ib_qp_init_attr *init_attr;
+ struct ib_cq *recv_cq, *send_cq;
+ struct ib_qp *qp;
int ret;
init_attr = kzalloc(sizeof *init_attr, GFP_KERNEL);
if (!init_attr)
return -ENOMEM;
- target->recv_cq = ib_create_cq(target->srp_host->srp_dev->dev,
- srp_recv_completion, NULL, target, SRP_RQ_SIZE, 0);
- if (IS_ERR(target->recv_cq)) {
- ret = PTR_ERR(target->recv_cq);
+ recv_cq = ib_create_cq(target->srp_host->srp_dev->dev,
+ srp_recv_completion, NULL, target, SRP_RQ_SIZE, 0);
+ if (IS_ERR(recv_cq)) {
+ ret = PTR_ERR(recv_cq);
goto err;
}
- target->send_cq = ib_create_cq(target->srp_host->srp_dev->dev,
- srp_send_completion, NULL, target, SRP_SQ_SIZE, 0);
- if (IS_ERR(target->send_cq)) {
- ret = PTR_ERR(target->send_cq);
+ send_cq = ib_create_cq(target->srp_host->srp_dev->dev,
+ srp_send_completion, NULL, target, SRP_SQ_SIZE, 0);
+ if (IS_ERR(send_cq)) {
+ ret = PTR_ERR(send_cq);
goto err_recv_cq;
}
- ib_req_notify_cq(target->recv_cq, IB_CQ_NEXT_COMP);
+ ib_req_notify_cq(recv_cq, IB_CQ_NEXT_COMP);
init_attr->event_handler = srp_qp_event;
init_attr->cap.max_send_wr = SRP_SQ_SIZE;
@@ -251,30 +253,41 @@ static int srp_create_target_ib(struct srp_target_port *target)
init_attr->cap.max_send_sge = 1;
init_attr->sq_sig_type = IB_SIGNAL_ALL_WR;
init_attr->qp_type = IB_QPT_RC;
- init_attr->send_cq = target->send_cq;
- init_attr->recv_cq = target->recv_cq;
+ init_attr->send_cq = send_cq;
+ init_attr->recv_cq = recv_cq;
- target->qp = ib_create_qp(target->srp_host->srp_dev->pd, init_attr);
- if (IS_ERR(target->qp)) {
- ret = PTR_ERR(target->qp);
+ qp = ib_create_qp(target->srp_host->srp_dev->pd, init_attr);
+ if (IS_ERR(qp)) {
+ ret = PTR_ERR(qp);
goto err_send_cq;
}
- ret = srp_init_qp(target, target->qp);
+ ret = srp_init_qp(target, qp);
if (ret)
goto err_qp;
+ if (target->qp)
+ ib_destroy_qp(target->qp);
+ if (target->recv_cq)
+ ib_destroy_cq(target->recv_cq);
+ if (target->send_cq)
+ ib_destroy_cq(target->send_cq);
+
+ target->qp = qp;
+ target->recv_cq = recv_cq;
+ target->send_cq = send_cq;
+
kfree(init_attr);
return 0;
err_qp:
- ib_destroy_qp(target->qp);
+ ib_destroy_qp(qp);
err_send_cq:
- ib_destroy_cq(target->send_cq);
+ ib_destroy_cq(send_cq);
err_recv_cq:
- ib_destroy_cq(target->recv_cq);
+ ib_destroy_cq(recv_cq);
err:
kfree(init_attr);
@@ -289,6 +302,9 @@ static void srp_free_target_ib(struct srp_target_port *target)
ib_destroy_cq(target->send_cq);
ib_destroy_cq(target->recv_cq);
+ target->qp = NULL;
+ target->send_cq = target->recv_cq = NULL;
+
for (i = 0; i < SRP_RQ_SIZE; ++i)
srp_free_iu(target->srp_host, target->rx_ring[i]);
for (i = 0; i < SRP_SQ_SIZE; ++i)
@@ -428,34 +444,50 @@ static int srp_send_req(struct srp_target_port *target)
return status;
}
-static void srp_disconnect_target(struct srp_target_port *target)
+static bool srp_queue_remove_work(struct srp_target_port *target)
{
- /* XXX should send SRP_I_LOGOUT request */
+ bool changed = false;
- init_completion(&target->done);
- if (ib_send_cm_dreq(target->cm_id, NULL, 0)) {
- shost_printk(KERN_DEBUG, target->scsi_host,
- PFX "Sending CM DREQ failed\n");
- return;
+ spin_lock_irq(&target->lock);
+ if (target->state != SRP_TARGET_REMOVED) {
+ target->state = SRP_TARGET_REMOVED;
+ changed = true;
}
- wait_for_completion(&target->done);
+ spin_unlock_irq(&target->lock);
+
+ if (changed)
+ queue_work(system_long_wq, &target->remove_work);
+
+ return changed;
}
-static bool srp_change_state(struct srp_target_port *target,
- enum srp_target_state old,
- enum srp_target_state new)
+static bool srp_change_conn_state(struct srp_target_port *target,
+ bool connected)
{
bool changed = false;
spin_lock_irq(&target->lock);
- if (target->state == old) {
- target->state = new;
+ if (target->connected != connected) {
+ target->connected = connected;
changed = true;
}
spin_unlock_irq(&target->lock);
+
return changed;
}
+static void srp_disconnect_target(struct srp_target_port *target)
+{
+ if (srp_change_conn_state(target, false)) {
+ /* XXX should send SRP_I_LOGOUT request */
+
+ if (ib_send_cm_dreq(target->cm_id, NULL, 0)) {
+ shost_printk(KERN_DEBUG, target->scsi_host,
+ PFX "Sending CM DREQ failed\n");
+ }
+ }
+}
+
static void srp_free_req_data(struct srp_target_port *target)
{
struct ib_device *ibdev = target->srp_host->srp_dev->dev;
@@ -489,32 +521,50 @@ static void srp_del_scsi_host_attr(struct Scsi_Host *shost)
device_remove_file(&shost->shost_dev, *attr);
}
-static void srp_remove_work(struct work_struct *work)
+static void srp_remove_target(struct srp_target_port *target)
{
- struct srp_target_port *target =
- container_of(work, struct srp_target_port, work);
-
- if (!srp_change_state(target, SRP_TARGET_DEAD, SRP_TARGET_REMOVED))
- return;
-
- spin_lock(&target->srp_host->target_lock);
- list_del(&target->list);
- spin_unlock(&target->srp_host->target_lock);
+ WARN_ON_ONCE(target->state != SRP_TARGET_REMOVED);
srp_del_scsi_host_attr(target->scsi_host);
srp_remove_host(target->scsi_host);
scsi_remove_host(target->scsi_host);
+ srp_disconnect_target(target);
ib_destroy_cm_id(target->cm_id);
srp_free_target_ib(target);
srp_free_req_data(target);
scsi_host_put(target->scsi_host);
}
+static void srp_remove_work(struct work_struct *work)
+{
+ struct srp_target_port *target =
+ container_of(work, struct srp_target_port, remove_work);
+
+ WARN_ON_ONCE(target->state != SRP_TARGET_REMOVED);
+
+ spin_lock(&target->srp_host->target_lock);
+ list_del(&target->list);
+ spin_unlock(&target->srp_host->target_lock);
+
+ srp_remove_target(target);
+}
+
+static void srp_rport_delete(struct srp_rport *rport)
+{
+ struct srp_target_port *target = rport->lld_data;
+
+ srp_queue_remove_work(target);
+}
+
static int srp_connect_target(struct srp_target_port *target)
{
int retries = 3;
int ret;
+ WARN_ON_ONCE(target->connected);
+
+ target->qp_in_error = false;
+
ret = srp_lookup_path(target);
if (ret)
return ret;
@@ -534,6 +584,7 @@ static int srp_connect_target(struct srp_target_port *target)
*/
switch (target->status) {
case 0:
+ srp_change_conn_state(target, true);
return 0;
case SRP_PORT_REDIRECT:
@@ -646,13 +697,14 @@ static void srp_reset_req(struct srp_target_port *target, struct srp_request *re
static int srp_reconnect_target(struct srp_target_port *target)
{
- struct ib_qp_attr qp_attr;
- struct ib_wc wc;
+ struct Scsi_Host *shost = target->scsi_host;
int i, ret;
- if (!srp_change_state(target, SRP_TARGET_LIVE, SRP_TARGET_CONNECTING))
+ if (target->state != SRP_TARGET_LIVE)
return -EAGAIN;
+ scsi_target_block(&shost->shost_gendev);
+
srp_disconnect_target(target);
/*
* Now get a new local CM ID so that we avoid confusing the
@@ -660,21 +712,11 @@ static int srp_reconnect_target(struct srp_target_port *target)
*/
ret = srp_new_cm_id(target);
if (ret)
- goto err;
+ goto unblock;
- qp_attr.qp_state = IB_QPS_RESET;
- ret = ib_modify_qp(target->qp, &qp_attr, IB_QP_STATE);
- if (ret)
- goto err;
-
- ret = srp_init_qp(target, target->qp);
+ ret = srp_create_target_ib(target);
if (ret)
- goto err;
-
- while (ib_poll_cq(target->recv_cq, 1, &wc) > 0)
- ; /* nothing */
- while (ib_poll_cq(target->send_cq, 1, &wc) > 0)
- ; /* nothing */
+ goto unblock;
for (i = 0; i < SRP_CMD_SQ_SIZE; ++i) {
struct srp_request *req = &target->req_ring[i];
@@ -686,13 +728,16 @@ static int srp_reconnect_target(struct srp_target_port *target)
for (i = 0; i < SRP_SQ_SIZE; ++i)
list_add(&target->tx_ring[i]->list, &target->free_tx);
- target->qp_in_error = 0;
ret = srp_connect_target(target);
+
+unblock:
+ scsi_target_unblock(&shost->shost_gendev, ret == 0 ? SDEV_RUNNING :
+ SDEV_TRANSPORT_OFFLINE);
+
if (ret)
goto err;
- if (!srp_change_state(target, SRP_TARGET_CONNECTING, SRP_TARGET_LIVE))
- ret = -EAGAIN;
+ shost_printk(KERN_INFO, target->scsi_host, PFX "reconnect succeeded\n");
return ret;
@@ -705,17 +750,8 @@ err:
* However, we have to defer the real removal because we
* are in the context of the SCSI error handler now, which
* will deadlock if we call scsi_remove_host().
- *
- * Schedule our work inside the lock to avoid a race with
- * the flush_scheduled_work() in srp_remove_one().
*/
- spin_lock_irq(&target->lock);
- if (target->state == SRP_TARGET_CONNECTING) {
- target->state = SRP_TARGET_DEAD;
- INIT_WORK(&target->work, srp_remove_work);
- queue_work(ib_wq, &target->work);
- }
- spin_unlock_irq(&target->lock);
+ srp_queue_remove_work(target);
return ret;
}
@@ -1262,6 +1298,19 @@ static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc)
PFX "Recv failed with error code %d\n", res);
}
+static void srp_handle_qp_err(enum ib_wc_status wc_status,
+ enum ib_wc_opcode wc_opcode,
+ struct srp_target_port *target)
+{
+ if (target->connected && !target->qp_in_error) {
+ shost_printk(KERN_ERR, target->scsi_host,
+ PFX "failed %s status %d\n",
+ wc_opcode & IB_WC_RECV ? "receive" : "send",
+ wc_status);
+ }
+ target->qp_in_error = true;
+}
+
static void srp_recv_completion(struct ib_cq *cq, void *target_ptr)
{
struct srp_target_port *target = target_ptr;
@@ -1269,15 +1318,11 @@ static void srp_recv_completion(struct ib_cq *cq, void *target_ptr)
ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
while (ib_poll_cq(cq, 1, &wc) > 0) {
- if (wc.status) {
- shost_printk(KERN_ERR, target->scsi_host,
- PFX "failed receive status %d\n",
- wc.status);
- target->qp_in_error = 1;
- break;
+ if (likely(wc.status == IB_WC_SUCCESS)) {
+ srp_handle_recv(target, &wc);
+ } else {
+ srp_handle_qp_err(wc.status, wc.opcode, target);
}
-
- srp_handle_recv(target, &wc);
}
}
@@ -1288,16 +1333,12 @@ static void srp_send_completion(struct ib_cq *cq, void *target_ptr)
struct srp_iu *iu;
while (ib_poll_cq(cq, 1, &wc) > 0) {
- if (wc.status) {
- shost_printk(KERN_ERR, target->scsi_host,
- PFX "failed send status %d\n",
- wc.status);
- target->qp_in_error = 1;
- break;
+ if (likely(wc.status == IB_WC_SUCCESS)) {
+ iu = (struct srp_iu *) (uintptr_t) wc.wr_id;
+ list_add(&iu->list, &target->free_tx);
+ } else {
+ srp_handle_qp_err(wc.status, wc.opcode, target);
}
-
- iu = (struct srp_iu *) (uintptr_t) wc.wr_id;
- list_add(&iu->list, &target->free_tx);
}
}
@@ -1311,16 +1352,6 @@ static int srp_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scmnd)
unsigned long flags;
int len;
- if (target->state == SRP_TARGET_CONNECTING)
- goto err;
-
- if (target->state == SRP_TARGET_DEAD ||
- target->state == SRP_TARGET_REMOVED) {
- scmnd->result = DID_BAD_TARGET << 16;
- scmnd->scsi_done(scmnd);
- return 0;
- }
-
spin_lock_irqsave(&target->lock, flags);
iu = __srp_get_tx_iu(target, SRP_IU_CMD);
if (!iu)
@@ -1377,7 +1408,6 @@ err_iu:
err_unlock:
spin_unlock_irqrestore(&target->lock, flags);
-err:
return SCSI_MLQUEUE_HOST_BUSY;
}
@@ -1419,6 +1449,33 @@ err:
return -ENOMEM;
}
+static uint32_t srp_compute_rq_tmo(struct ib_qp_attr *qp_attr, int attr_mask)
+{
+ uint64_t T_tr_ns, max_compl_time_ms;
+ uint32_t rq_tmo_jiffies;
+
+ /*
+ * According to section 11.2.4.2 in the IBTA spec (Modify Queue Pair,
+ * table 91), both the QP timeout and the retry count have to be set
+ * for RC QP's during the RTR to RTS transition.
+ */
+ WARN_ON_ONCE((attr_mask & (IB_QP_TIMEOUT | IB_QP_RETRY_CNT)) !=
+ (IB_QP_TIMEOUT | IB_QP_RETRY_CNT));
+
+ /*
+ * Set target->rq_tmo_jiffies to one second more than the largest time
+ * it can take before an error completion is generated. See also
+ * C9-140..142 in the IBTA spec for more information about how to
+ * convert the QP Local ACK Timeout value to nanoseconds.
+ */
+ T_tr_ns = 4096 * (1ULL << qp_attr->timeout);
+ max_compl_time_ms = qp_attr->retry_cnt * 4 * T_tr_ns;
+ do_div(max_compl_time_ms, NSEC_PER_MSEC);
+ rq_tmo_jiffies = msecs_to_jiffies(max_compl_time_ms + 1000);
+
+ return rq_tmo_jiffies;
+}
+
static void srp_cm_rep_handler(struct ib_cm_id *cm_id,
struct srp_login_rsp *lrsp,
struct srp_target_port *target)
@@ -1478,6 +1535,8 @@ static void srp_cm_rep_handler(struct ib_cm_id *cm_id,
if (ret)
goto error_free;
+ target->rq_tmo_jiffies = srp_compute_rq_tmo(qp_attr, attr_mask);
+
ret = ib_modify_qp(target->qp, qp_attr, attr_mask);
if (ret)
goto error_free;
@@ -1599,6 +1658,7 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
case IB_CM_DREQ_RECEIVED:
shost_printk(KERN_WARNING, target->scsi_host,
PFX "DREQ received - connection closed\n");
+ srp_change_conn_state(target, false);
if (ib_send_cm_drep(cm_id, NULL, 0))
shost_printk(KERN_ERR, target->scsi_host,
PFX "Sending CM DREP failed\n");
@@ -1608,7 +1668,6 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
shost_printk(KERN_ERR, target->scsi_host,
PFX "connection closed\n");
- comp = 1;
target->status = 0;
break;
@@ -1636,10 +1695,6 @@ static int srp_send_tsk_mgmt(struct srp_target_port *target,
struct srp_iu *iu;
struct srp_tsk_mgmt *tsk_mgmt;
- if (target->state == SRP_TARGET_DEAD ||
- target->state == SRP_TARGET_REMOVED)
- return -1;
-
init_completion(&target->tsk_mgmt_done);
spin_lock_irq(&target->lock);
@@ -1729,6 +1784,21 @@ static int srp_reset_host(struct scsi_cmnd *scmnd)
return ret;
}
+static int srp_slave_configure(struct scsi_device *sdev)
+{
+ struct Scsi_Host *shost = sdev->host;
+ struct srp_target_port *target = host_to_target(shost);
+ struct request_queue *q = sdev->request_queue;
+ unsigned long timeout;
+
+ if (sdev->type == TYPE_DISK) {
+ timeout = max_t(unsigned, 30 * HZ, target->rq_tmo_jiffies);
+ blk_queue_rq_timeout(q, timeout);
+ }
+
+ return 0;
+}
+
static ssize_t show_id_ext(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -1861,6 +1931,7 @@ static struct scsi_host_template srp_template = {
.module = THIS_MODULE,
.name = "InfiniBand SRP initiator",
.proc_name = DRV_NAME,
+ .slave_configure = srp_slave_configure,
.info = srp_target_info,
.queuecommand = srp_queuecommand,
.eh_abort_handler = srp_abort,
@@ -1894,11 +1965,14 @@ static int srp_add_target(struct srp_host *host, struct srp_target_port *target)
return PTR_ERR(rport);
}
+ rport->lld_data = target;
+
spin_lock(&host->target_lock);
list_add_tail(&target->list, &host->target_list);
spin_unlock(&host->target_lock);
target->state = SRP_TARGET_LIVE;
+ target->connected = false;
scsi_scan_target(&target->scsi_host->shost_gendev,
0, target->scsi_id, SCAN_WILD_CARD, 0);
@@ -2188,6 +2262,7 @@ static ssize_t srp_create_target(struct device *dev,
sizeof (struct srp_indirect_buf) +
target->cmd_sg_cnt * sizeof (struct srp_direct_buf);
+ INIT_WORK(&target->remove_work, srp_remove_work);
spin_lock_init(&target->lock);
INIT_LIST_HEAD(&target->free_tx);
INIT_LIST_HEAD(&target->free_reqs);
@@ -2232,7 +2307,6 @@ static ssize_t srp_create_target(struct device *dev,
if (ret)
goto err_free_ib;
- target->qp_in_error = 0;
ret = srp_connect_target(target);
if (ret) {
shost_printk(KERN_ERR, target->scsi_host,
@@ -2422,8 +2496,7 @@ static void srp_remove_one(struct ib_device *device)
{
struct srp_device *srp_dev;
struct srp_host *host, *tmp_host;
- LIST_HEAD(target_list);
- struct srp_target_port *target, *tmp_target;
+ struct srp_target_port *target;
srp_dev = ib_get_client_data(device, &srp_client);
@@ -2436,35 +2509,17 @@ static void srp_remove_one(struct ib_device *device)
wait_for_completion(&host->released);
/*
- * Mark all target ports as removed, so we stop queueing
- * commands and don't try to reconnect.
+ * Remove all target ports.
*/
spin_lock(&host->target_lock);
- list_for_each_entry(target, &host->target_list, list) {
- spin_lock_irq(&target->lock);
- target->state = SRP_TARGET_REMOVED;
- spin_unlock_irq(&target->lock);
- }
+ list_for_each_entry(target, &host->target_list, list)
+ srp_queue_remove_work(target);
spin_unlock(&host->target_lock);
/*
- * Wait for any reconnection tasks that may have
- * started before we marked our target ports as
- * removed, and any target port removal tasks.
+ * Wait for target port removal tasks.
*/
- flush_workqueue(ib_wq);
-
- list_for_each_entry_safe(target, tmp_target,
- &host->target_list, list) {
- srp_del_scsi_host_attr(target->scsi_host);
- srp_remove_host(target->scsi_host);
- scsi_remove_host(target->scsi_host);
- srp_disconnect_target(target);
- ib_destroy_cm_id(target->cm_id);
- srp_free_target_ib(target);
- srp_free_req_data(target);
- scsi_host_put(target->scsi_host);
- }
+ flush_workqueue(system_long_wq);
kfree(host);
}
@@ -2478,6 +2533,7 @@ static void srp_remove_one(struct ib_device *device)
}
static struct srp_function_template ib_srp_transport_functions = {
+ .rport_delete = srp_rport_delete,
};
static int __init srp_init_module(void)
diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h
index 020caf0c378..de2d0b3c0bf 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.h
+++ b/drivers/infiniband/ulp/srp/ib_srp.h
@@ -80,9 +80,7 @@ enum {
enum srp_target_state {
SRP_TARGET_LIVE,
- SRP_TARGET_CONNECTING,
- SRP_TARGET_DEAD,
- SRP_TARGET_REMOVED
+ SRP_TARGET_REMOVED,
};
enum srp_iu_type {
@@ -163,6 +161,9 @@ struct srp_target_port {
struct ib_sa_query *path_query;
int path_query_id;
+ u32 rq_tmo_jiffies;
+ bool connected;
+
struct ib_cm_id *cm_id;
int max_ti_iu_len;
@@ -173,12 +174,12 @@ struct srp_target_port {
struct srp_iu *rx_ring[SRP_RQ_SIZE];
struct srp_request req_ring[SRP_CMD_SQ_SIZE];
- struct work_struct work;
+ struct work_struct remove_work;
struct list_head list;
struct completion done;
int status;
- int qp_in_error;
+ bool qp_in_error;
struct completion tsk_mgmt_done;
u8 tsk_mgmt_status;
diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c
index 1abbc170d8b..8c4b50fd9a7 100644
--- a/drivers/input/input-mt.c
+++ b/drivers/input/input-mt.c
@@ -251,7 +251,7 @@ void input_mt_sync_frame(struct input_dev *dev)
if (mt->flags & INPUT_MT_DROP_UNUSED) {
for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
- if (s->frame == mt->frame)
+ if (input_mt_is_used(mt, s))
continue;
input_mt_slot(dev, s - mt->slots);
input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 7c0f1ecfdd7..104a7c3153c 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -72,6 +72,16 @@ config INPUT_AD714X_SPI
To compile this driver as a module, choose M here: the
module will be called ad714x-spi.
+config INPUT_ARIZONA_HAPTICS
+ tristate "Arizona haptics support"
+ depends on MFD_ARIZONA && SND_SOC
+ select INPUT_FF_MEMLESS
+ help
+ Say Y to enable support for the haptics module in Arizona CODECs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called arizona-haptics.
+
config INPUT_BMA150
tristate "BMA150/SMB380 acceleration sensor support"
depends on I2C
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 83fe6f5b77d..5ea769eda99 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_INPUT_ADXL34X) += adxl34x.o
obj-$(CONFIG_INPUT_ADXL34X_I2C) += adxl34x-i2c.o
obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
+obj-$(CONFIG_INPUT_ARIZONA_HAPTICS) += arizona-haptics.o
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o
diff --git a/drivers/input/misc/arizona-haptics.c b/drivers/input/misc/arizona-haptics.c
new file mode 100644
index 00000000000..7a04f54ef96
--- /dev/null
+++ b/drivers/input/misc/arizona-haptics.c
@@ -0,0 +1,255 @@
+/*
+ * Arizona haptics driver
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/pdata.h>
+#include <linux/mfd/arizona/registers.h>
+
+struct arizona_haptics {
+ struct arizona *arizona;
+ struct input_dev *input_dev;
+ struct work_struct work;
+
+ struct mutex mutex;
+ u8 intensity;
+};
+
+static void arizona_haptics_work(struct work_struct *work)
+{
+ struct arizona_haptics *haptics = container_of(work,
+ struct arizona_haptics,
+ work);
+ struct arizona *arizona = haptics->arizona;
+ struct mutex *dapm_mutex = &arizona->dapm->card->dapm_mutex;
+ int ret;
+
+ if (!haptics->arizona->dapm) {
+ dev_err(arizona->dev, "No DAPM context\n");
+ return;
+ }
+
+ if (haptics->intensity) {
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_HAPTICS_PHASE_2_INTENSITY,
+ ARIZONA_PHASE2_INTENSITY_MASK,
+ haptics->intensity);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to set intensity: %d\n",
+ ret);
+ return;
+ }
+
+ /* This enable sequence will be a noop if already enabled */
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_HAPTICS_CONTROL_1,
+ ARIZONA_HAP_CTRL_MASK,
+ 1 << ARIZONA_HAP_CTRL_SHIFT);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to start haptics: %d\n",
+ ret);
+ return;
+ }
+
+ mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+ ret = snd_soc_dapm_enable_pin(arizona->dapm, "HAPTICS");
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to start HAPTICS: %d\n",
+ ret);
+ mutex_unlock(dapm_mutex);
+ return;
+ }
+
+ ret = snd_soc_dapm_sync(arizona->dapm);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to sync DAPM: %d\n",
+ ret);
+ mutex_unlock(dapm_mutex);
+ return;
+ }
+
+ mutex_unlock(dapm_mutex);
+
+ } else {
+ /* This disable sequence will be a noop if already enabled */
+ mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+ ret = snd_soc_dapm_disable_pin(arizona->dapm, "HAPTICS");
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n",
+ ret);
+ mutex_unlock(dapm_mutex);
+ return;
+ }
+
+ ret = snd_soc_dapm_sync(arizona->dapm);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to sync DAPM: %d\n",
+ ret);
+ mutex_unlock(dapm_mutex);
+ return;
+ }
+
+ mutex_unlock(dapm_mutex);
+
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_HAPTICS_CONTROL_1,
+ ARIZONA_HAP_CTRL_MASK,
+ 1 << ARIZONA_HAP_CTRL_SHIFT);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to stop haptics: %d\n",
+ ret);
+ return;
+ }
+ }
+}
+
+static int arizona_haptics_play(struct input_dev *input, void *data,
+ struct ff_effect *effect)
+{
+ struct arizona_haptics *haptics = input_get_drvdata(input);
+ struct arizona *arizona = haptics->arizona;
+
+ if (!arizona->dapm) {
+ dev_err(arizona->dev, "No DAPM context\n");
+ return -EBUSY;
+ }
+
+ if (effect->u.rumble.strong_magnitude) {
+ /* Scale the magnitude into the range the device supports */
+ if (arizona->pdata.hap_act) {
+ haptics->intensity =
+ effect->u.rumble.strong_magnitude >> 9;
+ if (effect->direction < 0x8000)
+ haptics->intensity += 0x7f;
+ } else {
+ haptics->intensity =
+ effect->u.rumble.strong_magnitude >> 8;
+ }
+ } else {
+ haptics->intensity = 0;
+ }
+
+ schedule_work(&haptics->work);
+
+ return 0;
+}
+
+static void arizona_haptics_close(struct input_dev *input)
+{
+ struct arizona_haptics *haptics = input_get_drvdata(input);
+ struct mutex *dapm_mutex = &haptics->arizona->dapm->card->dapm_mutex;
+
+ cancel_work_sync(&haptics->work);
+
+ mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+ if (haptics->arizona->dapm)
+ snd_soc_dapm_disable_pin(haptics->arizona->dapm, "HAPTICS");
+
+ mutex_unlock(dapm_mutex);
+}
+
+static int arizona_haptics_probe(struct platform_device *pdev)
+{
+ struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ struct arizona_haptics *haptics;
+ int ret;
+
+ haptics = devm_kzalloc(&pdev->dev, sizeof(*haptics), GFP_KERNEL);
+ if (!haptics)
+ return -ENOMEM;
+
+ haptics->arizona = arizona;
+
+ ret = regmap_update_bits(arizona->regmap, ARIZONA_HAPTICS_CONTROL_1,
+ ARIZONA_HAP_ACT, arizona->pdata.hap_act);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to set haptics actuator: %d\n",
+ ret);
+ return ret;
+ }
+
+ INIT_WORK(&haptics->work, arizona_haptics_work);
+
+ haptics->input_dev = input_allocate_device();
+ if (haptics->input_dev == NULL) {
+ dev_err(arizona->dev, "Failed to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ input_set_drvdata(haptics->input_dev, haptics);
+
+ haptics->input_dev->name = "arizona:haptics";
+ haptics->input_dev->dev.parent = pdev->dev.parent;
+ haptics->input_dev->close = arizona_haptics_close;
+ __set_bit(FF_RUMBLE, haptics->input_dev->ffbit);
+
+ ret = input_ff_create_memless(haptics->input_dev, NULL,
+ arizona_haptics_play);
+ if (ret < 0) {
+ dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n",
+ ret);
+ goto err_ialloc;
+ }
+
+ ret = input_register_device(haptics->input_dev);
+ if (ret < 0) {
+ dev_err(arizona->dev, "couldn't register input device: %d\n",
+ ret);
+ goto err_iff;
+ }
+
+ platform_set_drvdata(pdev, haptics);
+
+ return 0;
+
+err_iff:
+ if (haptics->input_dev)
+ input_ff_destroy(haptics->input_dev);
+err_ialloc:
+ input_free_device(haptics->input_dev);
+
+ return ret;
+}
+
+static int arizona_haptics_remove(struct platform_device *pdev)
+{
+ struct arizona_haptics *haptics = platform_get_drvdata(pdev);
+
+ input_unregister_device(haptics->input_dev);
+
+ return 0;
+}
+
+static struct platform_driver arizona_haptics_driver = {
+ .probe = arizona_haptics_probe,
+ .remove = arizona_haptics_remove,
+ .driver = {
+ .name = "arizona-haptics",
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(arizona_haptics_driver);
+
+MODULE_ALIAS("platform:arizona-haptics");
+MODULE_DESCRIPTION("Arizona haptics driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 02bd37a6187..bf4609a5bd9 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -1,3 +1,4 @@
-obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o
-obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o
-obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
+obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o
+obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o
+obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
+obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c
new file mode 100644
index 00000000000..80e1d2fd9d4
--- /dev/null
+++ b/drivers/irqchip/spear-shirq.c
@@ -0,0 +1,316 @@
+/*
+ * SPEAr platform shared irq layer source file
+ *
+ * Copyright (C) 2009-2012 ST Microelectronics
+ * Viresh Kumar <viresh.linux@gmail.com>
+ *
+ * Copyright (C) 2012 ST Microelectronics
+ * Shiraz Hashim <shiraz.hashim@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/spear-shirq.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/spinlock.h>
+
+static DEFINE_SPINLOCK(lock);
+
+/* spear300 shared irq registers offsets and masks */
+#define SPEAR300_INT_ENB_MASK_REG 0x54
+#define SPEAR300_INT_STS_MASK_REG 0x58
+
+static struct spear_shirq spear300_shirq_ras1 = {
+ .irq_nr = 9,
+ .irq_bit_off = 0,
+ .regs = {
+ .enb_reg = SPEAR300_INT_ENB_MASK_REG,
+ .status_reg = SPEAR300_INT_STS_MASK_REG,
+ .clear_reg = -1,
+ },
+};
+
+static struct spear_shirq *spear300_shirq_blocks[] = {
+ &spear300_shirq_ras1,
+};
+
+/* spear310 shared irq registers offsets and masks */
+#define SPEAR310_INT_STS_MASK_REG 0x04
+
+static struct spear_shirq spear310_shirq_ras1 = {
+ .irq_nr = 8,
+ .irq_bit_off = 0,
+ .regs = {
+ .enb_reg = -1,
+ .status_reg = SPEAR310_INT_STS_MASK_REG,
+ .clear_reg = -1,
+ },
+};
+
+static struct spear_shirq spear310_shirq_ras2 = {
+ .irq_nr = 5,
+ .irq_bit_off = 8,
+ .regs = {
+ .enb_reg = -1,
+ .status_reg = SPEAR310_INT_STS_MASK_REG,
+ .clear_reg = -1,
+ },
+};
+
+static struct spear_shirq spear310_shirq_ras3 = {
+ .irq_nr = 1,
+ .irq_bit_off = 13,
+ .regs = {
+ .enb_reg = -1,
+ .status_reg = SPEAR310_INT_STS_MASK_REG,
+ .clear_reg = -1,
+ },
+};
+
+static struct spear_shirq spear310_shirq_intrcomm_ras = {
+ .irq_nr = 3,
+ .irq_bit_off = 14,
+ .regs = {
+ .enb_reg = -1,
+ .status_reg = SPEAR310_INT_STS_MASK_REG,
+ .clear_reg = -1,
+ },
+};
+
+static struct spear_shirq *spear310_shirq_blocks[] = {
+ &spear310_shirq_ras1,
+ &spear310_shirq_ras2,
+ &spear310_shirq_ras3,
+ &spear310_shirq_intrcomm_ras,
+};
+
+/* spear320 shared irq registers offsets and masks */
+#define SPEAR320_INT_STS_MASK_REG 0x04
+#define SPEAR320_INT_CLR_MASK_REG 0x04
+#define SPEAR320_INT_ENB_MASK_REG 0x08
+
+static struct spear_shirq spear320_shirq_ras1 = {
+ .irq_nr = 3,
+ .irq_bit_off = 7,
+ .regs = {
+ .enb_reg = -1,
+ .status_reg = SPEAR320_INT_STS_MASK_REG,
+ .clear_reg = SPEAR320_INT_CLR_MASK_REG,
+ .reset_to_clear = 1,
+ },
+};
+
+static struct spear_shirq spear320_shirq_ras2 = {
+ .irq_nr = 1,
+ .irq_bit_off = 10,
+ .regs = {
+ .enb_reg = -1,
+ .status_reg = SPEAR320_INT_STS_MASK_REG,
+ .clear_reg = SPEAR320_INT_CLR_MASK_REG,
+ .reset_to_clear = 1,
+ },
+};
+
+static struct spear_shirq spear320_shirq_ras3 = {
+ .irq_nr = 3,
+ .irq_bit_off = 0,
+ .invalid_irq = 1,
+ .regs = {
+ .enb_reg = SPEAR320_INT_ENB_MASK_REG,
+ .reset_to_enb = 1,
+ .status_reg = SPEAR320_INT_STS_MASK_REG,
+ .clear_reg = SPEAR320_INT_CLR_MASK_REG,
+ .reset_to_clear = 1,
+ },
+};
+
+static struct spear_shirq spear320_shirq_intrcomm_ras = {
+ .irq_nr = 11,
+ .irq_bit_off = 11,
+ .regs = {
+ .enb_reg = -1,
+ .status_reg = SPEAR320_INT_STS_MASK_REG,
+ .clear_reg = SPEAR320_INT_CLR_MASK_REG,
+ .reset_to_clear = 1,
+ },
+};
+
+static struct spear_shirq *spear320_shirq_blocks[] = {
+ &spear320_shirq_ras3,
+ &spear320_shirq_ras1,
+ &spear320_shirq_ras2,
+ &spear320_shirq_intrcomm_ras,
+};
+
+static void shirq_irq_mask_unmask(struct irq_data *d, bool mask)
+{
+ struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
+ u32 val, offset = d->irq - shirq->irq_base;
+ unsigned long flags;
+
+ if (shirq->regs.enb_reg == -1)
+ return;
+
+ spin_lock_irqsave(&lock, flags);
+ val = readl(shirq->base + shirq->regs.enb_reg);
+
+ if (mask ^ shirq->regs.reset_to_enb)
+ val &= ~(0x1 << shirq->irq_bit_off << offset);
+ else
+ val |= 0x1 << shirq->irq_bit_off << offset;
+
+ writel(val, shirq->base + shirq->regs.enb_reg);
+ spin_unlock_irqrestore(&lock, flags);
+
+}
+
+static void shirq_irq_mask(struct irq_data *d)
+{
+ shirq_irq_mask_unmask(d, 1);
+}
+
+static void shirq_irq_unmask(struct irq_data *d)
+{
+ shirq_irq_mask_unmask(d, 0);
+}
+
+static struct irq_chip shirq_chip = {
+ .name = "spear-shirq",
+ .irq_ack = shirq_irq_mask,
+ .irq_mask = shirq_irq_mask,
+ .irq_unmask = shirq_irq_unmask,
+};
+
+static void shirq_handler(unsigned irq, struct irq_desc *desc)
+{
+ u32 i, j, val, mask, tmp;
+ struct irq_chip *chip;
+ struct spear_shirq *shirq = irq_get_handler_data(irq);
+
+ chip = irq_get_chip(irq);
+ chip->irq_ack(&desc->irq_data);
+
+ mask = ((0x1 << shirq->irq_nr) - 1) << shirq->irq_bit_off;
+ while ((val = readl(shirq->base + shirq->regs.status_reg) &
+ mask)) {
+
+ val >>= shirq->irq_bit_off;
+ for (i = 0, j = 1; i < shirq->irq_nr; i++, j <<= 1) {
+
+ if (!(j & val))
+ continue;
+
+ generic_handle_irq(shirq->irq_base + i);
+
+ /* clear interrupt */
+ if (shirq->regs.clear_reg == -1)
+ continue;
+
+ tmp = readl(shirq->base + shirq->regs.clear_reg);
+ if (shirq->regs.reset_to_clear)
+ tmp &= ~(j << shirq->irq_bit_off);
+ else
+ tmp |= (j << shirq->irq_bit_off);
+ writel(tmp, shirq->base + shirq->regs.clear_reg);
+ }
+ }
+ chip->irq_unmask(&desc->irq_data);
+}
+
+static void __init spear_shirq_register(struct spear_shirq *shirq)
+{
+ int i;
+
+ if (shirq->invalid_irq)
+ return;
+
+ irq_set_chained_handler(shirq->irq, shirq_handler);
+ for (i = 0; i < shirq->irq_nr; i++) {
+ irq_set_chip_and_handler(shirq->irq_base + i,
+ &shirq_chip, handle_simple_irq);
+ set_irq_flags(shirq->irq_base + i, IRQF_VALID);
+ irq_set_chip_data(shirq->irq_base + i, shirq);
+ }
+
+ irq_set_handler_data(shirq->irq, shirq);
+}
+
+static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr,
+ struct device_node *np)
+{
+ int i, irq_base, hwirq = 0, irq_nr = 0;
+ static struct irq_domain *shirq_domain;
+ void __iomem *base;
+
+ base = of_iomap(np, 0);
+ if (!base) {
+ pr_err("%s: failed to map shirq registers\n", __func__);
+ return -ENXIO;
+ }
+
+ for (i = 0; i < block_nr; i++)
+ irq_nr += shirq_blocks[i]->irq_nr;
+
+ irq_base = irq_alloc_descs(-1, 0, irq_nr, 0);
+ if (IS_ERR_VALUE(irq_base)) {
+ pr_err("%s: irq desc alloc failed\n", __func__);
+ goto err_unmap;
+ }
+
+ shirq_domain = irq_domain_add_legacy(np, irq_nr, irq_base, 0,
+ &irq_domain_simple_ops, NULL);
+ if (WARN_ON(!shirq_domain)) {
+ pr_warn("%s: irq domain init failed\n", __func__);
+ goto err_free_desc;
+ }
+
+ for (i = 0; i < block_nr; i++) {
+ shirq_blocks[i]->base = base;
+ shirq_blocks[i]->irq_base = irq_find_mapping(shirq_domain,
+ hwirq);
+ shirq_blocks[i]->irq = irq_of_parse_and_map(np, i);
+
+ spear_shirq_register(shirq_blocks[i]);
+ hwirq += shirq_blocks[i]->irq_nr;
+ }
+
+ return 0;
+
+err_free_desc:
+ irq_free_descs(irq_base, irq_nr);
+err_unmap:
+ iounmap(base);
+ return -ENXIO;
+}
+
+int __init spear300_shirq_of_init(struct device_node *np,
+ struct device_node *parent)
+{
+ return shirq_init(spear300_shirq_blocks,
+ ARRAY_SIZE(spear300_shirq_blocks), np);
+}
+
+int __init spear310_shirq_of_init(struct device_node *np,
+ struct device_node *parent)
+{
+ return shirq_init(spear310_shirq_blocks,
+ ARRAY_SIZE(spear310_shirq_blocks), np);
+}
+
+int __init spear320_shirq_of_init(struct device_node *np,
+ struct device_node *parent)
+{
+ return shirq_init(spear320_shirq_blocks,
+ ARRAY_SIZE(spear320_shirq_blocks), np);
+}
diff --git a/drivers/isdn/hisax/amd7930_fn.c b/drivers/isdn/hisax/amd7930_fn.c
index 89342f7e0c5..525471e776a 100644
--- a/drivers/isdn/hisax/amd7930_fn.c
+++ b/drivers/isdn/hisax/amd7930_fn.c
@@ -628,7 +628,7 @@ Amd7930_l1hw(struct PStack *st, int pr, void *arg)
if ((cs->dc.amd7930.ph_state == 8)) {
/* b-channels off, PH-AR cleared
* change to F3 */
- Amd7930_ph_command(cs, 0x20, "HW_RESET REQEST"); //LMR1 bit 5
+ Amd7930_ph_command(cs, 0x20, "HW_RESET REQUEST"); //LMR1 bit 5
spin_unlock_irqrestore(&cs->lock, flags);
} else {
Amd7930_ph_command(cs, 0x40, "HW_RESET REQUEST");
diff --git a/drivers/isdn/hisax/isar.c b/drivers/isdn/hisax/isar.c
index ff5e139f485..7fdf34704fe 100644
--- a/drivers/isdn/hisax/isar.c
+++ b/drivers/isdn/hisax/isar.c
@@ -1417,7 +1417,7 @@ modeisar(struct BCState *bcs, int mode, int bc)
&bcs->hw.isar.reg->Flags))
bcs->hw.isar.dpath = 1;
else {
- printk(KERN_WARNING"isar modeisar both pathes in use\n");
+ printk(KERN_WARNING"isar modeisar both paths in use\n");
return (1);
}
break;
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 48cce18e9d6..a20752f562b 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -211,7 +211,7 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
led_trigger_set_default(led_cdev);
#endif
- printk(KERN_DEBUG "Registered led device: %s\n",
+ dev_dbg(parent, "Registered led device: %s\n",
led_cdev->name);
return 0;
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index 262eb419371..3c972b2f989 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -166,6 +166,19 @@ void led_trigger_set_default(struct led_classdev *led_cdev)
}
EXPORT_SYMBOL_GPL(led_trigger_set_default);
+void led_trigger_rename_static(const char *name, struct led_trigger *trig)
+{
+ /* new name must be on a temporary string to prevent races */
+ BUG_ON(name == trig->name);
+
+ down_write(&triggers_list_lock);
+ /* this assumes that trig->name was originaly allocated to
+ * non constant storage */
+ strcpy((char *)trig->name, name);
+ up_write(&triggers_list_lock);
+}
+EXPORT_SYMBOL_GPL(led_trigger_rename_static);
+
/* LED Trigger Interface */
int led_trigger_register(struct led_trigger *trig)
@@ -300,13 +313,13 @@ void led_trigger_register_simple(const char *name, struct led_trigger **tp)
if (err < 0) {
kfree(trig);
trig = NULL;
- printk(KERN_WARNING "LED trigger %s failed to register"
- " (%d)\n", name, err);
+ pr_warn("LED trigger %s failed to register (%d)\n",
+ name, err);
}
- } else
- printk(KERN_WARNING "LED trigger %s failed to register"
- " (no memory)\n", name);
-
+ } else {
+ pr_warn("LED trigger %s failed to register (no memory)\n",
+ name);
+ }
*tp = trig;
}
EXPORT_SYMBOL_GPL(led_trigger_register_simple);
diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c
index b7e8cc0957f..6be2edd4117 100644
--- a/drivers/leds/leds-88pm860x.c
+++ b/drivers/leds/leds-88pm860x.c
@@ -165,15 +165,13 @@ static int pm860x_led_probe(struct platform_device *pdev)
res = platform_get_resource_byname(pdev, IORESOURCE_REG, "control");
if (!res) {
dev_err(&pdev->dev, "No REG resource for control\n");
- ret = -ENXIO;
- goto out;
+ return -ENXIO;
}
data->reg_control = res->start;
res = platform_get_resource_byname(pdev, IORESOURCE_REG, "blink");
if (!res) {
dev_err(&pdev->dev, "No REG resource for blink\n");
- ret = -ENXIO;
- goto out;
+ return -ENXIO;
}
data->reg_blink = res->start;
memset(data->name, 0, MFD_NAME_SIZE);
@@ -224,9 +222,6 @@ static int pm860x_led_probe(struct platform_device *pdev)
}
pm860x_led_set(&data->cdev, 0);
return 0;
-out:
- devm_kfree(&pdev->dev, data);
- return ret;
}
static int pm860x_led_remove(struct platform_device *pdev)
diff --git a/drivers/leds/leds-adp5520.c b/drivers/leds/leds-adp5520.c
index dcd9128a51a..e8072abe76e 100644
--- a/drivers/leds/leds-adp5520.c
+++ b/drivers/leds/leds-adp5520.c
@@ -5,10 +5,10 @@
*
* Loosely derived from leds-da903x:
* Copyright (C) 2008 Compulab, Ltd.
- * Mike Rapoport <mike@compulab.co.il>
+ * Mike Rapoport <mike@compulab.co.il>
*
* Copyright (C) 2006-2008 Marvell International Ltd.
- * Eric Miao <eric.miao@marvell.com>
+ * Eric Miao <eric.miao@marvell.com>
*
* Licensed under the GPL-2 or later.
*/
diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c
index 9abe8de40ed..851517030cc 100644
--- a/drivers/leds/leds-bd2802.c
+++ b/drivers/leds/leds-bd2802.c
@@ -26,8 +26,8 @@
#define BD2802_LED_OFFSET 0xa
#define BD2802_COLOR_OFFSET 0x3
-#define BD2802_REG_CLKSETUP 0x00
-#define BD2802_REG_CONTROL 0x01
+#define BD2802_REG_CLKSETUP 0x00
+#define BD2802_REG_CONTROL 0x01
#define BD2802_REG_HOURSETUP 0x02
#define BD2802_REG_CURRENT1SETUP 0x03
#define BD2802_REG_CURRENT2SETUP 0x04
@@ -93,7 +93,7 @@ struct bd2802_led {
* In ADF mode, user can set registers of BD2802GU directly,
* therefore BD2802GU doesn't enter reset state.
*/
- int adf_on;
+ int adf_on;
enum led_ids led_id;
enum led_colors color;
@@ -328,7 +328,7 @@ static ssize_t bd2802_store_reg##reg_addr(struct device *dev, \
int ret; \
if (!count) \
return -EINVAL; \
- ret = strict_strtoul(buf, 16, &val); \
+ ret = kstrtoul(buf, 16, &val); \
if (ret) \
return ret; \
down_write(&led->rwsem); \
@@ -492,7 +492,7 @@ static ssize_t bd2802_store_##attr_name(struct device *dev, \
int ret; \
if (!count) \
return -EINVAL; \
- ret = strict_strtoul(buf, 16, &val); \
+ ret = kstrtoul(buf, 16, &val); \
if (ret) \
return ret; \
down_write(&led->rwsem); \
diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c
index b02547052e1..6a8405df76a 100644
--- a/drivers/leds/leds-clevo-mail.c
+++ b/drivers/leds/leds-clevo-mail.c
@@ -1,3 +1,4 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
@@ -26,7 +27,7 @@ static struct platform_device *pdev;
static int __init clevo_mail_led_dmi_callback(const struct dmi_system_id *id)
{
- printk(KERN_INFO KBUILD_MODNAME ": '%s' found\n", id->ident);
+ pr_info("'%s' found\n", id->ident);
return 1;
}
@@ -135,8 +136,7 @@ static int clevo_mail_led_blink(struct led_classdev *led_cdev,
status = 0;
} else {
- printk(KERN_DEBUG KBUILD_MODNAME
- ": clevo_mail_led_blink(..., %lu, %lu),"
+ pr_debug("clevo_mail_led_blink(..., %lu, %lu),"
" returning -EINVAL (unsupported)\n",
*delay_on, *delay_off);
}
@@ -183,7 +183,7 @@ static int __init clevo_mail_led_init(void)
count = dmi_check_system(clevo_mail_led_dmi_table);
} else {
count = 1;
- printk(KERN_ERR KBUILD_MODNAME ": Skipping DMI detection. "
+ pr_err("Skipping DMI detection. "
"If the driver works on your hardware please "
"report model and the output of dmidecode in tracker "
"at http://sourceforge.net/projects/clevo-mailled/\n");
@@ -197,8 +197,7 @@ static int __init clevo_mail_led_init(void)
error = platform_driver_probe(&clevo_mail_led_driver,
clevo_mail_led_probe);
if (error) {
- printk(KERN_ERR KBUILD_MODNAME
- ": Can't probe platform driver\n");
+ pr_err("Can't probe platform driver\n");
platform_device_unregister(pdev);
}
} else
diff --git a/drivers/leds/leds-cobalt-qube.c b/drivers/leds/leds-cobalt-qube.c
index ffa99303b62..8abcb66db01 100644
--- a/drivers/leds/leds-cobalt-qube.c
+++ b/drivers/leds/leds-cobalt-qube.c
@@ -43,7 +43,7 @@ static int cobalt_qube_led_probe(struct platform_device *pdev)
if (!res)
return -EBUSY;
- led_port = ioremap(res->start, resource_size(res));
+ led_port = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!led_port)
return -ENOMEM;
@@ -52,12 +52,11 @@ static int cobalt_qube_led_probe(struct platform_device *pdev)
retval = led_classdev_register(&pdev->dev, &qube_front_led);
if (retval)
- goto err_iounmap;
+ goto err_null;
return 0;
-err_iounmap:
- iounmap(led_port);
+err_null:
led_port = NULL;
return retval;
@@ -67,10 +66,8 @@ static int cobalt_qube_led_remove(struct platform_device *pdev)
{
led_classdev_unregister(&qube_front_led);
- if (led_port) {
- iounmap(led_port);
+ if (led_port)
led_port = NULL;
- }
return 0;
}
diff --git a/drivers/leds/leds-cobalt-raq.c b/drivers/leds/leds-cobalt-raq.c
index d52e47de396..001088b3137 100644
--- a/drivers/leds/leds-cobalt-raq.c
+++ b/drivers/leds/leds-cobalt-raq.c
@@ -85,13 +85,13 @@ static int cobalt_raq_led_probe(struct platform_device *pdev)
if (!res)
return -EBUSY;
- led_port = ioremap(res->start, resource_size(res));
+ led_port = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!led_port)
return -ENOMEM;
retval = led_classdev_register(&pdev->dev, &raq_power_off_led);
if (retval)
- goto err_iounmap;
+ goto err_null;
retval = led_classdev_register(&pdev->dev, &raq_web_led);
if (retval)
@@ -102,8 +102,7 @@ static int cobalt_raq_led_probe(struct platform_device *pdev)
err_unregister:
led_classdev_unregister(&raq_power_off_led);
-err_iounmap:
- iounmap(led_port);
+err_null:
led_port = NULL;
return retval;
@@ -114,10 +113,8 @@ static int cobalt_raq_led_remove(struct platform_device *pdev)
led_classdev_unregister(&raq_power_off_led);
led_classdev_unregister(&raq_web_led);
- if (led_port) {
- iounmap(led_port);
+ if (led_port)
led_port = NULL;
- }
return 0;
}
diff --git a/drivers/leds/leds-da903x.c b/drivers/leds/leds-da903x.c
index 6f31b776765..c263a21db82 100644
--- a/drivers/leds/leds-da903x.c
+++ b/drivers/leds/leds-da903x.c
@@ -2,10 +2,10 @@
* LEDs driver for Dialog Semiconductor DA9030/DA9034
*
* Copyright (C) 2008 Compulab, Ltd.
- * Mike Rapoport <mike@compulab.co.il>
+ * Mike Rapoport <mike@compulab.co.il>
*
* Copyright (C) 2006-2008 Marvell International Ltd.
- * Eric Miao <eric.miao@marvell.com>
+ * Eric Miao <eric.miao@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -85,7 +85,7 @@ static void da903x_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct da903x_led *led;
-
+
led = container_of(led_cdev, struct da903x_led, cdev);
led->new_brightness = value;
schedule_work(&led->work);
@@ -156,7 +156,7 @@ static struct platform_driver da903x_led_driver = {
module_platform_driver(da903x_led_driver);
MODULE_DESCRIPTION("LEDs driver for Dialog Semiconductor DA9030/DA9034");
-MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"
- "Mike Rapoport <mike@compulab.co.il>");
+MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:da903x-led");
diff --git a/drivers/leds/leds-fsg.c b/drivers/leds/leds-fsg.c
index b9053fa6e25..b4d5a44cc41 100644
--- a/drivers/leds/leds-fsg.c
+++ b/drivers/leds/leds-fsg.c
@@ -20,8 +20,8 @@
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/module.h>
+#include <linux/io.h>
#include <mach/hardware.h>
-#include <asm/io.h>
#define FSG_LED_WLAN_BIT 0
#define FSG_LED_WAN_BIT 1
@@ -149,11 +149,10 @@ static int fsg_led_probe(struct platform_device *pdev)
int ret;
/* Map the LED chip select address space */
- latch_address = (unsigned short *) ioremap(IXP4XX_EXP_BUS_BASE(2), 512);
- if (!latch_address) {
- ret = -ENOMEM;
- goto failremap;
- }
+ latch_address = (unsigned short *) devm_ioremap(&pdev->dev,
+ IXP4XX_EXP_BUS_BASE(2), 512);
+ if (!latch_address)
+ return -ENOMEM;
latch_value = 0xffff;
*latch_address = latch_value;
@@ -195,8 +194,6 @@ static int fsg_led_probe(struct platform_device *pdev)
failwan:
led_classdev_unregister(&fsg_wlan_led);
failwlan:
- iounmap(latch_address);
- failremap:
return ret;
}
@@ -210,8 +207,6 @@ static int fsg_led_remove(struct platform_device *pdev)
led_classdev_unregister(&fsg_sync_led);
led_classdev_unregister(&fsg_ring_led);
- iounmap(latch_address);
-
return 0;
}
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
index 291c20797ca..1885a26776b 100644
--- a/drivers/leds/leds-gpio.c
+++ b/drivers/leds/leds-gpio.c
@@ -21,6 +21,7 @@
#include <linux/workqueue.h>
#include <linux/module.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/err.h>
struct gpio_led_data {
struct led_classdev cdev;
@@ -101,15 +102,11 @@ static int create_gpio_led(const struct gpio_led *template,
/* skip leds that aren't available */
if (!gpio_is_valid(template->gpio)) {
- printk(KERN_INFO "Skipping unavailable LED gpio %d (%s)\n",
+ dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n",
template->gpio, template->name);
return 0;
}
- ret = gpio_request(template->gpio, template->name);
- if (ret < 0)
- return ret;
-
led_dat->cdev.name = template->name;
led_dat->cdev.default_trigger = template->default_trigger;
led_dat->gpio = template->gpio;
@@ -129,20 +126,19 @@ static int create_gpio_led(const struct gpio_led *template,
if (!template->retain_state_suspended)
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
- ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state);
+ ret = devm_gpio_request_one(parent, template->gpio,
+ GPIOF_DIR_OUT | (led_dat->active_low ^ state),
+ template->name);
if (ret < 0)
- goto err;
-
+ return ret;
+
INIT_WORK(&led_dat->work, gpio_led_work);
ret = led_classdev_register(parent, &led_dat->cdev);
if (ret < 0)
- goto err;
+ return ret;
return 0;
-err:
- gpio_free(led_dat->gpio);
- return ret;
}
static void delete_gpio_led(struct gpio_led_data *led)
@@ -151,7 +147,6 @@ static void delete_gpio_led(struct gpio_led_data *led)
return;
led_classdev_unregister(&led->cdev);
cancel_work_sync(&led->work);
- gpio_free(led->gpio);
}
struct gpio_leds_priv {
@@ -176,12 +171,16 @@ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
/* count LEDs in this device, so we know how much to allocate */
count = of_get_child_count(np);
if (!count)
- return NULL;
+ return ERR_PTR(-ENODEV);
+
+ for_each_child_of_node(np, child)
+ if (of_get_gpio(child, 0) == -EPROBE_DEFER)
+ return ERR_PTR(-EPROBE_DEFER);
priv = devm_kzalloc(&pdev->dev, sizeof_gpio_leds_priv(count),
GFP_KERNEL);
if (!priv)
- return NULL;
+ return ERR_PTR(-ENOMEM);
for_each_child_of_node(np, child) {
struct gpio_led led = {};
@@ -216,7 +215,7 @@ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
err:
for (count = priv->num_leds - 2; count >= 0; count--)
delete_gpio_led(&priv->leds[count]);
- return NULL;
+ return ERR_PTR(-ENODEV);
}
static const struct of_device_id of_gpio_leds_match[] = {
@@ -226,7 +225,7 @@ static const struct of_device_id of_gpio_leds_match[] = {
#else /* CONFIG_OF_GPIO */
static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
{
- return NULL;
+ return ERR_PTR(-ENODEV);
}
#endif /* CONFIG_OF_GPIO */
@@ -264,8 +263,8 @@ static int gpio_led_probe(struct platform_device *pdev)
}
} else {
priv = gpio_leds_create_of(pdev);
- if (!priv)
- return -ENODEV;
+ if (IS_ERR(priv))
+ return PTR_ERR(priv);
}
platform_set_drvdata(pdev, priv);
diff --git a/drivers/leds/leds-lm355x.c b/drivers/leds/leds-lm355x.c
index b13ce037191..65d79284c48 100644
--- a/drivers/leds/leds-lm355x.c
+++ b/drivers/leds/leds-lm355x.c
@@ -408,10 +408,10 @@ static ssize_t lm3556_indicator_pattern_store(struct device *dev,
return size;
out:
dev_err(chip->dev, "%s:i2c access fail to register\n", __func__);
- return size;
+ return ret;
}
-static DEVICE_ATTR(pattern, 0666, NULL, lm3556_indicator_pattern_store);
+static DEVICE_ATTR(pattern, S_IWUSR, NULL, lm3556_indicator_pattern_store);
static const struct regmap_config lm355x_regmap = {
.reg_bits = 8,
diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c
index 215a7c1e56c..07b3dde9061 100644
--- a/drivers/leds/leds-lm3642.c
+++ b/drivers/leds/leds-lm3642.c
@@ -201,13 +201,13 @@ static ssize_t lm3642_torch_pin_store(struct device *dev,
return size;
out:
dev_err(chip->dev, "%s:i2c access fail to register\n", __func__);
- return size;
+ return ret;
out_strtoint:
dev_err(chip->dev, "%s: fail to change str to int\n", __func__);
- return size;
+ return ret;
}
-static DEVICE_ATTR(torch_pin, 0666, NULL, lm3642_torch_pin_store);
+static DEVICE_ATTR(torch_pin, S_IWUSR, NULL, lm3642_torch_pin_store);
static void lm3642_deferred_torch_brightness_set(struct work_struct *work)
{
@@ -258,13 +258,13 @@ static ssize_t lm3642_strobe_pin_store(struct device *dev,
return size;
out:
dev_err(chip->dev, "%s:i2c access fail to register\n", __func__);
- return size;
+ return ret;
out_strtoint:
dev_err(chip->dev, "%s: fail to change str to int\n", __func__);
- return size;
+ return ret;
}
-static DEVICE_ATTR(strobe_pin, 0666, NULL, lm3642_strobe_pin_store);
+static DEVICE_ATTR(strobe_pin, S_IWUSR, NULL, lm3642_strobe_pin_store);
static void lm3642_deferred_strobe_brightness_set(struct work_struct *work)
{
diff --git a/drivers/leds/leds-lp3944.c b/drivers/leds/leds-lp3944.c
index b081f67e1de..0c4386e656c 100644
--- a/drivers/leds/leds-lp3944.c
+++ b/drivers/leds/leds-lp3944.c
@@ -86,7 +86,7 @@ static int lp3944_reg_read(struct i2c_client *client, u8 reg, u8 *value)
tmp = i2c_smbus_read_byte_data(client, reg);
if (tmp < 0)
- return -EINVAL;
+ return tmp;
*value = tmp;
diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
index 966f158a07d..cb8a5220200 100644
--- a/drivers/leds/leds-lp5521.c
+++ b/drivers/leds/leds-lp5521.c
@@ -152,7 +152,7 @@ static int lp5521_read(struct i2c_client *client, u8 reg, u8 *buf)
ret = i2c_smbus_read_byte_data(client, reg);
if (ret < 0)
- return -EIO;
+ return ret;
*buf = ret;
return 0;
@@ -616,7 +616,7 @@ static ssize_t store_led_pattern(struct device *dev,
unsigned long val;
int ret;
- ret = strict_strtoul(buf, 16, &val);
+ ret = kstrtoul(buf, 16, &val);
if (ret)
return ret;
@@ -788,10 +788,17 @@ static int lp5521_probe(struct i2c_client *client,
* LP5521_REG_ENABLE register will not have any effect - strange!
*/
ret = lp5521_read(client, LP5521_REG_R_CURRENT, &buf);
- if (ret || buf != LP5521_REG_R_CURR_DEFAULT) {
+ if (ret) {
dev_err(&client->dev, "error in resetting chip\n");
goto fail2;
}
+ if (buf != LP5521_REG_R_CURR_DEFAULT) {
+ dev_err(&client->dev,
+ "unexpected data in register (expected 0x%x got 0x%x)\n",
+ LP5521_REG_R_CURR_DEFAULT, buf);
+ ret = -EINVAL;
+ goto fail2;
+ }
usleep_range(10000, 20000);
ret = lp5521_detect(client);
diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c
index 7e304b7ff77..7f5be8948cd 100644
--- a/drivers/leds/leds-lp5523.c
+++ b/drivers/leds/leds-lp5523.c
@@ -171,7 +171,7 @@ static int lp5523_read(struct i2c_client *client, u8 reg, u8 *buf)
s32 ret = i2c_smbus_read_byte_data(client, reg);
if (ret < 0)
- return -EIO;
+ return ret;
*buf = ret;
return 0;
@@ -248,7 +248,10 @@ static int lp5523_configure(struct i2c_client *client)
/* Let the programs run for couple of ms and check the engine status */
usleep_range(3000, 6000);
- lp5523_read(client, LP5523_REG_STATUS, &status);
+ ret = lp5523_read(client, LP5523_REG_STATUS, &status);
+ if (ret < 0)
+ return ret;
+
status &= LP5523_ENG_STATUS_MASK;
if (status == LP5523_ENG_STATUS_MASK) {
@@ -464,10 +467,16 @@ static ssize_t lp5523_selftest(struct device *dev,
LP5523_EN_LEDTEST | 16);
usleep_range(3000, 6000); /* ADC conversion time is typically 2.7 ms */
ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status);
+ if (ret < 0)
+ goto fail;
+
if (!(status & LP5523_LEDTEST_DONE))
usleep_range(3000, 6000); /* Was not ready. Wait little bit */
- ret |= lp5523_read(chip->client, LP5523_REG_LED_TEST_ADC, &vdd);
+ ret = lp5523_read(chip->client, LP5523_REG_LED_TEST_ADC, &vdd);
+ if (ret < 0)
+ goto fail;
+
vdd--; /* There may be some fluctuation in measurement */
for (i = 0; i < LP5523_LEDS; i++) {
@@ -489,9 +498,14 @@ static ssize_t lp5523_selftest(struct device *dev,
/* ADC conversion time is 2.7 ms typically */
usleep_range(3000, 6000);
ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status);
+ if (ret < 0)
+ goto fail;
+
if (!(status & LP5523_LEDTEST_DONE))
usleep_range(3000, 6000);/* Was not ready. Wait. */
- ret |= lp5523_read(chip->client, LP5523_REG_LED_TEST_ADC, &adc);
+ ret = lp5523_read(chip->client, LP5523_REG_LED_TEST_ADC, &adc);
+ if (ret < 0)
+ goto fail;
if (adc >= vdd || adc < LP5523_ADC_SHORTCIRC_LIM)
pos += sprintf(buf + pos, "LED %d FAIL\n", i);
@@ -696,7 +710,7 @@ static ssize_t store_current(struct device *dev,
ssize_t ret;
unsigned long curr;
- if (strict_strtoul(buf, 0, &curr))
+ if (kstrtoul(buf, 0, &curr))
return -EINVAL;
if (curr > led->max_current)
diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c
index 34b3ba4376f..c9b9e1fec58 100644
--- a/drivers/leds/leds-lt3593.c
+++ b/drivers/leds/leds-lt3593.c
@@ -89,15 +89,11 @@ static int create_lt3593_led(const struct gpio_led *template,
/* skip leds on GPIOs that aren't available */
if (!gpio_is_valid(template->gpio)) {
- printk(KERN_INFO "%s: skipping unavailable LT3593 LED at gpio %d (%s)\n",
+ dev_info(parent, "%s: skipping unavailable LT3593 LED at gpio %d (%s)\n",
KBUILD_MODNAME, template->gpio, template->name);
return 0;
}
- ret = gpio_request(template->gpio, template->name);
- if (ret < 0)
- return ret;
-
led_dat->cdev.name = template->name;
led_dat->cdev.default_trigger = template->default_trigger;
led_dat->gpio = template->gpio;
@@ -110,24 +106,21 @@ static int create_lt3593_led(const struct gpio_led *template,
if (!template->retain_state_suspended)
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
- ret = gpio_direction_output(led_dat->gpio, state);
+ ret = devm_gpio_request_one(parent, template->gpio,
+ GPIOF_DIR_OUT | state, template->name);
if (ret < 0)
- goto err;
+ return ret;
INIT_WORK(&led_dat->work, lt3593_led_work);
ret = led_classdev_register(parent, &led_dat->cdev);
if (ret < 0)
- goto err;
+ return ret;
- printk(KERN_INFO "%s: registered LT3593 LED '%s' at GPIO %d\n",
+ dev_info(parent, "%s: registered LT3593 LED '%s' at GPIO %d\n",
KBUILD_MODNAME, template->name, template->gpio);
return 0;
-
-err:
- gpio_free(led_dat->gpio);
- return ret;
}
static void delete_lt3593_led(struct lt3593_led_data *led)
@@ -137,7 +130,6 @@ static void delete_lt3593_led(struct lt3593_led_data *led)
led_classdev_unregister(&led->cdev);
cancel_work_sync(&led->work);
- gpio_free(led->gpio);
}
static int lt3593_led_probe(struct platform_device *pdev)
diff --git a/drivers/leds/leds-net48xx.c b/drivers/leds/leds-net48xx.c
index f117f7326c5..27d06c52824 100644
--- a/drivers/leds/leds-net48xx.c
+++ b/drivers/leds/leds-net48xx.c
@@ -15,7 +15,7 @@
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/err.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/nsc_gpio.h>
#include <linux/scx200_gpio.h>
#include <linux/module.h>
diff --git a/drivers/leds/leds-netxbig.c b/drivers/leds/leds-netxbig.c
index 58a800b17dc..c61c5ebcc08 100644
--- a/drivers/leds/leds-netxbig.c
+++ b/drivers/leds/leds-netxbig.c
@@ -243,7 +243,7 @@ static ssize_t netxbig_led_sata_store(struct device *dev,
int mode_val;
int ret;
- ret = strict_strtoul(buff, 10, &enable);
+ ret = kstrtoul(buff, 10, &enable);
if (ret < 0)
return ret;
diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c
index 7b75affb308..d978171c25b 100644
--- a/drivers/leds/leds-ns2.c
+++ b/drivers/leds/leds-ns2.c
@@ -150,7 +150,7 @@ static ssize_t ns2_led_sata_store(struct device *dev,
unsigned long enable;
enum ns2_led_modes mode;
- ret = strict_strtoul(buff, 10, &enable);
+ ret = kstrtoul(buff, 10, &enable);
if (ret < 0)
return ret;
@@ -192,29 +192,22 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
int ret;
enum ns2_led_modes mode;
- ret = gpio_request(template->cmd, template->name);
- if (ret == 0) {
- ret = gpio_direction_output(template->cmd,
- gpio_get_value(template->cmd));
- if (ret)
- gpio_free(template->cmd);
- }
+ ret = devm_gpio_request_one(&pdev->dev, template->cmd,
+ GPIOF_DIR_OUT | gpio_get_value(template->cmd),
+ template->name);
if (ret) {
dev_err(&pdev->dev, "%s: failed to setup command GPIO\n",
template->name);
+ return ret;
}
- ret = gpio_request(template->slow, template->name);
- if (ret == 0) {
- ret = gpio_direction_output(template->slow,
- gpio_get_value(template->slow));
- if (ret)
- gpio_free(template->slow);
- }
+ ret = devm_gpio_request_one(&pdev->dev, template->slow,
+ GPIOF_DIR_OUT | gpio_get_value(template->slow),
+ template->name);
if (ret) {
dev_err(&pdev->dev, "%s: failed to setup slow GPIO\n",
template->name);
- goto err_free_cmd;
+ return ret;
}
rwlock_init(&led_dat->rw_lock);
@@ -229,7 +222,7 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
ret = ns2_led_get_mode(led_dat, &mode);
if (ret < 0)
- goto err_free_slow;
+ return ret;
/* Set LED initial state. */
led_dat->sata = (mode == NS_V2_LED_SATA) ? 1 : 0;
@@ -238,7 +231,7 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
if (ret < 0)
- goto err_free_slow;
+ return ret;
ret = device_create_file(led_dat->cdev.dev, &dev_attr_sata);
if (ret < 0)
@@ -248,11 +241,6 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
err_free_cdev:
led_classdev_unregister(&led_dat->cdev);
-err_free_slow:
- gpio_free(led_dat->slow);
-err_free_cmd:
- gpio_free(led_dat->cmd);
-
return ret;
}
@@ -260,8 +248,6 @@ static void delete_ns2_led(struct ns2_led_data *led_dat)
{
device_remove_file(led_dat->cdev.dev, &dev_attr_sata);
led_classdev_unregister(&led_dat->cdev);
- gpio_free(led_dat->cmd);
- gpio_free(led_dat->slow);
}
#ifdef CONFIG_OF_GPIO
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index 706791af8fc..edf485b773c 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -277,7 +277,7 @@ static int pca955x_probe(struct i2c_client *client,
return -ENODEV;
}
- printk(KERN_INFO "leds-pca955x: Using %s %d-bit LED driver at "
+ dev_info(&client->dev, "leds-pca955x: Using %s %d-bit LED driver at "
"slave address 0x%02x\n",
id->name, chip->bits, client->addr);
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c
index e51ff7a3cd8..2157524f277 100644
--- a/drivers/leds/leds-pwm.c
+++ b/drivers/leds/leds-pwm.c
@@ -26,7 +26,7 @@
struct led_pwm_data {
struct led_classdev cdev;
struct pwm_device *pwm;
- unsigned int active_low;
+ unsigned int active_low;
unsigned int period;
};
diff --git a/drivers/leds/leds-rb532.c b/drivers/leds/leds-rb532.c
index 9ebdd5011a7..2e746d257b0 100644
--- a/drivers/leds/leds-rb532.c
+++ b/drivers/leds/leds-rb532.c
@@ -16,7 +16,7 @@
#include <asm/mach-rc32434/rb.h>
static void rb532_led_set(struct led_classdev *cdev,
- enum led_brightness brightness)
+ enum led_brightness brightness)
{
if (brightness)
set_latch_u5(LO_ULED, 0);
diff --git a/drivers/leds/leds-renesas-tpu.c b/drivers/leds/leds-renesas-tpu.c
index bc8984795a3..e0fff1ca592 100644
--- a/drivers/leds/leds-renesas-tpu.c
+++ b/drivers/leds/leds-renesas-tpu.c
@@ -204,10 +204,10 @@ static void r_tpu_set_pin(struct r_tpu_priv *p, enum r_tpu_pin new_state,
if (p->pin_state == R_TPU_PIN_GPIO_FN)
gpio_free(cfg->pin_gpio_fn);
- if (new_state == R_TPU_PIN_GPIO) {
- gpio_request(cfg->pin_gpio, cfg->name);
- gpio_direction_output(cfg->pin_gpio, !!brightness);
- }
+ if (new_state == R_TPU_PIN_GPIO)
+ gpio_request_one(cfg->pin_gpio, GPIOF_DIR_OUT | !!brightness,
+ cfg->name);
+
if (new_state == R_TPU_PIN_GPIO_FN)
gpio_request(cfg->pin_gpio_fn, cfg->name);
@@ -263,18 +263,18 @@ static int r_tpu_probe(struct platform_device *pdev)
}
/* map memory, let mapbase point to our channel */
- p->mapbase = ioremap_nocache(res->start, resource_size(res));
+ p->mapbase = devm_ioremap_nocache(&pdev->dev, res->start,
+ resource_size(res));
if (p->mapbase == NULL) {
dev_err(&pdev->dev, "failed to remap I/O memory\n");
return -ENXIO;
}
/* get hold of clock */
- p->clk = clk_get(&pdev->dev, NULL);
+ p->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(p->clk)) {
dev_err(&pdev->dev, "cannot get clock\n");
- ret = PTR_ERR(p->clk);
- goto err0;
+ return PTR_ERR(p->clk);
}
p->pdev = pdev;
@@ -293,7 +293,7 @@ static int r_tpu_probe(struct platform_device *pdev)
p->ldev.flags |= LED_CORE_SUSPENDRESUME;
ret = led_classdev_register(&pdev->dev, &p->ldev);
if (ret < 0)
- goto err1;
+ goto err0;
/* max_brightness may be updated by the LED core code */
p->min_rate = p->ldev.max_brightness * p->refresh_rate;
@@ -301,11 +301,8 @@ static int r_tpu_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
return 0;
- err1:
- r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF);
- clk_put(p->clk);
err0:
- iounmap(p->mapbase);
+ r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF);
return ret;
}
@@ -320,9 +317,7 @@ static int r_tpu_remove(struct platform_device *pdev)
r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF);
pm_runtime_disable(&pdev->dev);
- clk_put(p->clk);
- iounmap(p->mapbase);
return 0;
}
diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c
index 6469849e826..ec9b287ecfb 100644
--- a/drivers/leds/leds-ss4200.c
+++ b/drivers/leds/leds-ss4200.c
@@ -459,7 +459,7 @@ static ssize_t nas_led_blink_store(struct device *dev,
struct led_classdev *led = dev_get_drvdata(dev);
unsigned long blink_state;
- ret = strict_strtoul(buf, 10, &blink_state);
+ ret = kstrtoul(buf, 10, &blink_state);
if (ret)
return ret;
diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c
index 88f23f84559..ed15157c8f6 100644
--- a/drivers/leds/leds-wm8350.c
+++ b/drivers/leds/leds-wm8350.c
@@ -216,13 +216,13 @@ static int wm8350_led_probe(struct platform_device *pdev)
isink = devm_regulator_get(&pdev->dev, "led_isink");
if (IS_ERR(isink)) {
- printk(KERN_ERR "%s: can't get ISINK\n", __func__);
+ dev_err(&pdev->dev, "%s: can't get ISINK\n", __func__);
return PTR_ERR(isink);
}
dcdc = devm_regulator_get(&pdev->dev, "led_vcc");
if (IS_ERR(dcdc)) {
- printk(KERN_ERR "%s: can't get DCDC\n", __func__);
+ dev_err(&pdev->dev, "%s: can't get DCDC\n", __func__);
return PTR_ERR(dcdc);
}
diff --git a/drivers/leds/leds-wrap.c b/drivers/leds/leds-wrap.c
index 6e21e654bb0..b358cc05eff 100644
--- a/drivers/leds/leds-wrap.c
+++ b/drivers/leds/leds-wrap.c
@@ -15,7 +15,7 @@
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/err.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/scx200_gpio.h>
#include <linux/module.h>
diff --git a/drivers/leds/ledtrig-backlight.c b/drivers/leds/ledtrig-backlight.c
index b941685f222..027a2b15d7d 100644
--- a/drivers/leds/ledtrig-backlight.c
+++ b/drivers/leds/ledtrig-backlight.c
@@ -40,7 +40,7 @@ static int fb_notifier_callback(struct notifier_block *p,
int new_status = *blank ? BLANK : UNBLANK;
switch (event) {
- case FB_EVENT_BLANK :
+ case FB_EVENT_BLANK:
if (new_status == n->old_status)
break;
@@ -76,7 +76,7 @@ static ssize_t bl_trig_invert_store(struct device *dev,
unsigned long invert;
int ret;
- ret = strict_strtoul(buf, 10, &invert);
+ ret = kstrtoul(buf, 10, &invert);
if (ret < 0)
return ret;
diff --git a/drivers/leds/ledtrig-gpio.c b/drivers/leds/ledtrig-gpio.c
index ba215dc42f9..72e3ebfc281 100644
--- a/drivers/leds/ledtrig-gpio.c
+++ b/drivers/leds/ledtrig-gpio.c
@@ -110,7 +110,7 @@ static ssize_t gpio_trig_inverted_store(struct device *dev,
unsigned long inverted;
int ret;
- ret = strict_strtoul(buf, 10, &inverted);
+ ret = kstrtoul(buf, 10, &inverted);
if (ret < 0)
return ret;
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 61200717687..bd8bf0953fe 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -4124,7 +4124,7 @@ static struct md_sysfs_entry md_size =
__ATTR(component_size, S_IRUGO|S_IWUSR, size_show, size_store);
-/* Metdata version.
+/* Metadata version.
* This is one of
* 'none' for arrays with no metadata (good luck...)
* 'external' for arrays with externally managed metadata,
diff --git a/drivers/md/persistent-data/dm-block-manager.c b/drivers/md/persistent-data/dm-block-manager.c
index 5ba277768d9..a3ae09124a6 100644
--- a/drivers/md/persistent-data/dm-block-manager.c
+++ b/drivers/md/persistent-data/dm-block-manager.c
@@ -25,7 +25,7 @@
* may be held at once. This is just an implementation detail.
*
* ii) Recursive locking attempts are detected and return EINVAL. A stack
- * trace is also emitted for the previous lock aquisition.
+ * trace is also emitted for the previous lock acquisition.
*
* iii) Priority is given to write locks.
*/
@@ -109,7 +109,7 @@ static int __check_holder(struct block_lock *lock)
DMERR("previously held here:");
print_stack_trace(lock->traces + i, 4);
- DMERR("subsequent aquisition attempted here:");
+ DMERR("subsequent acquisition attempted here:");
t.nr_entries = 0;
t.max_entries = MAX_STACK;
t.entries = entries;
diff --git a/drivers/md/persistent-data/dm-btree.h b/drivers/md/persistent-data/dm-btree.h
index ae02c84410f..a2cd50441ca 100644
--- a/drivers/md/persistent-data/dm-btree.h
+++ b/drivers/md/persistent-data/dm-btree.h
@@ -35,7 +35,7 @@ struct dm_transaction_manager;
*/
/*
- * Infomation about the values stored within the btree.
+ * Information about the values stored within the btree.
*/
struct dm_btree_value_type {
void *context;
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index a4502686e7a..3380372c039 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -1576,7 +1576,7 @@ static int resize_stripes(struct r5conf *conf, int newsize)
* This happens in stages:
* 1/ create a new kmem_cache and allocate the required number of
* stripe_heads.
- * 2/ gather all the old stripe_heads and tranfer the pages across
+ * 2/ gather all the old stripe_heads and transfer the pages across
* to the new stripe_heads. This will have the side effect of
* freezing the array as once all stripe_heads have been collected,
* no IO will be possible. Old stripe heads are freed once their
diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig
index 121b0110af3..d2a436ce77f 100644
--- a/drivers/media/common/Kconfig
+++ b/drivers/media/common/Kconfig
@@ -1,3 +1,10 @@
+# Used by common drivers, when they need to ask questions
+config MEDIA_COMMON_OPTIONS
+ bool
+
+comment "common driver options"
+ depends on MEDIA_COMMON_OPTIONS
+
source "drivers/media/common/b2c2/Kconfig"
source "drivers/media/common/saa7146/Kconfig"
source "drivers/media/common/siano/Kconfig"
diff --git a/drivers/media/common/b2c2/Kconfig b/drivers/media/common/b2c2/Kconfig
index 1df9e578daa..a8c6cdfaa2f 100644
--- a/drivers/media/common/b2c2/Kconfig
+++ b/drivers/media/common/b2c2/Kconfig
@@ -17,11 +17,6 @@ config DVB_B2C2_FLEXCOP
select DVB_CX24123 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT
select DVB_TUNER_CX24113 if MEDIA_SUBDRV_AUTOSELECT
- help
- Support for the digital TV receiver chip made by B2C2 Inc. included in
- Technisats PCI cards and USB boxes.
-
- Say Y if you own such a device and want to use it.
# Selected via the PCI or USB flexcop drivers
config DVB_B2C2_FLEXCOP_DEBUG
diff --git a/drivers/media/common/siano/Kconfig b/drivers/media/common/siano/Kconfig
index 425aeadfb49..68f0f604678 100644
--- a/drivers/media/common/siano/Kconfig
+++ b/drivers/media/common/siano/Kconfig
@@ -4,14 +4,16 @@
config SMS_SIANO_MDTV
tristate
- depends on DVB_CORE && RC_CORE && HAS_DMA
+ depends on DVB_CORE && HAS_DMA
+ depends on !RC_CORE || RC_CORE
depends on SMS_USB_DRV || SMS_SDIO_DRV
default y
- ---help---
- Choose Y or M here if you have MDTV receiver with a Siano chipset.
-
- To compile this driver as a module, choose M here
- (The module will be called smsmdtv).
- Further documentation on this driver can be found on the WWW
- at http://www.siano-ms.com/
+config SMS_SIANO_RC
+ bool "Enable Remote Controller support for Siano devices"
+ depends on SMS_SIANO_MDTV && RC_CORE
+ depends on SMS_USB_DRV || SMS_SDIO_DRV
+ depends on MEDIA_COMMON_OPTIONS
+ default y
+ ---help---
+ Choose Y to select Remote Controller support for Siano driver.
diff --git a/drivers/media/common/siano/Makefile b/drivers/media/common/siano/Makefile
index 2a09279e064..81b1e985bea 100644
--- a/drivers/media/common/siano/Makefile
+++ b/drivers/media/common/siano/Makefile
@@ -1,7 +1,11 @@
-smsmdtv-objs := smscoreapi.o sms-cards.o smsendian.o smsir.o
+smsmdtv-objs := smscoreapi.o sms-cards.o smsendian.o
obj-$(CONFIG_SMS_SIANO_MDTV) += smsmdtv.o smsdvb.o
+ifeq ($(CONFIG_SMS_SIANO_RC),y)
+ smsmdtv-objs += smsir.o
+endif
+
ccflags-y += -Idrivers/media/dvb-core
ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/common/siano/smscoreapi.c b/drivers/media/common/siano/smscoreapi.c
index 9cc55546cc3..1842e64e633 100644
--- a/drivers/media/common/siano/smscoreapi.c
+++ b/drivers/media/common/siano/smscoreapi.c
@@ -1092,7 +1092,7 @@ EXPORT_SYMBOL_GPL(smscore_onresponse);
* @return pointer to descriptor on success, NULL on error.
*/
-struct smscore_buffer_t *get_entry(struct smscore_device_t *coredev)
+static struct smscore_buffer_t *get_entry(struct smscore_device_t *coredev)
{
struct smscore_buffer_t *cb = NULL;
unsigned long flags;
diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c
index 37bc5c4b8ad..b8c5cad7853 100644
--- a/drivers/media/common/siano/smsir.c
+++ b/drivers/media/common/siano/smsir.c
@@ -88,7 +88,7 @@ int sms_ir_init(struct smscore_device_t *coredev)
dev->priv = coredev;
dev->driver_type = RC_DRIVER_IR_RAW;
- dev->allowed_protos = RC_TYPE_ALL;
+ dev->allowed_protos = RC_BIT_ALL;
dev->map_name = sms_get_board(board_id)->rc_codes;
dev->driver_name = MODULE_NAME;
diff --git a/drivers/media/common/siano/smsir.h b/drivers/media/common/siano/smsir.h
index ae92b3a8587..69b59b9eee2 100644
--- a/drivers/media/common/siano/smsir.h
+++ b/drivers/media/common/siano/smsir.h
@@ -46,10 +46,19 @@ struct ir_t {
u32 controller;
};
+#ifdef CONFIG_SMS_SIANO_RC
int sms_ir_init(struct smscore_device_t *coredev);
void sms_ir_exit(struct smscore_device_t *coredev);
void sms_ir_event(struct smscore_device_t *coredev,
const char *buf, int len);
+#else
+inline static int sms_ir_init(struct smscore_device_t *coredev) {
+ return 0;
+}
+inline static void sms_ir_exit(struct smscore_device_t *coredev) {};
+inline static void sms_ir_event(struct smscore_device_t *coredev,
+ const char *buf, int len) {};
+#endif
#endif /* __SMS_IR_H__ */
diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c
index 889c9c16c6d..d81dbb22aa8 100644
--- a/drivers/media/dvb-core/dmxdev.c
+++ b/drivers/media/dvb-core/dmxdev.c
@@ -877,7 +877,7 @@ static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev,
dvb_dmxdev_filter_stop(dmxdevfilter);
dvb_dmxdev_filter_reset(dmxdevfilter);
- if (params->pes_type > DMX_PES_OTHER || params->pes_type < 0)
+ if ((unsigned)params->pes_type > DMX_PES_OTHER)
return -EINVAL;
dmxdevfilter->type = DMXDEV_TYPE_PES;
diff --git a/drivers/media/dvb-core/dmxdev.h b/drivers/media/dvb-core/dmxdev.h
index 02ebe28f830..48c6cf92ab9 100644
--- a/drivers/media/dvb-core/dmxdev.h
+++ b/drivers/media/dvb-core/dmxdev.h
@@ -26,6 +26,7 @@
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/kernel.h>
+#include <linux/time.h>
#include <linux/timer.h>
#include <linux/wait.h>
#include <linux/fs.h>
diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h
index 58e0220447c..388c2eb4d74 100644
--- a/drivers/media/dvb-core/dvb-usb-ids.h
+++ b/drivers/media/dvb-core/dvb-usb-ids.h
@@ -250,6 +250,7 @@
#define USB_PID_TERRATEC_T3 0x10a0
#define USB_PID_TERRATEC_T5 0x10a1
#define USB_PID_NOXON_DAB_STICK 0x00b3
+#define USB_PID_NOXON_DAB_STICK_REV2 0x00e0
#define USB_PID_PINNACLE_EXPRESSCARD_320CX 0x022e
#define USB_PID_PINNACLE_PCTV2000E 0x022c
#define USB_PID_PINNACLE_PCTV_DVB_T_FLASH 0x0228
diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c
index 7e92793260f..49d95040096 100644
--- a/drivers/media/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb-core/dvb_frontend.c
@@ -1029,12 +1029,6 @@ static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = {
/* Get */
_DTV_CMD(DTV_DISEQC_SLAVE_REPLY, 0, 1),
_DTV_CMD(DTV_API_VERSION, 0, 0),
- _DTV_CMD(DTV_CODE_RATE_HP, 0, 0),
- _DTV_CMD(DTV_CODE_RATE_LP, 0, 0),
- _DTV_CMD(DTV_GUARD_INTERVAL, 0, 0),
- _DTV_CMD(DTV_TRANSMISSION_MODE, 0, 0),
- _DTV_CMD(DTV_HIERARCHY, 0, 0),
- _DTV_CMD(DTV_INTERLEAVING, 0, 0),
_DTV_CMD(DTV_ENUM_DELSYS, 0, 0),
@@ -1042,13 +1036,11 @@ static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = {
_DTV_CMD(DTV_ATSCMH_RS_FRAME_ENSEMBLE, 1, 0),
_DTV_CMD(DTV_ATSCMH_FIC_VER, 0, 0),
- _DTV_CMD(DTV_ATSCMH_PARADE_ID, 0, 0),
_DTV_CMD(DTV_ATSCMH_NOG, 0, 0),
_DTV_CMD(DTV_ATSCMH_TNOG, 0, 0),
_DTV_CMD(DTV_ATSCMH_SGN, 0, 0),
_DTV_CMD(DTV_ATSCMH_PRC, 0, 0),
_DTV_CMD(DTV_ATSCMH_RS_FRAME_MODE, 0, 0),
- _DTV_CMD(DTV_ATSCMH_RS_FRAME_ENSEMBLE, 0, 0),
_DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_PRI, 0, 0),
_DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_SEC, 0, 0),
_DTV_CMD(DTV_ATSCMH_SCCC_BLOCK_MODE, 0, 0),
@@ -1056,8 +1048,6 @@ static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = {
_DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_B, 0, 0),
_DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_C, 0, 0),
_DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_D, 0, 0),
-
- _DTV_CMD(DTV_LNA, 0, 0),
};
static void dtv_property_dump(struct dvb_frontend *fe, struct dtv_property *tvp)
diff --git a/drivers/media/dvb-frontends/cx22700.c b/drivers/media/dvb-frontends/cx22700.c
index f2a90f990ce..3d399d9a634 100644
--- a/drivers/media/dvb-frontends/cx22700.c
+++ b/drivers/media/dvb-frontends/cx22700.c
@@ -139,7 +139,7 @@ static int cx22700_set_tps(struct cx22700_state *state,
if (p->code_rate_HP == FEC_4_5 || p->code_rate_LP == FEC_4_5)
return -EINVAL;
- if (p->guard_interval < GUARD_INTERVAL_1_32 ||
+ if ((int)p->guard_interval < GUARD_INTERVAL_1_32 ||
p->guard_interval > GUARD_INTERVAL_1_4)
return -EINVAL;
@@ -152,7 +152,7 @@ static int cx22700_set_tps(struct cx22700_state *state,
p->modulation != QAM_64)
return -EINVAL;
- if (p->hierarchy < HIERARCHY_NONE ||
+ if ((int)p->hierarchy < HIERARCHY_NONE ||
p->hierarchy > HIERARCHY_4)
return -EINVAL;
diff --git a/drivers/media/dvb-frontends/cx24123.c b/drivers/media/dvb-frontends/cx24123.c
index 7e28b4ee7d4..68c88ab58e7 100644
--- a/drivers/media/dvb-frontends/cx24123.c
+++ b/drivers/media/dvb-frontends/cx24123.c
@@ -338,7 +338,7 @@ static int cx24123_set_fec(struct cx24123_state *state, fe_code_rate_t fec)
{
u8 nom_reg = cx24123_readreg(state, 0x0e) & ~0x07;
- if ((fec < FEC_NONE) || (fec > FEC_AUTO))
+ if (((int)fec < FEC_NONE) || (fec > FEC_AUTO))
fec = FEC_AUTO;
/* Set the soft decision threshold */
diff --git a/drivers/media/dvb-frontends/dib9000.h b/drivers/media/dvb-frontends/dib9000.h
index b5781a48034..de1cc91fd83 100644
--- a/drivers/media/dvb-frontends/dib9000.h
+++ b/drivers/media/dvb-frontends/dib9000.h
@@ -97,7 +97,7 @@ static inline int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb
return -ENODEV;
}
-int dib9000_remove_slave_frontend(struct dvb_frontend *fe)
+static inline int dib9000_remove_slave_frontend(struct dvb_frontend *fe)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c
index 6d9853750d2..e71cc60851e 100644
--- a/drivers/media/dvb-frontends/drxd_hard.c
+++ b/drivers/media/dvb-frontends/drxd_hard.c
@@ -1748,7 +1748,8 @@ static int DRX_Stop(struct drxd_state *state)
return status;
}
-int SetOperationMode(struct drxd_state *state, int oMode)
+#if 0 /* Currently unused */
+static int SetOperationMode(struct drxd_state *state, int oMode)
{
int status;
@@ -1788,6 +1789,7 @@ int SetOperationMode(struct drxd_state *state, int oMode)
state->operation_mode = oMode;
return status;
}
+#endif
static int StartDiversity(struct drxd_state *state)
{
@@ -2612,7 +2614,7 @@ static int CDRXD(struct drxd_state *state, u32 IntermediateFrequency)
return 0;
}
-int DRXD_init(struct drxd_state *state, const u8 * fw, u32 fw_size)
+static int DRXD_init(struct drxd_state *state, const u8 *fw, u32 fw_size)
{
int status = 0;
u32 driverVersion;
@@ -2774,7 +2776,7 @@ int DRXD_init(struct drxd_state *state, const u8 * fw, u32 fw_size)
return status;
}
-int DRXD_status(struct drxd_state *state, u32 * pLockStatus)
+static int DRXD_status(struct drxd_state *state, u32 *pLockStatus)
{
DRX_GetLockStatus(state, pLockStatus);
diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c
index 8b4c6d5f8f3..c2fc7da0d6b 100644
--- a/drivers/media/dvb-frontends/drxk_hard.c
+++ b/drivers/media/dvb-frontends/drxk_hard.c
@@ -65,16 +65,6 @@ static bool IsQAM(struct drxk_state *state)
state->m_OperationMode == OM_QAM_ITU_C;
}
-bool IsA1WithPatchCode(struct drxk_state *state)
-{
- return state->m_DRXK_A1_PATCH_CODE;
-}
-
-bool IsA1WithRomCode(struct drxk_state *state)
-{
- return state->m_DRXK_A1_ROM_CODE;
-}
-
#define NOA1ROM 0
#define DRXDAP_FASI_SHORT_FORMAT(addr) (((addr) & 0xFC30FF80) == 0)
@@ -189,7 +179,7 @@ static inline u32 MulDiv32(u32 a, u32 b, u32 c)
return (u32) tmp64;
}
-inline u32 Frac28a(u32 a, u32 c)
+static inline u32 Frac28a(u32 a, u32 c)
{
int i = 0;
u32 Q1 = 0;
@@ -587,7 +577,7 @@ static int write_block(struct drxk_state *state, u32 Address,
#define DRXK_MAX_RETRIES_POWERUP 20
#endif
-int PowerUpDevice(struct drxk_state *state)
+static int PowerUpDevice(struct drxk_state *state)
{
int status;
u8 data = 0;
@@ -720,11 +710,6 @@ static int init_state(struct drxk_state *state)
state->m_bPowerDown = (ulPowerDown != 0);
- state->m_DRXK_A1_PATCH_CODE = false;
- state->m_DRXK_A1_ROM_CODE = false;
- state->m_DRXK_A2_ROM_CODE = false;
- state->m_DRXK_A3_ROM_CODE = false;
- state->m_DRXK_A2_PATCH_CODE = false;
state->m_DRXK_A3_PATCH_CODE = false;
/* Init AGC and PGA parameters */
@@ -921,7 +906,7 @@ static int GetDeviceCapabilities(struct drxk_state *state)
status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
if (status < 0)
goto error;
- status = write16(state, SIO_TOP_COMM_KEY__A, 0xFABA);
+ status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY);
if (status < 0)
goto error;
status = read16(state, SIO_PDR_OHW_CFG__A, &sioPdrOhwCfg);
@@ -948,7 +933,7 @@ static int GetDeviceCapabilities(struct drxk_state *state)
state->m_oscClockFreq = 20250;
break;
default:
- printk(KERN_ERR "drxk: Clock Frequency is unkonwn\n");
+ printk(KERN_ERR "drxk: Clock Frequency is unknown\n");
return -EINVAL;
}
/*
@@ -1217,7 +1202,7 @@ static int MPEGTSConfigurePins(struct drxk_state *state, bool mpegEnable)
goto error;
/* MPEG TS pad configuration */
- status = write16(state, SIO_TOP_COMM_KEY__A, 0xFABA);
+ status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY);
if (status < 0)
goto error;
@@ -5461,6 +5446,7 @@ static int QAMDemodulatorCommand(struct drxk_state *state,
} else {
printk(KERN_WARNING "drxk: Unknown QAM demodulator parameter "
"count %d\n", numberOfParameters);
+ status = -EINVAL;
}
error:
diff --git a/drivers/media/dvb-frontends/drxk_hard.h b/drivers/media/dvb-frontends/drxk_hard.h
index 6bb9fc4a7b9..d18a896a983 100644
--- a/drivers/media/dvb-frontends/drxk_hard.h
+++ b/drivers/media/dvb-frontends/drxk_hard.h
@@ -320,11 +320,7 @@ struct drxk_state {
u8 *m_microcode;
int m_microcode_length;
- bool m_DRXK_A1_PATCH_CODE;
- bool m_DRXK_A1_ROM_CODE;
- bool m_DRXK_A2_ROM_CODE;
- bool m_DRXK_A3_ROM_CODE;
- bool m_DRXK_A2_PATCH_CODE;
+ bool m_DRXK_A3_ROM_CODE;
bool m_DRXK_A3_PATCH_CODE;
bool m_rfmirror;
diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c
index 5b639087ce4..60a529e3833 100644
--- a/drivers/media/dvb-frontends/ds3000.c
+++ b/drivers/media/dvb-frontends/ds3000.c
@@ -30,7 +30,6 @@
#include "ds3000.h"
static int debug;
-static int force_fw_upload;
#define dprintk(args...) \
do { \
@@ -234,7 +233,6 @@ struct ds3000_state {
struct i2c_adapter *i2c;
const struct ds3000_config *config;
struct dvb_frontend frontend;
- u8 skip_fw_load;
/* previous uncorrected block counter for DVB-S2 */
u16 prevUCBS2;
};
@@ -397,9 +395,6 @@ static int ds3000_firmware_ondemand(struct dvb_frontend *fe)
if (ret < 0)
return ret;
- if (state->skip_fw_load || !force_fw_upload)
- return 0; /* Firmware already uploaded, skipping */
-
/* Load firmware */
/* request the firmware, this will block until someone uploads it */
printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", __func__,
@@ -413,9 +408,6 @@ static int ds3000_firmware_ondemand(struct dvb_frontend *fe)
return ret;
}
- /* Make sure we don't recurse back through here during loading */
- state->skip_fw_load = 1;
-
ret = ds3000_load_firmware(fe, fw);
if (ret)
printk("%s: Writing firmware to device failed\n", __func__);
@@ -425,9 +417,6 @@ static int ds3000_firmware_ondemand(struct dvb_frontend *fe)
dprintk("%s: Firmware upload %s\n", __func__,
ret == 0 ? "complete" : "failed");
- /* Ensure firmware is always loaded if required */
- state->skip_fw_load = 0;
-
return ret;
}
@@ -1309,10 +1298,8 @@ static struct dvb_frontend_ops ds3000_ops = {
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
-module_param(force_fw_upload, int, 0644);
-MODULE_PARM_DESC(force_fw_upload, "Force firmware upload (default:0)");
-
MODULE_DESCRIPTION("DVB Frontend module for Montage Technology "
"DS3000/TS2020 hardware");
MODULE_AUTHOR("Konstantin Dimitrov");
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(DS3000_DEFAULT_FIRMWARE);
diff --git a/drivers/media/dvb-frontends/l64781.c b/drivers/media/dvb-frontends/l64781.c
index 36fcf559e36..ddf866c46f8 100644
--- a/drivers/media/dvb-frontends/l64781.c
+++ b/drivers/media/dvb-frontends/l64781.c
@@ -180,11 +180,11 @@ static int apply_frontend_param(struct dvb_frontend *fe)
p->transmission_mode != TRANSMISSION_MODE_8K)
return -EINVAL;
- if (p->guard_interval < GUARD_INTERVAL_1_32 ||
+ if ((int)p->guard_interval < GUARD_INTERVAL_1_32 ||
p->guard_interval > GUARD_INTERVAL_1_4)
return -EINVAL;
- if (p->hierarchy < HIERARCHY_NONE ||
+ if ((int)p->hierarchy < HIERARCHY_NONE ||
p->hierarchy > HIERARCHY_4)
return -EINVAL;
diff --git a/drivers/media/dvb-frontends/mt312.c b/drivers/media/dvb-frontends/mt312.c
index e20bf13aa86..ec388c1d691 100644
--- a/drivers/media/dvb-frontends/mt312.c
+++ b/drivers/media/dvb-frontends/mt312.c
@@ -549,7 +549,7 @@ static int mt312_set_frontend(struct dvb_frontend *fe)
|| (p->frequency > fe->ops.info.frequency_max))
return -EINVAL;
- if ((p->inversion < INVERSION_OFF)
+ if (((int)p->inversion < INVERSION_OFF)
|| (p->inversion > INVERSION_ON))
return -EINVAL;
@@ -557,7 +557,7 @@ static int mt312_set_frontend(struct dvb_frontend *fe)
|| (p->symbol_rate > fe->ops.info.symbol_rate_max))
return -EINVAL;
- if ((p->fec_inner < FEC_NONE)
+ if (((int)p->fec_inner < FEC_NONE)
|| (p->fec_inner > FEC_AUTO))
return -EINVAL;
diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c
index b0f6ec03d1e..362d26d11e8 100644
--- a/drivers/media/dvb-frontends/rtl2830.c
+++ b/drivers/media/dvb-frontends/rtl2830.c
@@ -130,7 +130,7 @@ static int rtl2830_rd_reg(struct rtl2830_priv *priv, u16 reg, u8 *val)
}
/* write single register with mask */
-int rtl2830_wr_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 val, u8 mask)
+static int rtl2830_wr_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 val, u8 mask)
{
int ret;
u8 tmp;
@@ -150,7 +150,7 @@ int rtl2830_wr_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 val, u8 mask)
}
/* read single register with mask */
-int rtl2830_rd_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 *val, u8 mask)
+static int rtl2830_rd_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 *val, u8 mask)
{
int ret, i;
u8 tmp;
@@ -256,7 +256,7 @@ static int rtl2830_sleep(struct dvb_frontend *fe)
return 0;
}
-int rtl2830_get_tune_settings(struct dvb_frontend *fe,
+static int rtl2830_get_tune_settings(struct dvb_frontend *fe,
struct dvb_frontend_tune_settings *s)
{
s->min_delay_ms = 500;
diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c
index 80c8e5f1182..73887690b04 100644
--- a/drivers/media/dvb-frontends/rtl2832.c
+++ b/drivers/media/dvb-frontends/rtl2832.c
@@ -265,7 +265,7 @@ static int rtl2832_rd_reg(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val)
return rtl2832_rd_regs(priv, reg, page, val, 1);
}
-int rtl2832_rd_demod_reg(struct rtl2832_priv *priv, int reg, u32 *val)
+static int rtl2832_rd_demod_reg(struct rtl2832_priv *priv, int reg, u32 *val)
{
int ret;
@@ -305,7 +305,7 @@ err:
}
-int rtl2832_wr_demod_reg(struct rtl2832_priv *priv, int reg, u32 val)
+static int rtl2832_wr_demod_reg(struct rtl2832_priv *priv, int reg, u32 val)
{
int ret, i;
u8 len;
@@ -510,7 +510,7 @@ static int rtl2832_sleep(struct dvb_frontend *fe)
return 0;
}
-int rtl2832_get_tune_settings(struct dvb_frontend *fe,
+static int rtl2832_get_tune_settings(struct dvb_frontend *fe,
struct dvb_frontend_tune_settings *s)
{
struct rtl2832_priv *priv = fe->demodulator_priv;
diff --git a/drivers/media/dvb-frontends/stb0899_drv.c b/drivers/media/dvb-frontends/stb0899_drv.c
index 79e29de87fb..cc278b3d6d5 100644
--- a/drivers/media/dvb-frontends/stb0899_drv.c
+++ b/drivers/media/dvb-frontends/stb0899_drv.c
@@ -1260,7 +1260,7 @@ static inline void CONVERT32(u32 x, char *str)
*str = '\0';
}
-int stb0899_get_dev_id(struct stb0899_state *state)
+static int stb0899_get_dev_id(struct stb0899_state *state)
{
u8 chip_id, release;
u16 id;
diff --git a/drivers/media/dvb-frontends/stv0367.c b/drivers/media/dvb-frontends/stv0367.c
index 2a8aaeb1112..0c8e45949b1 100644
--- a/drivers/media/dvb-frontends/stv0367.c
+++ b/drivers/media/dvb-frontends/stv0367.c
@@ -879,7 +879,8 @@ static u8 stv0367_readbits(struct stv0367_state *state, u32 label)
return val;
}
-u8 stv0367_getbits(u8 reg, u32 label)
+#if 0 /* Currently, unused */
+static u8 stv0367_getbits(u8 reg, u32 label)
{
u8 mask, pos;
@@ -887,7 +888,7 @@ u8 stv0367_getbits(u8 reg, u32 label)
return (reg & mask) >> pos;
}
-
+#endif
static int stv0367ter_gate_ctrl(struct dvb_frontend *fe, int enable)
{
struct stv0367_state *state = fe->demodulator_priv;
@@ -1263,8 +1264,8 @@ stv0367_ter_signal_type stv0367ter_check_cpamp(struct stv0367_state *state,
return CPAMPStatus;
}
-enum
-stv0367_ter_signal_type stv0367ter_lock_algo(struct stv0367_state *state)
+static enum stv0367_ter_signal_type
+stv0367ter_lock_algo(struct stv0367_state *state)
{
enum stv0367_ter_signal_type ret_flag;
short int wd, tempo;
@@ -1528,7 +1529,7 @@ static int stv0367ter_sleep(struct dvb_frontend *fe)
return stv0367ter_standby(fe, 1);
}
-int stv0367ter_init(struct dvb_frontend *fe)
+static int stv0367ter_init(struct dvb_frontend *fe)
{
struct stv0367_state *state = fe->demodulator_priv;
struct stv0367ter_state *ter_state = state->ter_state;
@@ -2378,9 +2379,9 @@ static u32 stv0367cab_get_adc_freq(struct dvb_frontend *fe, u32 ExtClk_Hz)
return ADCClk_Hz;
}
-enum stv0367cab_mod stv0367cab_SetQamSize(struct stv0367_state *state,
- u32 SymbolRate,
- enum stv0367cab_mod QAMSize)
+static enum stv0367cab_mod stv0367cab_SetQamSize(struct stv0367_state *state,
+ u32 SymbolRate,
+ enum stv0367cab_mod QAMSize)
{
/* Set QAM size */
stv0367_writebits(state, F367CAB_QAM_MODE, QAMSize);
@@ -2762,7 +2763,7 @@ static int stv0367cab_sleep(struct dvb_frontend *fe)
return stv0367cab_standby(fe, 1);
}
-int stv0367cab_init(struct dvb_frontend *fe)
+static int stv0367cab_init(struct dvb_frontend *fe)
{
struct stv0367_state *state = fe->demodulator_priv;
struct stv0367cab_state *cab_state = state->cab_state;
diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c
index a83bf680234..16a4bc54dbe 100644
--- a/drivers/media/dvb-frontends/tda10071.c
+++ b/drivers/media/dvb-frontends/tda10071.c
@@ -96,7 +96,8 @@ static int tda10071_rd_reg(struct tda10071_priv *priv, u8 reg, u8 *val)
}
/* write single register with mask */
-int tda10071_wr_reg_mask(struct tda10071_priv *priv, u8 reg, u8 val, u8 mask)
+static int tda10071_wr_reg_mask(struct tda10071_priv *priv,
+ u8 reg, u8 val, u8 mask)
{
int ret;
u8 tmp;
@@ -116,7 +117,8 @@ int tda10071_wr_reg_mask(struct tda10071_priv *priv, u8 reg, u8 val, u8 mask)
}
/* read single register with mask */
-int tda10071_rd_reg_mask(struct tda10071_priv *priv, u8 reg, u8 *val, u8 mask)
+static int tda10071_rd_reg_mask(struct tda10071_priv *priv,
+ u8 reg, u8 *val, u8 mask)
{
int ret, i;
u8 tmp;
diff --git a/drivers/media/dvb-frontends/tda18271c2dd.c b/drivers/media/dvb-frontends/tda18271c2dd.c
index ad7c72e8f51..d281f77d5c2 100644
--- a/drivers/media/dvb-frontends/tda18271c2dd.c
+++ b/drivers/media/dvb-frontends/tda18271c2dd.c
@@ -32,6 +32,7 @@
#include <asm/div64.h>
#include "dvb_frontend.h"
+#include "tda18271c2dd.h"
struct SStandardParam {
s32 m_IFFrequency;
diff --git a/drivers/media/firewire/firedtv.h b/drivers/media/firewire/firedtv.h
index 4fdcd8cb753..c2ba085e0d2 100644
--- a/drivers/media/firewire/firedtv.h
+++ b/drivers/media/firewire/firedtv.h
@@ -13,6 +13,7 @@
#ifndef _FIREDTV_H
#define _FIREDTV_H
+#include <linux/time.h>
#include <linux/dvb/dmx.h>
#include <linux/dvb/frontend.h>
#include <linux/list.h>
diff --git a/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c
index 18a38b38fcb..df163800c8e 100644
--- a/drivers/media/i2c/adp1653.c
+++ b/drivers/media/i2c/adp1653.c
@@ -3,10 +3,10 @@
*
* Copyright (C) 2008--2011 Nokia Corporation
*
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
*
* Contributors:
- * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
* Tuukka Toivonen <tuukkat76@gmail.com>
*
* This program is free software; you can redistribute it and/or
diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c
index e1d4c89d714..10c3c1db4cd 100644
--- a/drivers/media/i2c/adv7183.c
+++ b/drivers/media/i2c/adv7183.c
@@ -681,18 +681,7 @@ static struct i2c_driver adv7183_driver = {
.id_table = adv7183_id,
};
-static __init int adv7183_init(void)
-{
- return i2c_add_driver(&adv7183_driver);
-}
-
-static __exit void adv7183_exit(void)
-{
- i2c_del_driver(&adv7183_driver);
-}
-
-module_init(adv7183_init);
-module_exit(adv7183_exit);
+module_i2c_driver(adv7183_driver);
MODULE_DESCRIPTION("Analog Devices ADV7183 video decoder driver");
MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 05f8950f6f9..f47555b1000 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -486,9 +486,19 @@ static inline int edid_read_block(struct v4l2_subdev *sd, unsigned len, u8 *val)
struct i2c_client *client = state->i2c_edid;
u8 msgbuf0[1] = { 0 };
u8 msgbuf1[256];
- struct i2c_msg msg[2] = { { client->addr, 0, 1, msgbuf0 },
- { client->addr, 0 | I2C_M_RD, len, msgbuf1 }
- };
+ struct i2c_msg msg[2] = {
+ {
+ .addr = client->addr,
+ .len = 1,
+ .buf = msgbuf0
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = msgbuf1
+ },
+ };
if (i2c_transfer(client->adapter, msg, 2) < 0)
return -EIO;
diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c
index 2cee69e3418..f4149eb4d7b 100644
--- a/drivers/media/i2c/cx25840/cx25840-core.c
+++ b/drivers/media/i2c/cx25840/cx25840-core.c
@@ -2065,7 +2065,7 @@ static int cx25840_irq_handler(struct v4l2_subdev *sd, u32 status,
#define DIF_BPF_COEFF3435 (0x38c)
#define DIF_BPF_COEFF36 (0x390)
-void cx23885_dif_setup(struct i2c_client *client, u32 ifHz)
+static void cx23885_dif_setup(struct i2c_client *client, u32 ifHz)
{
u64 pll_freq;
u32 pll_freq_word;
diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c
index 04f192a0398..08ae067b2b6 100644
--- a/drivers/media/i2c/ir-kbd-i2c.c
+++ b/drivers/media/i2c/ir-kbd-i2c.c
@@ -284,7 +284,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
char *ir_codes = NULL;
const char *name = NULL;
- u64 rc_type = RC_TYPE_UNKNOWN;
+ u64 rc_type = RC_BIT_UNKNOWN;
struct IR_i2c *ir;
struct rc_dev *rc = NULL;
struct i2c_adapter *adap = client->adapter;
@@ -303,7 +303,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
case 0x64:
name = "Pixelview";
ir->get_key = get_key_pixelview;
- rc_type = RC_TYPE_OTHER;
+ rc_type = RC_BIT_OTHER;
ir_codes = RC_MAP_EMPTY;
break;
case 0x18:
@@ -311,31 +311,31 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
case 0x1a:
name = "Hauppauge";
ir->get_key = get_key_haup;
- rc_type = RC_TYPE_RC5;
+ rc_type = RC_BIT_RC5;
ir_codes = RC_MAP_HAUPPAUGE;
break;
case 0x30:
name = "KNC One";
ir->get_key = get_key_knc1;
- rc_type = RC_TYPE_OTHER;
+ rc_type = RC_BIT_OTHER;
ir_codes = RC_MAP_EMPTY;
break;
case 0x6b:
name = "FusionHDTV";
ir->get_key = get_key_fusionhdtv;
- rc_type = RC_TYPE_RC5;
+ rc_type = RC_BIT_RC5;
ir_codes = RC_MAP_FUSIONHDTV_MCE;
break;
case 0x40:
name = "AVerMedia Cardbus remote";
ir->get_key = get_key_avermedia_cardbus;
- rc_type = RC_TYPE_OTHER;
+ rc_type = RC_BIT_OTHER;
ir_codes = RC_MAP_AVERMEDIA_CARDBUS;
break;
case 0x71:
name = "Hauppauge/Zilog Z8";
ir->get_key = get_key_haup_xvr;
- rc_type = RC_TYPE_RC5;
+ rc_type = RC_BIT_RC5;
ir_codes = RC_MAP_HAUPPAUGE;
break;
}
diff --git a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c
index 49c1b3abb42..2750de63427 100644
--- a/drivers/media/i2c/s5k4ecgx.c
+++ b/drivers/media/i2c/s5k4ecgx.c
@@ -343,7 +343,7 @@ static int s5k4ecgx_load_firmware(struct v4l2_subdev *sd)
}
regs_num = le32_to_cpu(get_unaligned_le32(fw->data));
- v4l2_dbg(3, debug, sd, "FW: %s size %d register sets %d\n",
+ v4l2_dbg(3, debug, sd, "FW: %s size %zu register sets %d\n",
S5K4ECGX_FIRMWARE, fw->size, regs_num);
regs_num++; /* Add header */
diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/smiapp-pll.c
index a577614bd84..d8d5da7c52d 100644
--- a/drivers/media/i2c/smiapp-pll.c
+++ b/drivers/media/i2c/smiapp-pll.c
@@ -4,7 +4,7 @@
* Generic driver for SMIA/SMIA++ compliant camera modules
*
* Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -58,7 +58,7 @@ static int bounds_check(struct device *dev, uint32_t val,
if (val >= min && val <= max)
return 0;
- dev_warn(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max);
+ dev_dbg(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max);
return -EINVAL;
}
@@ -87,14 +87,14 @@ static void print_pll(struct device *dev, struct smiapp_pll *pll)
dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz);
}
-int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
- struct smiapp_pll *pll)
+static int __smiapp_pll_calculate(struct device *dev,
+ const struct smiapp_pll_limits *limits,
+ struct smiapp_pll *pll, uint32_t mul,
+ uint32_t div, uint32_t lane_op_clock_ratio)
{
uint32_t sys_div;
uint32_t best_pix_div = INT_MAX >> 1;
uint32_t vt_op_binning_div;
- uint32_t lane_op_clock_ratio;
- uint32_t mul, div;
uint32_t more_mul_min, more_mul_max;
uint32_t more_mul_factor;
uint32_t min_vt_div, max_vt_div, vt_div;
@@ -102,54 +102,6 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
unsigned int i;
int rval;
- if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE)
- lane_op_clock_ratio = pll->lanes;
- else
- lane_op_clock_ratio = 1;
- dev_dbg(dev, "lane_op_clock_ratio: %d\n", lane_op_clock_ratio);
-
- dev_dbg(dev, "binning: %dx%d\n", pll->binning_horizontal,
- pll->binning_vertical);
-
- /* CSI transfers 2 bits per clock per lane; thus times 2 */
- pll->pll_op_clk_freq_hz = pll->link_freq * 2
- * (pll->lanes / lane_op_clock_ratio);
-
- /* Figure out limits for pre-pll divider based on extclk */
- dev_dbg(dev, "min / max pre_pll_clk_div: %d / %d\n",
- limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);
- limits->max_pre_pll_clk_div =
- min_t(uint16_t, limits->max_pre_pll_clk_div,
- clk_div_even(pll->ext_clk_freq_hz /
- limits->min_pll_ip_freq_hz));
- limits->min_pre_pll_clk_div =
- max_t(uint16_t, limits->min_pre_pll_clk_div,
- clk_div_even_up(
- DIV_ROUND_UP(pll->ext_clk_freq_hz,
- limits->max_pll_ip_freq_hz)));
- dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n",
- limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);
-
- i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz);
- mul = div_u64(pll->pll_op_clk_freq_hz, i);
- div = pll->ext_clk_freq_hz / i;
- dev_dbg(dev, "mul %d / div %d\n", mul, div);
-
- limits->min_pre_pll_clk_div =
- max_t(uint16_t, limits->min_pre_pll_clk_div,
- clk_div_even_up(
- DIV_ROUND_UP(mul * pll->ext_clk_freq_hz,
- limits->max_pll_op_freq_hz)));
- dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n",
- limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);
-
- if (limits->min_pre_pll_clk_div > limits->max_pre_pll_clk_div) {
- dev_err(dev, "unable to compute pre_pll divisor\n");
- return -EINVAL;
- }
-
- pll->pre_pll_clk_div = limits->min_pre_pll_clk_div;
-
/*
* Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be
* too high.
@@ -162,7 +114,7 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
more_mul_max);
/* Don't go above max pll op frequency. */
more_mul_max =
- min_t(int,
+ min_t(uint32_t,
more_mul_max,
limits->max_pll_op_freq_hz
/ (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul));
@@ -170,7 +122,7 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
more_mul_max);
/* Don't go above the division capability of op sys clock divider. */
more_mul_max = min(more_mul_max,
- limits->max_op_sys_clk_div * pll->pre_pll_clk_div
+ limits->op.max_sys_clk_div * pll->pre_pll_clk_div
/ div);
dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %d\n",
more_mul_max);
@@ -193,14 +145,14 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
more_mul_min);
if (more_mul_min > more_mul_max) {
- dev_warn(dev,
- "unable to compute more_mul_min and more_mul_max");
+ dev_dbg(dev,
+ "unable to compute more_mul_min and more_mul_max\n");
return -EINVAL;
}
more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div;
dev_dbg(dev, "more_mul_factor: %d\n", more_mul_factor);
- more_mul_factor = lcm(more_mul_factor, limits->min_op_sys_clk_div);
+ more_mul_factor = lcm(more_mul_factor, limits->op.min_sys_clk_div);
dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n",
more_mul_factor);
i = roundup(more_mul_min, more_mul_factor);
@@ -209,7 +161,7 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
dev_dbg(dev, "final more_mul: %d\n", i);
if (i > more_mul_max) {
- dev_warn(dev, "final more_mul is bad, max %d", more_mul_max);
+ dev_dbg(dev, "final more_mul is bad, max %d\n", more_mul_max);
return -EINVAL;
}
@@ -268,19 +220,19 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
dev_dbg(dev, "min_vt_div: %d\n", min_vt_div);
min_vt_div = max(min_vt_div,
DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
- limits->max_vt_pix_clk_freq_hz));
+ limits->vt.max_pix_clk_freq_hz));
dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %d\n",
min_vt_div);
min_vt_div = max_t(uint32_t, min_vt_div,
- limits->min_vt_pix_clk_div
- * limits->min_vt_sys_clk_div);
+ limits->vt.min_pix_clk_div
+ * limits->vt.min_sys_clk_div);
dev_dbg(dev, "min_vt_div: min_vt_clk_div: %d\n", min_vt_div);
- max_vt_div = limits->max_vt_sys_clk_div * limits->max_vt_pix_clk_div;
+ max_vt_div = limits->vt.max_sys_clk_div * limits->vt.max_pix_clk_div;
dev_dbg(dev, "max_vt_div: %d\n", max_vt_div);
max_vt_div = min(max_vt_div,
DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
- limits->min_vt_pix_clk_freq_hz));
+ limits->vt.min_pix_clk_freq_hz));
dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %d\n",
max_vt_div);
@@ -288,28 +240,28 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
* Find limitsits for sys_clk_div. Not all values are possible
* with all values of pix_clk_div.
*/
- min_sys_div = limits->min_vt_sys_clk_div;
+ min_sys_div = limits->vt.min_sys_clk_div;
dev_dbg(dev, "min_sys_div: %d\n", min_sys_div);
min_sys_div = max(min_sys_div,
DIV_ROUND_UP(min_vt_div,
- limits->max_vt_pix_clk_div));
+ limits->vt.max_pix_clk_div));
dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %d\n", min_sys_div);
min_sys_div = max(min_sys_div,
pll->pll_op_clk_freq_hz
- / limits->max_vt_sys_clk_freq_hz);
+ / limits->vt.max_sys_clk_freq_hz);
dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %d\n", min_sys_div);
min_sys_div = clk_div_even_up(min_sys_div);
dev_dbg(dev, "min_sys_div: one or even: %d\n", min_sys_div);
- max_sys_div = limits->max_vt_sys_clk_div;
+ max_sys_div = limits->vt.max_sys_clk_div;
dev_dbg(dev, "max_sys_div: %d\n", max_sys_div);
max_sys_div = min(max_sys_div,
DIV_ROUND_UP(max_vt_div,
- limits->min_vt_pix_clk_div));
+ limits->vt.min_pix_clk_div));
dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %d\n", max_sys_div);
max_sys_div = min(max_sys_div,
DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
- limits->min_vt_pix_clk_freq_hz));
+ limits->vt.min_pix_clk_freq_hz));
dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %d\n", max_sys_div);
/*
@@ -322,15 +274,15 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
for (sys_div = min_sys_div;
sys_div <= max_sys_div;
sys_div += 2 - (sys_div & 1)) {
- int pix_div = DIV_ROUND_UP(vt_div, sys_div);
+ uint16_t pix_div = DIV_ROUND_UP(vt_div, sys_div);
- if (pix_div < limits->min_vt_pix_clk_div
- || pix_div > limits->max_vt_pix_clk_div) {
+ if (pix_div < limits->vt.min_pix_clk_div
+ || pix_div > limits->vt.max_pix_clk_div) {
dev_dbg(dev,
"pix_div %d too small or too big (%d--%d)\n",
pix_div,
- limits->min_vt_pix_clk_div,
- limits->max_vt_pix_clk_div);
+ limits->vt.min_pix_clk_div,
+ limits->vt.max_pix_clk_div);
continue;
}
@@ -354,16 +306,10 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
pll->pixel_rate_csi =
pll->op_pix_clk_freq_hz * lane_op_clock_ratio;
- print_pll(dev, pll);
-
- rval = bounds_check(dev, pll->pre_pll_clk_div,
- limits->min_pre_pll_clk_div,
- limits->max_pre_pll_clk_div, "pre_pll_clk_div");
- if (!rval)
- rval = bounds_check(
- dev, pll->pll_ip_clk_freq_hz,
- limits->min_pll_ip_freq_hz, limits->max_pll_ip_freq_hz,
- "pll_ip_clk_freq_hz");
+ rval = bounds_check(dev, pll->pll_ip_clk_freq_hz,
+ limits->min_pll_ip_freq_hz,
+ limits->max_pll_ip_freq_hz,
+ "pll_ip_clk_freq_hz");
if (!rval)
rval = bounds_check(
dev, pll->pll_multiplier,
@@ -377,42 +323,121 @@ int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
if (!rval)
rval = bounds_check(
dev, pll->op_sys_clk_div,
- limits->min_op_sys_clk_div, limits->max_op_sys_clk_div,
+ limits->op.min_sys_clk_div, limits->op.max_sys_clk_div,
"op_sys_clk_div");
if (!rval)
rval = bounds_check(
dev, pll->op_pix_clk_div,
- limits->min_op_pix_clk_div, limits->max_op_pix_clk_div,
+ limits->op.min_pix_clk_div, limits->op.max_pix_clk_div,
"op_pix_clk_div");
if (!rval)
rval = bounds_check(
dev, pll->op_sys_clk_freq_hz,
- limits->min_op_sys_clk_freq_hz,
- limits->max_op_sys_clk_freq_hz,
+ limits->op.min_sys_clk_freq_hz,
+ limits->op.max_sys_clk_freq_hz,
"op_sys_clk_freq_hz");
if (!rval)
rval = bounds_check(
dev, pll->op_pix_clk_freq_hz,
- limits->min_op_pix_clk_freq_hz,
- limits->max_op_pix_clk_freq_hz,
+ limits->op.min_pix_clk_freq_hz,
+ limits->op.max_pix_clk_freq_hz,
"op_pix_clk_freq_hz");
if (!rval)
rval = bounds_check(
dev, pll->vt_sys_clk_freq_hz,
- limits->min_vt_sys_clk_freq_hz,
- limits->max_vt_sys_clk_freq_hz,
+ limits->vt.min_sys_clk_freq_hz,
+ limits->vt.max_sys_clk_freq_hz,
"vt_sys_clk_freq_hz");
if (!rval)
rval = bounds_check(
dev, pll->vt_pix_clk_freq_hz,
- limits->min_vt_pix_clk_freq_hz,
- limits->max_vt_pix_clk_freq_hz,
+ limits->vt.min_pix_clk_freq_hz,
+ limits->vt.max_pix_clk_freq_hz,
"vt_pix_clk_freq_hz");
return rval;
}
+
+int smiapp_pll_calculate(struct device *dev,
+ const struct smiapp_pll_limits *limits,
+ struct smiapp_pll *pll)
+{
+ uint16_t min_pre_pll_clk_div;
+ uint16_t max_pre_pll_clk_div;
+ uint32_t lane_op_clock_ratio;
+ uint32_t mul, div;
+ unsigned int i;
+ int rval = -EINVAL;
+
+ if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE)
+ lane_op_clock_ratio = pll->csi2.lanes;
+ else
+ lane_op_clock_ratio = 1;
+ dev_dbg(dev, "lane_op_clock_ratio: %d\n", lane_op_clock_ratio);
+
+ dev_dbg(dev, "binning: %dx%d\n", pll->binning_horizontal,
+ pll->binning_vertical);
+
+ switch (pll->bus_type) {
+ case SMIAPP_PLL_BUS_TYPE_CSI2:
+ /* CSI transfers 2 bits per clock per lane; thus times 2 */
+ pll->pll_op_clk_freq_hz = pll->link_freq * 2
+ * (pll->csi2.lanes / lane_op_clock_ratio);
+ break;
+ case SMIAPP_PLL_BUS_TYPE_PARALLEL:
+ pll->pll_op_clk_freq_hz = pll->link_freq * pll->bits_per_pixel
+ / DIV_ROUND_UP(pll->bits_per_pixel,
+ pll->parallel.bus_width);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Figure out limits for pre-pll divider based on extclk */
+ dev_dbg(dev, "min / max pre_pll_clk_div: %d / %d\n",
+ limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);
+ max_pre_pll_clk_div =
+ min_t(uint16_t, limits->max_pre_pll_clk_div,
+ clk_div_even(pll->ext_clk_freq_hz /
+ limits->min_pll_ip_freq_hz));
+ min_pre_pll_clk_div =
+ max_t(uint16_t, limits->min_pre_pll_clk_div,
+ clk_div_even_up(
+ DIV_ROUND_UP(pll->ext_clk_freq_hz,
+ limits->max_pll_ip_freq_hz)));
+ dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n",
+ min_pre_pll_clk_div, max_pre_pll_clk_div);
+
+ i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz);
+ mul = div_u64(pll->pll_op_clk_freq_hz, i);
+ div = pll->ext_clk_freq_hz / i;
+ dev_dbg(dev, "mul %d / div %d\n", mul, div);
+
+ min_pre_pll_clk_div =
+ max_t(uint16_t, min_pre_pll_clk_div,
+ clk_div_even_up(
+ DIV_ROUND_UP(mul * pll->ext_clk_freq_hz,
+ limits->max_pll_op_freq_hz)));
+ dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n",
+ min_pre_pll_clk_div, max_pre_pll_clk_div);
+
+ for (pll->pre_pll_clk_div = min_pre_pll_clk_div;
+ pll->pre_pll_clk_div <= max_pre_pll_clk_div;
+ pll->pre_pll_clk_div += 2 - (pll->pre_pll_clk_div & 1)) {
+ rval = __smiapp_pll_calculate(dev, limits, pll, mul, div,
+ lane_op_clock_ratio);
+ if (rval)
+ continue;
+
+ print_pll(dev, pll);
+ return 0;
+ }
+
+ dev_info(dev, "unable to compute pre_pll divisor\n");
+ return rval;
+}
EXPORT_SYMBOL_GPL(smiapp_pll_calculate);
-MODULE_AUTHOR("Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>");
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@iki.fi>");
MODULE_DESCRIPTION("Generic SMIA/SMIA++ PLL calculator");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/smiapp-pll.h b/drivers/media/i2c/smiapp-pll.h
index cb2d2db5d02..a4a649834a1 100644
--- a/drivers/media/i2c/smiapp-pll.h
+++ b/drivers/media/i2c/smiapp-pll.h
@@ -4,7 +4,7 @@
* Generic driver for SMIA/SMIA++ compliant camera modules
*
* Copyright (C) 2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -27,16 +27,34 @@
#include <linux/device.h>
+/* CSI-2 or CCP-2 */
+#define SMIAPP_PLL_BUS_TYPE_CSI2 0x00
+#define SMIAPP_PLL_BUS_TYPE_PARALLEL 0x01
+
+/* op pix clock is for all lanes in total normally */
+#define SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0)
+#define SMIAPP_PLL_FLAG_NO_OP_CLOCKS (1 << 1)
+
struct smiapp_pll {
- uint8_t lanes;
+ /* input values */
+ uint8_t bus_type;
+ union {
+ struct {
+ uint8_t lanes;
+ } csi2;
+ struct {
+ uint8_t bus_width;
+ } parallel;
+ };
+ uint8_t flags;
uint8_t binning_horizontal;
uint8_t binning_vertical;
uint8_t scale_m;
uint8_t scale_n;
uint8_t bits_per_pixel;
- uint16_t flags;
uint32_t link_freq;
+ /* output values */
uint16_t pre_pll_clk_div;
uint16_t pll_multiplier;
uint16_t op_sys_clk_div;
@@ -55,6 +73,17 @@ struct smiapp_pll {
uint32_t pixel_rate_csi;
};
+struct smiapp_pll_branch_limits {
+ uint16_t min_sys_clk_div;
+ uint16_t max_sys_clk_div;
+ uint32_t min_sys_clk_freq_hz;
+ uint32_t max_sys_clk_freq_hz;
+ uint16_t min_pix_clk_div;
+ uint16_t max_pix_clk_div;
+ uint32_t min_pix_clk_freq_hz;
+ uint32_t max_pix_clk_freq_hz;
+};
+
struct smiapp_pll_limits {
/* Strict PLL limits */
uint32_t min_ext_clk_freq_hz;
@@ -68,36 +97,18 @@ struct smiapp_pll_limits {
uint32_t min_pll_op_freq_hz;
uint32_t max_pll_op_freq_hz;
- uint16_t min_vt_sys_clk_div;
- uint16_t max_vt_sys_clk_div;
- uint32_t min_vt_sys_clk_freq_hz;
- uint32_t max_vt_sys_clk_freq_hz;
- uint16_t min_vt_pix_clk_div;
- uint16_t max_vt_pix_clk_div;
- uint32_t min_vt_pix_clk_freq_hz;
- uint32_t max_vt_pix_clk_freq_hz;
-
- uint16_t min_op_sys_clk_div;
- uint16_t max_op_sys_clk_div;
- uint32_t min_op_sys_clk_freq_hz;
- uint32_t max_op_sys_clk_freq_hz;
- uint16_t min_op_pix_clk_div;
- uint16_t max_op_pix_clk_div;
- uint32_t min_op_pix_clk_freq_hz;
- uint32_t max_op_pix_clk_freq_hz;
+ struct smiapp_pll_branch_limits vt;
+ struct smiapp_pll_branch_limits op;
/* Other relevant limits */
uint32_t min_line_length_pck_bin;
uint32_t min_line_length_pck;
};
-/* op pix clock is for all lanes in total normally */
-#define SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0)
-#define SMIAPP_PLL_FLAG_NO_OP_CLOCKS (1 << 1)
-
struct device;
-int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
+int smiapp_pll_calculate(struct device *dev,
+ const struct smiapp_pll_limits *limits,
struct smiapp_pll *pll);
#endif /* SMIAPP_PLL_H */
diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c
index e08e588ad24..83c7ed7ffcc 100644
--- a/drivers/media/i2c/smiapp/smiapp-core.c
+++ b/drivers/media/i2c/smiapp/smiapp-core.c
@@ -4,7 +4,7 @@
* Generic driver for SMIA/SMIA++ compliant camera modules
*
* Copyright (C) 2010--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
*
* Based on smiapp driver by Vimarsh Zutshi
* Based on jt8ev1.c by Vimarsh Zutshi
@@ -252,23 +252,23 @@ static int smiapp_pll_update(struct smiapp_sensor *sensor)
.min_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ],
.max_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ],
- .min_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV],
- .max_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV],
- .min_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV],
- .max_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV],
- .min_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ],
- .max_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ],
- .min_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ],
- .max_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ],
-
- .min_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV],
- .max_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV],
- .min_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV],
- .max_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV],
- .min_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ],
- .max_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ],
- .min_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ],
- .max_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ],
+ .op.min_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV],
+ .op.max_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV],
+ .op.min_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV],
+ .op.max_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV],
+ .op.min_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ],
+ .op.max_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ],
+ .op.min_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ],
+ .op.max_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ],
+
+ .vt.min_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV],
+ .vt.max_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV],
+ .vt.min_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV],
+ .vt.max_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV],
+ .vt.min_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ],
+ .vt.max_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ],
+ .vt.min_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ],
+ .vt.max_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ],
.min_line_length_pck_bin = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN],
.min_line_length_pck = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK],
@@ -276,11 +276,6 @@ static int smiapp_pll_update(struct smiapp_sensor *sensor)
struct smiapp_pll *pll = &sensor->pll;
int rval;
- memset(&sensor->pll, 0, sizeof(sensor->pll));
-
- pll->lanes = sensor->platform_data->lanes;
- pll->ext_clk_freq_hz = sensor->platform_data->ext_clk;
-
if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) {
/*
* Fill in operational clock divisors limits from the
@@ -288,28 +283,14 @@ static int smiapp_pll_update(struct smiapp_sensor *sensor)
* requirements regarding them are essentially the
* same as on VT ones.
*/
- lim.min_op_sys_clk_div = lim.min_vt_sys_clk_div;
- lim.max_op_sys_clk_div = lim.max_vt_sys_clk_div;
- lim.min_op_pix_clk_div = lim.min_vt_pix_clk_div;
- lim.max_op_pix_clk_div = lim.max_vt_pix_clk_div;
- lim.min_op_sys_clk_freq_hz = lim.min_vt_sys_clk_freq_hz;
- lim.max_op_sys_clk_freq_hz = lim.max_vt_sys_clk_freq_hz;
- lim.min_op_pix_clk_freq_hz = lim.min_vt_pix_clk_freq_hz;
- lim.max_op_pix_clk_freq_hz = lim.max_vt_pix_clk_freq_hz;
- /* Profile 0 sensors have no separate OP clock branch. */
- pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS;
+ lim.op = lim.vt;
}
- if (smiapp_needs_quirk(sensor,
- SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE))
- pll->flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE;
-
pll->binning_horizontal = sensor->binning_horizontal;
pll->binning_vertical = sensor->binning_vertical;
pll->link_freq =
sensor->link_freq->qmenu_int[sensor->link_freq->val];
pll->scale_m = sensor->scale_m;
- pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
pll->bits_per_pixel = sensor->csi_format->compressed;
rval = smiapp_pll_calculate(&client->dev, &lim, pll);
@@ -1010,7 +991,7 @@ static int smiapp_setup_flash_strobe(struct smiapp_sensor *sensor)
* do not change, or if you do at least know what you're
* doing. :-)
*
- * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> 2010-10-25
+ * Sakari Ailus <sakari.ailus@iki.fi> 2010-10-25
*
* flash_strobe_length [us] / 10^6 = (tFlash_strobe_width_ctrl
* / EXTCLK freq [Hz]) * flash_strobe_adjustment
@@ -2369,6 +2350,7 @@ static int smiapp_registered(struct v4l2_subdev *subdev)
{
struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct smiapp_pll *pll = &sensor->pll;
struct smiapp_subdev *last = NULL;
u32 tmp;
unsigned int i;
@@ -2635,6 +2617,18 @@ static int smiapp_registered(struct v4l2_subdev *subdev)
if (rval < 0)
goto out_nvm_release;
+ /* prepare PLL configuration input values */
+ pll->bus_type = SMIAPP_PLL_BUS_TYPE_CSI2;
+ pll->csi2.lanes = sensor->platform_data->lanes;
+ pll->ext_clk_freq_hz = sensor->platform_data->ext_clk;
+ /* Profile 0 sensors have no separate OP clock branch. */
+ if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0)
+ pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS;
+ if (smiapp_needs_quirk(sensor,
+ SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE))
+ pll->flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE;
+ pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
+
rval = smiapp_update_mode(sensor);
if (rval) {
dev_err(&client->dev, "update mode failed\n");
@@ -2893,6 +2887,6 @@ static struct i2c_driver smiapp_i2c_driver = {
module_i2c_driver(smiapp_i2c_driver);
-MODULE_AUTHOR("Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>");
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@iki.fi>");
MODULE_DESCRIPTION("Generic SMIA/SMIA++ camera module driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/smiapp/smiapp-limits.c b/drivers/media/i2c/smiapp/smiapp-limits.c
index fb2f81ad8c3..847cb235e19 100644
--- a/drivers/media/i2c/smiapp/smiapp-limits.c
+++ b/drivers/media/i2c/smiapp/smiapp-limits.c
@@ -4,7 +4,7 @@
* Generic driver for SMIA/SMIA++ compliant camera modules
*
* Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/drivers/media/i2c/smiapp/smiapp-limits.h b/drivers/media/i2c/smiapp/smiapp-limits.h
index 9ae765e23ea..343e9c3827f 100644
--- a/drivers/media/i2c/smiapp/smiapp-limits.h
+++ b/drivers/media/i2c/smiapp/smiapp-limits.h
@@ -4,7 +4,7 @@
* Generic driver for SMIA/SMIA++ compliant camera modules
*
* Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.c b/drivers/media/i2c/smiapp/smiapp-quirk.c
index 725cf62836c..bb8c506e0e3 100644
--- a/drivers/media/i2c/smiapp/smiapp-quirk.c
+++ b/drivers/media/i2c/smiapp/smiapp-quirk.c
@@ -4,7 +4,7 @@
* Generic driver for SMIA/SMIA++ compliant camera modules
*
* Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.h b/drivers/media/i2c/smiapp/smiapp-quirk.h
index 86fd3e8bfb0..504a6d80ced 100644
--- a/drivers/media/i2c/smiapp/smiapp-quirk.h
+++ b/drivers/media/i2c/smiapp/smiapp-quirk.h
@@ -4,7 +4,7 @@
* Generic driver for SMIA/SMIA++ compliant camera modules
*
* Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/drivers/media/i2c/smiapp/smiapp-reg-defs.h b/drivers/media/i2c/smiapp/smiapp-reg-defs.h
index defa7c5adeb..3aa0ca948d8 100644
--- a/drivers/media/i2c/smiapp/smiapp-reg-defs.h
+++ b/drivers/media/i2c/smiapp/smiapp-reg-defs.h
@@ -4,7 +4,7 @@
* Generic driver for SMIA/SMIA++ compliant camera modules
*
* Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/drivers/media/i2c/smiapp/smiapp-reg.h b/drivers/media/i2c/smiapp/smiapp-reg.h
index 54568ca2fe6..b0dcbb8fa5e 100644
--- a/drivers/media/i2c/smiapp/smiapp-reg.h
+++ b/drivers/media/i2c/smiapp/smiapp-reg.h
@@ -4,7 +4,7 @@
* Generic driver for SMIA/SMIA++ compliant camera modules
*
* Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/drivers/media/i2c/smiapp/smiapp-regs.c b/drivers/media/i2c/smiapp/smiapp-regs.c
index 70e0d8db013..4fac32cfcb3 100644
--- a/drivers/media/i2c/smiapp/smiapp-regs.c
+++ b/drivers/media/i2c/smiapp/smiapp-regs.c
@@ -4,7 +4,7 @@
* Generic driver for SMIA/SMIA++ compliant camera modules
*
* Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/drivers/media/i2c/smiapp/smiapp-regs.h b/drivers/media/i2c/smiapp/smiapp-regs.h
index 7f9013b4797..eefc6c84d5f 100644
--- a/drivers/media/i2c/smiapp/smiapp-regs.h
+++ b/drivers/media/i2c/smiapp/smiapp-regs.h
@@ -4,7 +4,7 @@
* Generic driver for SMIA/SMIA++ compliant camera modules
*
* Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/smiapp/smiapp.h
index 4182a695ab5..7cc5aae662f 100644
--- a/drivers/media/i2c/smiapp/smiapp.h
+++ b/drivers/media/i2c/smiapp/smiapp.h
@@ -4,7 +4,7 @@
* Generic driver for SMIA/SMIA++ compliant camera modules
*
* Copyright (C) 2010--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c
index 333ef178d6f..d40a8858be0 100644
--- a/drivers/media/i2c/soc_camera/mt9v022.c
+++ b/drivers/media/i2c/soc_camera/mt9v022.c
@@ -15,6 +15,7 @@
#include <linux/log2.h>
#include <linux/module.h>
+#include <media/mt9v022.h>
#include <media/soc_camera.h>
#include <media/soc_mediabus.h>
#include <media/v4l2-subdev.h>
@@ -50,6 +51,7 @@ MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\"");
#define MT9V022_PIXEL_OPERATION_MODE 0x0f
#define MT9V022_LED_OUT_CONTROL 0x1b
#define MT9V022_ADC_MODE_CONTROL 0x1c
+#define MT9V022_REG32 0x20
#define MT9V022_ANALOG_GAIN 0x35
#define MT9V022_BLACK_LEVEL_CALIB_CTRL 0x47
#define MT9V022_PIXCLK_FV_LV 0x74
@@ -71,7 +73,15 @@ MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\"");
#define MT9V022_COLUMN_SKIP 1
#define MT9V022_ROW_SKIP 4
-#define is_mt9v024(id) (id == 0x1324)
+#define MT9V022_HORIZONTAL_BLANKING_MIN 43
+#define MT9V022_HORIZONTAL_BLANKING_MAX 1023
+#define MT9V022_HORIZONTAL_BLANKING_DEF 94
+#define MT9V022_VERTICAL_BLANKING_MIN 2
+#define MT9V022_VERTICAL_BLANKING_MAX 3000
+#define MT9V022_VERTICAL_BLANKING_DEF 45
+
+#define is_mt9v022_rev3(id) (id == 0x1313)
+#define is_mt9v024(id) (id == 0x1324)
/* MT9V022 has only one fixed colorspace per pixelcode */
struct mt9v022_datafmt {
@@ -136,6 +146,8 @@ struct mt9v022 {
struct v4l2_ctrl *autogain;
struct v4l2_ctrl *gain;
};
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
struct v4l2_rect rect; /* Sensor window */
const struct mt9v022_datafmt *fmt;
const struct mt9v022_datafmt *fmts;
@@ -143,6 +155,7 @@ struct mt9v022 {
int num_fmts;
int model; /* V4L2_IDENT_MT9V022* codes from v4l2-chip-ident.h */
u16 chip_control;
+ u16 chip_version;
unsigned short y_skip_top; /* Lines to skip at the top */
};
@@ -225,12 +238,32 @@ static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable)
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9v022 *mt9v022 = to_mt9v022(client);
- if (enable)
+ if (enable) {
/* Switch to master "normal" mode */
mt9v022->chip_control &= ~0x10;
- else
+ if (is_mt9v022_rev3(mt9v022->chip_version) ||
+ is_mt9v024(mt9v022->chip_version)) {
+ /*
+ * Unset snapshot mode specific settings: clear bit 9
+ * and bit 2 in reg. 0x20 when in normal mode.
+ */
+ if (reg_clear(client, MT9V022_REG32, 0x204))
+ return -EIO;
+ }
+ } else {
/* Switch to snapshot mode */
mt9v022->chip_control |= 0x10;
+ if (is_mt9v022_rev3(mt9v022->chip_version) ||
+ is_mt9v024(mt9v022->chip_version)) {
+ /*
+ * Required settings for snapshot mode: set bit 9
+ * (RST enable) and bit 2 (CR enable) in reg. 0x20
+ * See TechNote TN0960 or TN-09-225.
+ */
+ if (reg_set(client, MT9V022_REG32, 0x204))
+ return -EIO;
+ }
+ }
if (reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control) < 0)
return -EIO;
@@ -282,11 +315,10 @@ static int mt9v022_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
* Default 94, Phytec driver says:
* "width + horizontal blank >= 660"
*/
- ret = reg_write(client, MT9V022_HORIZONTAL_BLANKING,
- rect.width > 660 - 43 ? 43 :
- 660 - rect.width);
+ ret = v4l2_ctrl_s_ctrl(mt9v022->hblank,
+ rect.width > 660 - 43 ? 43 : 660 - rect.width);
if (!ret)
- ret = reg_write(client, MT9V022_VERTICAL_BLANKING, 45);
+ ret = v4l2_ctrl_s_ctrl(mt9v022->vblank, 45);
if (!ret)
ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect.width);
if (!ret)
@@ -509,6 +541,18 @@ static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
range = exp->maximum - exp->minimum;
exp->val = ((data - 1) * range + 239) / 479 + exp->minimum;
return 0;
+ case V4L2_CID_HBLANK:
+ data = reg_read(client, MT9V022_HORIZONTAL_BLANKING);
+ if (data < 0)
+ return -EIO;
+ ctrl->val = data;
+ return 0;
+ case V4L2_CID_VBLANK:
+ data = reg_read(client, MT9V022_VERTICAL_BLANKING);
+ if (data < 0)
+ return -EIO;
+ ctrl->val = data;
+ return 0;
}
return -EINVAL;
}
@@ -590,6 +634,16 @@ static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl)
return -EIO;
}
return 0;
+ case V4L2_CID_HBLANK:
+ if (reg_write(client, MT9V022_HORIZONTAL_BLANKING,
+ ctrl->val) < 0)
+ return -EIO;
+ return 0;
+ case V4L2_CID_VBLANK:
+ if (reg_write(client, MT9V022_VERTICAL_BLANKING,
+ ctrl->val) < 0)
+ return -EIO;
+ return 0;
}
return -EINVAL;
}
@@ -621,6 +675,8 @@ static int mt9v022_video_probe(struct i2c_client *client)
goto ei2c;
}
+ mt9v022->chip_version = data;
+
mt9v022->reg = is_mt9v024(data) ? &mt9v024_register :
&mt9v022_register;
@@ -819,6 +875,7 @@ static int mt9v022_probe(struct i2c_client *client,
struct mt9v022 *mt9v022;
struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct mt9v022_platform_data *pdata = icl->priv;
int ret;
if (!icl) {
@@ -857,10 +914,21 @@ static int mt9v022_probe(struct i2c_client *client,
mt9v022->exposure = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
V4L2_CID_EXPOSURE, 1, 255, 1, 255);
+ mt9v022->hblank = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_HBLANK, MT9V022_HORIZONTAL_BLANKING_MIN,
+ MT9V022_HORIZONTAL_BLANKING_MAX, 1,
+ MT9V022_HORIZONTAL_BLANKING_DEF);
+
+ mt9v022->vblank = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_VBLANK, MT9V022_VERTICAL_BLANKING_MIN,
+ MT9V022_VERTICAL_BLANKING_MAX, 1,
+ MT9V022_VERTICAL_BLANKING_DEF);
+
mt9v022->subdev.ctrl_handler = &mt9v022->hdl;
if (mt9v022->hdl.error) {
int err = mt9v022->hdl.error;
+ dev_err(&client->dev, "control initialisation err %d\n", err);
kfree(mt9v022);
return err;
}
@@ -871,10 +939,10 @@ static int mt9v022_probe(struct i2c_client *client,
mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT;
/*
- * MT9V022 _really_ corrupts the first read out line.
- * TODO: verify on i.MX31
+ * On some platforms the first read out line is corrupted.
+ * Workaround it by skipping if indicated by platform data.
*/
- mt9v022->y_skip_top = 1;
+ mt9v022->y_skip_top = pdata ? pdata->y_skip_top : 0;
mt9v022->rect.left = MT9V022_COLUMN_SKIP;
mt9v022->rect.top = MT9V022_ROW_SKIP;
mt9v022->rect.width = MT9V022_MAX_WIDTH;
diff --git a/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/soc_camera/ov2640.c
index d2d298b6354..66698a83bda 100644
--- a/drivers/media/i2c/soc_camera/ov2640.c
+++ b/drivers/media/i2c/soc_camera/ov2640.c
@@ -586,9 +586,20 @@ static const struct regval_list ov2640_format_change_preamble_regs[] = {
ENDMARKER,
};
-static const struct regval_list ov2640_yuv422_regs[] = {
+static const struct regval_list ov2640_yuyv_regs[] = {
+ { IMAGE_MODE, IMAGE_MODE_YUV422 },
+ { 0xd7, 0x03 },
+ { 0x33, 0xa0 },
+ { 0xe5, 0x1f },
+ { 0xe1, 0x67 },
+ { RESET, 0x00 },
+ { R_BYPASS, R_BYPASS_USE_DSP },
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_uyvy_regs[] = {
{ IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_YUV422 },
- { 0xD7, 0x01 },
+ { 0xd7, 0x01 },
{ 0x33, 0xa0 },
{ 0xe1, 0x67 },
{ RESET, 0x00 },
@@ -596,7 +607,15 @@ static const struct regval_list ov2640_yuv422_regs[] = {
ENDMARKER,
};
-static const struct regval_list ov2640_rgb565_regs[] = {
+static const struct regval_list ov2640_rgb565_be_regs[] = {
+ { IMAGE_MODE, IMAGE_MODE_RGB565 },
+ { 0xd7, 0x03 },
+ { RESET, 0x00 },
+ { R_BYPASS, R_BYPASS_USE_DSP },
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_rgb565_le_regs[] = {
{ IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_RGB565 },
{ 0xd7, 0x03 },
{ RESET, 0x00 },
@@ -605,7 +624,9 @@ static const struct regval_list ov2640_rgb565_regs[] = {
};
static enum v4l2_mbus_pixelcode ov2640_codes[] = {
+ V4L2_MBUS_FMT_YUYV8_2X8,
V4L2_MBUS_FMT_UYVY8_2X8,
+ V4L2_MBUS_FMT_RGB565_2X8_BE,
V4L2_MBUS_FMT_RGB565_2X8_LE,
};
@@ -787,14 +808,22 @@ static int ov2640_set_params(struct i2c_client *client, u32 *width, u32 *height,
/* select format */
priv->cfmt_code = 0;
switch (code) {
+ case V4L2_MBUS_FMT_RGB565_2X8_BE:
+ dev_dbg(&client->dev, "%s: Selected cfmt RGB565 BE", __func__);
+ selected_cfmt_regs = ov2640_rgb565_be_regs;
+ break;
case V4L2_MBUS_FMT_RGB565_2X8_LE:
- dev_dbg(&client->dev, "%s: Selected cfmt RGB565", __func__);
- selected_cfmt_regs = ov2640_rgb565_regs;
+ dev_dbg(&client->dev, "%s: Selected cfmt RGB565 LE", __func__);
+ selected_cfmt_regs = ov2640_rgb565_le_regs;
+ break;
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ dev_dbg(&client->dev, "%s: Selected cfmt YUYV (YUV422)", __func__);
+ selected_cfmt_regs = ov2640_yuyv_regs;
break;
default:
case V4L2_MBUS_FMT_UYVY8_2X8:
- dev_dbg(&client->dev, "%s: Selected cfmt YUV422", __func__);
- selected_cfmt_regs = ov2640_yuv422_regs;
+ dev_dbg(&client->dev, "%s: Selected cfmt UYVY", __func__);
+ selected_cfmt_regs = ov2640_uyvy_regs;
}
/* reset hardware */
@@ -859,10 +888,12 @@ static int ov2640_g_fmt(struct v4l2_subdev *sd,
mf->code = priv->cfmt_code;
switch (mf->code) {
+ case V4L2_MBUS_FMT_RGB565_2X8_BE:
case V4L2_MBUS_FMT_RGB565_2X8_LE:
mf->colorspace = V4L2_COLORSPACE_SRGB;
break;
default:
+ case V4L2_MBUS_FMT_YUYV8_2X8:
case V4L2_MBUS_FMT_UYVY8_2X8:
mf->colorspace = V4L2_COLORSPACE_JPEG;
}
@@ -879,11 +910,13 @@ static int ov2640_s_fmt(struct v4l2_subdev *sd,
switch (mf->code) {
+ case V4L2_MBUS_FMT_RGB565_2X8_BE:
case V4L2_MBUS_FMT_RGB565_2X8_LE:
mf->colorspace = V4L2_COLORSPACE_SRGB;
break;
default:
mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ case V4L2_MBUS_FMT_YUYV8_2X8:
case V4L2_MBUS_FMT_UYVY8_2X8:
mf->colorspace = V4L2_COLORSPACE_JPEG;
}
@@ -896,21 +929,21 @@ static int ov2640_s_fmt(struct v4l2_subdev *sd,
static int ov2640_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
- const struct ov2640_win_size *win;
-
/*
- * select suitable win
+ * select suitable win, but don't store it
*/
- win = ov2640_select_win(&mf->width, &mf->height);
+ ov2640_select_win(&mf->width, &mf->height);
mf->field = V4L2_FIELD_NONE;
switch (mf->code) {
+ case V4L2_MBUS_FMT_RGB565_2X8_BE:
case V4L2_MBUS_FMT_RGB565_2X8_LE:
mf->colorspace = V4L2_COLORSPACE_SRGB;
break;
default:
mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ case V4L2_MBUS_FMT_YUYV8_2X8:
case V4L2_MBUS_FMT_UYVY8_2X8:
mf->colorspace = V4L2_COLORSPACE_JPEG;
}
diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c
index 42ae9dc9c57..f434a19b9bc 100644
--- a/drivers/media/i2c/vs6624.c
+++ b/drivers/media/i2c/vs6624.c
@@ -910,18 +910,7 @@ static struct i2c_driver vs6624_driver = {
.id_table = vs6624_id,
};
-static __init int vs6624_init(void)
-{
- return i2c_add_driver(&vs6624_driver);
-}
-
-static __exit void vs6624_exit(void)
-{
- i2c_del_driver(&vs6624_driver);
-}
-
-module_init(vs6624_init);
-module_exit(vs6624_exit);
+module_i2c_driver(vs6624_driver);
MODULE_DESCRIPTION("VS6624 sensor driver");
MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
diff --git a/drivers/media/mmc/siano/Kconfig b/drivers/media/mmc/siano/Kconfig
index fa62475be3b..aa05ad3c1cc 100644
--- a/drivers/media/mmc/siano/Kconfig
+++ b/drivers/media/mmc/siano/Kconfig
@@ -4,7 +4,8 @@
config SMS_SDIO_DRV
tristate "Siano SMS1xxx based MDTV via SDIO interface"
- depends on DVB_CORE && RC_CORE && HAS_DMA
+ depends on DVB_CORE && HAS_DMA
depends on MMC
+ select MEDIA_COMMON_OPTIONS
---help---
Choose if you would like to have Siano's support for SDIO interface
diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c
index 56c6c77793d..de6f41f1918 100644
--- a/drivers/media/pci/bt8xx/bttv-driver.c
+++ b/drivers/media/pci/bt8xx/bttv-driver.c
@@ -200,7 +200,7 @@ static void flush_request_modules(struct bttv *dev)
}
#else
#define request_modules(dev)
-#define flush_request_modules(dev)
+#define flush_request_modules(dev) do {} while(0)
#endif /* CONFIG_MODULES */
@@ -301,11 +301,10 @@ const struct bttv_tvnorm bttv_tvnorms[] = {
/* totalwidth */ 1135,
/* sqwidth */ 944,
/* vdelay */ 0x20,
- /* sheight */ 576,
- /* videostart0 */ 23)
/* bt878 (and bt848?) can capture another
line below active video. */
- .cropcap.bounds.height = (576 + 2) + 0x20 - 2,
+ /* sheight */ (576 + 2) + 0x20 - 2,
+ /* videostart0 */ 23)
},{
.v4l2_id = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
.name = "NTSC",
diff --git a/drivers/media/pci/cx18/cx18-alsa-main.c b/drivers/media/pci/cx18/cx18-alsa-main.c
index 6d2a98246b6..8e971ff6058 100644
--- a/drivers/media/pci/cx18/cx18-alsa-main.c
+++ b/drivers/media/pci/cx18/cx18-alsa-main.c
@@ -197,7 +197,7 @@ err_exit:
return ret;
}
-int cx18_alsa_load(struct cx18 *cx)
+static int __init cx18_alsa_load(struct cx18 *cx)
{
struct v4l2_device *v4l2_dev = &cx->v4l2_dev;
struct cx18_stream *s;
diff --git a/drivers/media/pci/cx18/cx18-alsa-pcm.c b/drivers/media/pci/cx18/cx18-alsa-pcm.c
index 7a5b84a86bb..180077c4912 100644
--- a/drivers/media/pci/cx18/cx18-alsa-pcm.c
+++ b/drivers/media/pci/cx18/cx18-alsa-pcm.c
@@ -37,6 +37,7 @@
#include "cx18-streams.h"
#include "cx18-fileops.h"
#include "cx18-alsa.h"
+#include "cx18-alsa-pcm.h"
static unsigned int pcm_debug;
module_param(pcm_debug, int, 0644);
diff --git a/drivers/media/pci/cx18/cx18-i2c.c b/drivers/media/pci/cx18/cx18-i2c.c
index 51609d5c88c..4908eb7bcf6 100644
--- a/drivers/media/pci/cx18/cx18-i2c.c
+++ b/drivers/media/pci/cx18/cx18-i2c.c
@@ -98,7 +98,7 @@ static int cx18_i2c_new_ir(struct cx18 *cx, struct i2c_adapter *adap, u32 hw,
case CX18_HW_Z8F0811_IR_RX_HAUP:
init_data->ir_codes = RC_MAP_HAUPPAUGE;
init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
- init_data->type = RC_TYPE_RC5;
+ init_data->type = RC_BIT_RC5;
init_data->name = cx->card_name;
info.platform_data = init_data;
break;
diff --git a/drivers/media/pci/cx18/cx18-streams.c b/drivers/media/pci/cx18/cx18-streams.c
index 72af9b5c2d7..843c62b2f48 100644
--- a/drivers/media/pci/cx18/cx18-streams.c
+++ b/drivers/media/pci/cx18/cx18-streams.c
@@ -97,7 +97,7 @@ static struct {
};
-void cx18_dma_free(struct videobuf_queue *q,
+static void cx18_dma_free(struct videobuf_queue *q,
struct cx18_stream *s, struct cx18_videobuf_buffer *buf)
{
videobuf_waiton(q, &buf->vb, 0, 0);
diff --git a/drivers/media/pci/cx23885/altera-ci.c b/drivers/media/pci/cx23885/altera-ci.c
index 495781ee471..2926f7fadcc 100644
--- a/drivers/media/pci/cx23885/altera-ci.c
+++ b/drivers/media/pci/cx23885/altera-ci.c
@@ -263,7 +263,7 @@ static int netup_fpga_op_rw(struct fpga_internal *inter, int addr,
}
/* flag - mem/io, read - read/write */
-int altera_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
+static int altera_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
u8 flag, u8 read, int addr, u8 val)
{
@@ -298,31 +298,32 @@ int altera_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
return mem;
}
-int altera_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
- int slot, int addr)
+static int altera_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
+ int slot, int addr)
{
return altera_ci_op_cam(en50221, slot, 0, NETUP_CI_FLG_RD, addr, 0);
}
-int altera_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
- int slot, int addr, u8 data)
+static int altera_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
+ int slot, int addr, u8 data)
{
return altera_ci_op_cam(en50221, slot, 0, 0, addr, data);
}
-int altera_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr)
+static int altera_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221,
+ int slot, u8 addr)
{
return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL,
NETUP_CI_FLG_RD, addr, 0);
}
-int altera_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot,
- u8 addr, u8 data)
+static int altera_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot,
+ u8 addr, u8 data)
{
return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL, 0, addr, data);
}
-int altera_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot)
+static int altera_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot)
{
struct altera_ci_state *state = en50221->data;
struct fpga_internal *inter = state->internal;
@@ -365,13 +366,13 @@ int altera_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot)
return 0;
}
-int altera_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot)
+static int altera_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot)
{
/* not implemented */
return 0;
}
-int altera_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot)
+static int altera_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot)
{
struct altera_ci_state *state = en50221->data;
struct fpga_internal *inter = state->internal;
@@ -448,8 +449,8 @@ int altera_ci_irq(void *dev)
}
EXPORT_SYMBOL(altera_ci_irq);
-int altera_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, int slot,
- int open)
+static int altera_poll_ci_slot_status(struct dvb_ca_en50221 *en50221,
+ int slot, int open)
{
struct altera_ci_state *state = en50221->data;
@@ -459,7 +460,7 @@ int altera_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, int slot,
return state->status;
}
-void altera_hw_filt_release(void *main_dev, int filt_nr)
+static void altera_hw_filt_release(void *main_dev, int filt_nr)
{
struct fpga_inode *temp_int = find_inode(main_dev);
struct netup_hw_pid_filter *pid_filt = NULL;
@@ -581,7 +582,7 @@ static void altera_toggle_fullts_streaming(struct netup_hw_pid_filter *pid_filt,
mutex_unlock(&inter->fpga_mutex);
}
-int altera_pid_feed_control(void *demux_dev, int filt_nr,
+static int altera_pid_feed_control(void *demux_dev, int filt_nr,
struct dvb_demux_feed *feed, int onoff)
{
struct fpga_inode *temp_int = find_dinode(demux_dev);
@@ -603,41 +604,41 @@ int altera_pid_feed_control(void *demux_dev, int filt_nr,
}
EXPORT_SYMBOL(altera_pid_feed_control);
-int altera_ci_start_feed(struct dvb_demux_feed *feed, int num)
+static int altera_ci_start_feed(struct dvb_demux_feed *feed, int num)
{
altera_pid_feed_control(feed->demux, num, feed, 1);
return 0;
}
-int altera_ci_stop_feed(struct dvb_demux_feed *feed, int num)
+static int altera_ci_stop_feed(struct dvb_demux_feed *feed, int num)
{
altera_pid_feed_control(feed->demux, num, feed, 0);
return 0;
}
-int altera_ci_start_feed_1(struct dvb_demux_feed *feed)
+static int altera_ci_start_feed_1(struct dvb_demux_feed *feed)
{
return altera_ci_start_feed(feed, 1);
}
-int altera_ci_stop_feed_1(struct dvb_demux_feed *feed)
+static int altera_ci_stop_feed_1(struct dvb_demux_feed *feed)
{
return altera_ci_stop_feed(feed, 1);
}
-int altera_ci_start_feed_2(struct dvb_demux_feed *feed)
+static int altera_ci_start_feed_2(struct dvb_demux_feed *feed)
{
return altera_ci_start_feed(feed, 2);
}
-int altera_ci_stop_feed_2(struct dvb_demux_feed *feed)
+static int altera_ci_stop_feed_2(struct dvb_demux_feed *feed)
{
return altera_ci_stop_feed(feed, 2);
}
-int altera_hw_filt_init(struct altera_ci_config *config, int hw_filt_nr)
+static int altera_hw_filt_init(struct altera_ci_config *config, int hw_filt_nr)
{
struct netup_hw_pid_filter *pid_filt = NULL;
struct fpga_inode *temp_int = find_inode(config->dev);
diff --git a/drivers/media/pci/cx23885/cimax2.c b/drivers/media/pci/cx23885/cimax2.c
index 6617774a326..7344849183a 100644
--- a/drivers/media/pci/cx23885/cimax2.c
+++ b/drivers/media/pci/cx23885/cimax2.c
@@ -24,6 +24,7 @@
*/
#include "cx23885.h"
+#include "cimax2.h"
#include "dvb_ca_en50221.h"
/**** Bit definitions for MC417_RWD and MC417_OEN registers ***
bits 31-16
@@ -87,7 +88,7 @@ struct netup_ci_state {
};
-int netup_read_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg,
+static int netup_read_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg,
u8 *buf, int len)
{
int ret;
@@ -120,7 +121,7 @@ int netup_read_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg,
return 0;
}
-int netup_write_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg,
+static int netup_write_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg,
u8 *buf, int len)
{
int ret;
@@ -147,7 +148,7 @@ int netup_write_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg,
return 0;
}
-int netup_ci_get_mem(struct cx23885_dev *dev)
+static int netup_ci_get_mem(struct cx23885_dev *dev)
{
int mem;
unsigned long timeout = jiffies + msecs_to_jiffies(1);
@@ -166,7 +167,7 @@ int netup_ci_get_mem(struct cx23885_dev *dev)
return mem & 0xff;
}
-int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
+static int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
u8 flag, u8 read, int addr, u8 data)
{
struct netup_ci_state *state = en50221->data;
@@ -248,7 +249,8 @@ int netup_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
return netup_ci_op_cam(en50221, slot, 0, 0, addr, data);
}
-int netup_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr)
+int netup_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot,
+ u8 addr)
{
return netup_ci_op_cam(en50221, slot, NETUP_CI_CTL,
NETUP_CI_RD, addr, 0);
@@ -295,7 +297,7 @@ int netup_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot)
return 0;
}
-int netup_ci_set_irq(struct dvb_ca_en50221 *en50221, u8 irq_mode)
+static int netup_ci_set_irq(struct dvb_ca_en50221 *en50221, u8 irq_mode)
{
struct netup_ci_state *state = en50221->data;
int ret;
@@ -399,7 +401,8 @@ int netup_ci_slot_status(struct cx23885_dev *dev, u32 pci_status)
return 1;
}
-int netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, int slot, int open)
+int netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221,
+ int slot, int open)
{
struct netup_ci_state *state = en50221->data;
diff --git a/drivers/media/pci/cx23885/cx23885-alsa.c b/drivers/media/pci/cx23885/cx23885-alsa.c
index 795169237e7..c6c9bd58f8b 100644
--- a/drivers/media/pci/cx23885/cx23885-alsa.c
+++ b/drivers/media/pci/cx23885/cx23885-alsa.c
@@ -45,8 +45,10 @@
#define AUDIO_SRAM_CHANNEL SRAM_CH07
-#define dprintk(level, fmt, arg...) if (audio_debug >= level) \
- printk(KERN_INFO "%s: " fmt, chip->dev->name , ## arg)
+#define dprintk(level, fmt, arg...) do { \
+ if (audio_debug + 1 > level) \
+ printk(KERN_INFO "%s: " fmt, chip->dev->name , ## arg); \
+} while(0)
#define dprintk_core(level, fmt, arg...) if (audio_debug >= level) \
printk(KERN_DEBUG "%s: " fmt, chip->dev->name , ## arg)
diff --git a/drivers/media/pci/cx23885/cx23885-av.c b/drivers/media/pci/cx23885/cx23885-av.c
index 134ebddd860..e958a01fd55 100644
--- a/drivers/media/pci/cx23885/cx23885-av.c
+++ b/drivers/media/pci/cx23885/cx23885-av.c
@@ -22,6 +22,7 @@
*/
#include "cx23885.h"
+#include "cx23885-av.h"
void cx23885_av_work_handler(struct work_struct *work)
{
diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c
index 5acdf954ff6..6277e145f0b 100644
--- a/drivers/media/pci/cx23885/cx23885-cards.c
+++ b/drivers/media/pci/cx23885/cx23885-cards.c
@@ -1427,7 +1427,7 @@ void cx23885_ir_fini(struct cx23885_dev *dev)
}
}
-int netup_jtag_io(void *device, int tms, int tdi, int read_tdo)
+static int netup_jtag_io(void *device, int tms, int tdi, int read_tdo)
{
int data;
int tdo = 0;
diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c
index 697728f0943..065ecd54bda 100644
--- a/drivers/media/pci/cx23885/cx23885-core.c
+++ b/drivers/media/pci/cx23885/cx23885-core.c
@@ -303,7 +303,7 @@ static struct sram_channel cx23887_sram_channels[] = {
},
};
-void cx23885_irq_add(struct cx23885_dev *dev, u32 mask)
+static void cx23885_irq_add(struct cx23885_dev *dev, u32 mask)
{
unsigned long flags;
spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
@@ -1516,8 +1516,7 @@ int cx23885_restart_queue(struct cx23885_tsport *port,
buf = list_entry(q->queued.next, struct cx23885_buffer,
vb.queue);
if (NULL == prev) {
- list_del(&buf->vb.queue);
- list_add_tail(&buf->vb.queue, &q->active);
+ list_move_tail(&buf->vb.queue, &q->active);
cx23885_start_dma(port, q, buf);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
@@ -1528,8 +1527,7 @@ int cx23885_restart_queue(struct cx23885_tsport *port,
} else if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
- list_del(&buf->vb.queue);
- list_add_tail(&buf->vb.queue, &q->active);
+ list_move_tail(&buf->vb.queue, &q->active);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c
index 4379d8a6dad..2f5b902e63a 100644
--- a/drivers/media/pci/cx23885/cx23885-dvb.c
+++ b/drivers/media/pci/cx23885/cx23885-dvb.c
@@ -659,7 +659,7 @@ static struct mt2063_config terratec_mt2063_config[] = {
},
};
-int netup_altera_fpga_rw(void *device, int flag, int data, int read)
+static int netup_altera_fpga_rw(void *device, int flag, int data, int read)
{
struct cx23885_dev *dev = (struct cx23885_dev *)device;
unsigned long timeout = jiffies + msecs_to_jiffies(1);
diff --git a/drivers/media/pci/cx23885/cx23885-f300.c b/drivers/media/pci/cx23885/cx23885-f300.c
index 93998f22098..5444cc52600 100644
--- a/drivers/media/pci/cx23885/cx23885-f300.c
+++ b/drivers/media/pci/cx23885/cx23885-f300.c
@@ -29,6 +29,7 @@
*/
#include "cx23885.h"
+#include "cx23885-f300.h"
#define F300_DATA GPIO_0
#define F300_RESET GPIO_1
diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c
index 2c925f77cf2..4f1055a194b 100644
--- a/drivers/media/pci/cx23885/cx23885-input.c
+++ b/drivers/media/pci/cx23885/cx23885-input.c
@@ -40,6 +40,7 @@
#include <media/v4l2-subdev.h>
#include "cx23885.h"
+#include "cx23885-input.h"
#define MODULE_NAME "cx23885"
@@ -270,21 +271,21 @@ int cx23885_input_init(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_HVR1250:
/* Integrated CX2388[58] IR controller */
driver_type = RC_DRIVER_IR_RAW;
- allowed_protos = RC_TYPE_ALL;
+ allowed_protos = RC_BIT_ALL;
/* The grey Hauppauge RC-5 remote */
rc_map = RC_MAP_HAUPPAUGE;
break;
case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
/* Integrated CX23885 IR controller */
driver_type = RC_DRIVER_IR_RAW;
- allowed_protos = RC_TYPE_NEC;
+ allowed_protos = RC_BIT_NEC;
/* The grey Terratec remote with orange buttons */
rc_map = RC_MAP_NEC_TERRATEC_CINERGY_XS;
break;
case CX23885_BOARD_TEVII_S470:
/* Integrated CX23885 IR controller */
driver_type = RC_DRIVER_IR_RAW;
- allowed_protos = RC_TYPE_ALL;
+ allowed_protos = RC_BIT_ALL;
/* A guess at the remote */
rc_map = RC_MAP_TEVII_NEC;
break;
diff --git a/drivers/media/pci/cx23885/cx23885-input.h b/drivers/media/pci/cx23885/cx23885-input.h
index 75ef15d3f52..87dc44e6997 100644
--- a/drivers/media/pci/cx23885/cx23885-input.h
+++ b/drivers/media/pci/cx23885/cx23885-input.h
@@ -23,7 +23,7 @@
#ifndef _CX23885_INPUT_H_
#define _CX23885_INPUT_H_
-int cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events);
+void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events);
int cx23885_input_init(struct cx23885_dev *dev);
void cx23885_input_fini(struct cx23885_dev *dev);
diff --git a/drivers/media/pci/cx23885/cx23885-ioctl.c b/drivers/media/pci/cx23885/cx23885-ioctl.c
index 44812ca7889..ea9a614f3bb 100644
--- a/drivers/media/pci/cx23885/cx23885-ioctl.c
+++ b/drivers/media/pci/cx23885/cx23885-ioctl.c
@@ -22,6 +22,8 @@
*/
#include "cx23885.h"
+#include "cx23885-ioctl.h"
+
#include <media/v4l2-chip-ident.h>
int cx23885_g_chip_ident(struct file *file, void *fh,
diff --git a/drivers/media/pci/cx23885/cx23885-ir.c b/drivers/media/pci/cx23885/cx23885-ir.c
index 7125247dd25..bfef1935929 100644
--- a/drivers/media/pci/cx23885/cx23885-ir.c
+++ b/drivers/media/pci/cx23885/cx23885-ir.c
@@ -24,6 +24,7 @@
#include <media/v4l2-device.h>
#include "cx23885.h"
+#include "cx23885-ir.h"
#include "cx23885-input.h"
#define CX23885_IR_RX_FIFO_SERVICE_REQ 0
diff --git a/drivers/media/pci/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c
index c2bc39c58f8..c4bd1e95d33 100644
--- a/drivers/media/pci/cx23885/cx23888-ir.c
+++ b/drivers/media/pci/cx23885/cx23888-ir.c
@@ -29,6 +29,7 @@
#include <media/rc-core.h>
#include "cx23885.h"
+#include "cx23888-ir.h"
static unsigned int ir_888_debug;
module_param(ir_888_debug, int, 0644);
diff --git a/drivers/media/pci/cx23885/netup-init.c b/drivers/media/pci/cx23885/netup-init.c
index f4893e69cd8..0044fef7ca2 100644
--- a/drivers/media/pci/cx23885/netup-init.c
+++ b/drivers/media/pci/cx23885/netup-init.c
@@ -24,6 +24,7 @@
*/
#include "cx23885.h"
+#include "netup-init.h"
static void i2c_av_write(struct i2c_adapter *i2c, u16 reg, u8 val)
{
diff --git a/drivers/media/pci/cx25821/cx25821-audio-upstream.c b/drivers/media/pci/cx25821/cx25821-audio-upstream.c
index 8b2a99975c2..87491ca05ee 100644
--- a/drivers/media/pci/cx25821/cx25821-audio-upstream.c
+++ b/drivers/media/pci/cx25821/cx25821-audio-upstream.c
@@ -44,7 +44,7 @@ MODULE_LICENSE("GPL");
static int _intr_msk = FLD_AUD_SRC_RISCI1 | FLD_AUD_SRC_OF |
FLD_AUD_SRC_SYNC | FLD_AUD_SRC_OPC_ERR;
-int cx25821_sram_channel_setup_upstream_audio(struct cx25821_dev *dev,
+static int cx25821_sram_channel_setup_upstream_audio(struct cx25821_dev *dev,
struct sram_channel *ch,
unsigned int bpl, u32 risc)
{
@@ -133,7 +133,7 @@ static __le32 *cx25821_risc_field_upstream_audio(struct cx25821_dev *dev,
return rp;
}
-int cx25821_risc_buffer_upstream_audio(struct cx25821_dev *dev,
+static int cx25821_risc_buffer_upstream_audio(struct cx25821_dev *dev,
struct pci_dev *pci,
unsigned int bpl, unsigned int lines)
{
@@ -197,7 +197,7 @@ int cx25821_risc_buffer_upstream_audio(struct cx25821_dev *dev,
return 0;
}
-void cx25821_free_memory_audio(struct cx25821_dev *dev)
+static void cx25821_free_memory_audio(struct cx25821_dev *dev)
{
if (dev->_risc_virt_addr) {
pci_free_consistent(dev->pci, dev->_audiorisc_size,
@@ -256,7 +256,7 @@ void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev)
cx25821_free_memory_audio(dev);
}
-int cx25821_get_audio_data(struct cx25821_dev *dev,
+static int cx25821_get_audio_data(struct cx25821_dev *dev,
struct sram_channel *sram_ch)
{
struct file *myfile;
@@ -351,7 +351,7 @@ static void cx25821_audioups_handler(struct work_struct *work)
sram_channels);
}
-int cx25821_openfile_audio(struct cx25821_dev *dev,
+static int cx25821_openfile_audio(struct cx25821_dev *dev,
struct sram_channel *sram_ch)
{
struct file *myfile;
@@ -490,7 +490,7 @@ error:
return ret;
}
-int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num,
+static int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num,
u32 status)
{
int i = 0;
@@ -634,8 +634,8 @@ static void cx25821_wait_fifo_enable(struct cx25821_dev *dev,
}
-int cx25821_start_audio_dma_upstream(struct cx25821_dev *dev,
- struct sram_channel *sram_ch)
+static int cx25821_start_audio_dma_upstream(struct cx25821_dev *dev,
+ struct sram_channel *sram_ch)
{
u32 tmp = 0;
int err = 0;
@@ -700,9 +700,7 @@ fail_irq:
int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select)
{
struct sram_channel *sram_ch;
- int retval = 0;
int err = 0;
- int str_length = 0;
if (dev->_audio_is_running) {
pr_warn("Audio Channel is still running so return!\n");
@@ -731,27 +729,29 @@ int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select)
_line_size = AUDIO_LINE_SIZE;
if (dev->input_audiofilename) {
- str_length = strlen(dev->input_audiofilename);
- dev->_audiofilename = kmemdup(dev->input_audiofilename,
- str_length + 1, GFP_KERNEL);
+ dev->_audiofilename = kstrdup(dev->input_audiofilename,
+ GFP_KERNEL);
- if (!dev->_audiofilename)
+ if (!dev->_audiofilename) {
+ err = -ENOMEM;
goto error;
+ }
/* Default if filename is empty string */
if (strcmp(dev->input_audiofilename, "") == 0)
dev->_audiofilename = "/root/audioGOOD.wav";
} else {
- str_length = strlen(_defaultAudioName);
- dev->_audiofilename = kmemdup(_defaultAudioName,
- str_length + 1, GFP_KERNEL);
+ dev->_audiofilename = kstrdup(_defaultAudioName,
+ GFP_KERNEL);
- if (!dev->_audiofilename)
+ if (!dev->_audiofilename) {
+ err = -ENOMEM;
goto error;
+ }
}
- retval = cx25821_sram_channel_setup_upstream_audio(dev, sram_ch,
- _line_size, 0);
+ cx25821_sram_channel_setup_upstream_audio(dev, sram_ch,
+ _line_size, 0);
dev->audio_upstream_riscbuf_size =
AUDIO_RISC_DMA_BUF_SIZE * NUM_AUDIO_PROGS +
@@ -759,9 +759,9 @@ int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select)
dev->audio_upstream_databuf_size = AUDIO_DATA_BUF_SZ * NUM_AUDIO_PROGS;
/* Allocating buffers and prepare RISC program */
- retval = cx25821_audio_upstream_buffer_prepare(dev, sram_ch,
+ err = cx25821_audio_upstream_buffer_prepare(dev, sram_ch,
_line_size);
- if (retval < 0) {
+ if (err < 0) {
pr_err("%s: Failed to set up Audio upstream buffers!\n",
dev->name);
goto error;
diff --git a/drivers/media/pci/cx25821/cx25821-biffuncs.h b/drivers/media/pci/cx25821/cx25821-biffuncs.h
index 9326a7c729e..937f5a70fb7 100644
--- a/drivers/media/pci/cx25821/cx25821-biffuncs.h
+++ b/drivers/media/pci/cx25821/cx25821-biffuncs.h
@@ -25,17 +25,17 @@
#define SetBit(Bit) (1 << Bit)
-inline u8 getBit(u32 sample, u8 index)
+static inline u8 getBit(u32 sample, u8 index)
{
return (u8) ((sample >> index) & 1);
}
-inline u32 clearBitAtPos(u32 value, u8 bit)
+static inline u32 clearBitAtPos(u32 value, u8 bit)
{
return value & ~(1 << bit);
}
-inline u32 setBitAtPos(u32 sample, u8 bit)
+static inline u32 setBitAtPos(u32 sample, u8 bit)
{
sample |= (1 << bit);
return sample;
diff --git a/drivers/media/pci/cx25821/cx25821-i2c.c b/drivers/media/pci/cx25821/cx25821-i2c.c
index 9844549764c..a8dc945bbe1 100644
--- a/drivers/media/pci/cx25821/cx25821-i2c.c
+++ b/drivers/media/pci/cx25821/cx25821-i2c.c
@@ -329,7 +329,8 @@ int cx25821_i2c_unregister(struct cx25821_i2c *bus)
return 0;
}
-void cx25821_av_clk(struct cx25821_dev *dev, int enable)
+#if 0 /* Currently unused */
+static void cx25821_av_clk(struct cx25821_dev *dev, int enable)
{
/* write 0 to bus 2 addr 0x144 via i2x_xfer() */
char buffer[3];
@@ -351,6 +352,7 @@ void cx25821_av_clk(struct cx25821_dev *dev, int enable)
i2c_xfer(&dev->i2c_bus[0].i2c_adap, &msg, 1);
}
+#endif
int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value)
{
diff --git a/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.c b/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.c
index d33fc1a2303..cf2723c7197 100644
--- a/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.c
+++ b/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.c
@@ -123,10 +123,11 @@ static __le32 *cx25821_risc_field_upstream_ch2(struct cx25821_dev *dev,
return rp;
}
-int cx25821_risc_buffer_upstream_ch2(struct cx25821_dev *dev,
- struct pci_dev *pci,
- unsigned int top_offset, unsigned int bpl,
- unsigned int lines)
+static int cx25821_risc_buffer_upstream_ch2(struct cx25821_dev *dev,
+ struct pci_dev *pci,
+ unsigned int top_offset,
+ unsigned int bpl,
+ unsigned int lines)
{
__le32 *rp;
int fifo_enable = 0;
@@ -255,7 +256,8 @@ void cx25821_free_mem_upstream_ch2(struct cx25821_dev *dev)
}
}
-int cx25821_get_frame_ch2(struct cx25821_dev *dev, struct sram_channel *sram_ch)
+static int cx25821_get_frame_ch2(struct cx25821_dev *dev,
+ struct sram_channel *sram_ch)
{
struct file *myfile;
int frame_index_temp = dev->_frame_index_ch2;
@@ -360,7 +362,8 @@ static void cx25821_vidups_handler_ch2(struct work_struct *work)
_channel2_upstream_select].sram_channels);
}
-int cx25821_openfile_ch2(struct cx25821_dev *dev, struct sram_channel *sram_ch)
+static int cx25821_openfile_ch2(struct cx25821_dev *dev,
+ struct sram_channel *sram_ch)
{
struct file *myfile;
int i = 0, j = 0;
@@ -507,8 +510,9 @@ error:
return ret;
}
-int cx25821_video_upstream_irq_ch2(struct cx25821_dev *dev, int chan_num,
- u32 status)
+static int cx25821_video_upstream_irq_ch2(struct cx25821_dev *dev,
+ int chan_num,
+ u32 status)
{
u32 int_msk_tmp;
struct sram_channel *channel = dev->channels[chan_num].sram_channels;
@@ -647,8 +651,8 @@ static void cx25821_set_pixelengine_ch2(struct cx25821_dev *dev,
cx_write(ch->vid_cdt_size, VID_CDT_SIZE >> 3);
}
-int cx25821_start_video_dma_upstream_ch2(struct cx25821_dev *dev,
- struct sram_channel *sram_ch)
+static int cx25821_start_video_dma_upstream_ch2(struct cx25821_dev *dev,
+ struct sram_channel *sram_ch)
{
u32 tmp = 0;
int err = 0;
@@ -704,11 +708,9 @@ int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, int channel_select,
{
struct sram_channel *sram_ch;
u32 tmp;
- int retval = 0;
int err = 0;
int data_frame_size = 0;
int risc_buffer_size = 0;
- int str_length = 0;
if (dev->_is_running_ch2) {
pr_info("Video Channel is still running so return!\n");
@@ -744,20 +746,16 @@ int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, int channel_select,
risc_buffer_size = dev->_isNTSC_ch2 ?
NTSC_RISC_BUF_SIZE : PAL_RISC_BUF_SIZE;
- if (dev->input_filename_ch2) {
- str_length = strlen(dev->input_filename_ch2);
- dev->_filename_ch2 = kmemdup(dev->input_filename_ch2,
- str_length + 1, GFP_KERNEL);
-
- if (!dev->_filename_ch2)
- goto error;
- } else {
- str_length = strlen(dev->_defaultname_ch2);
- dev->_filename_ch2 = kmemdup(dev->_defaultname_ch2,
- str_length + 1, GFP_KERNEL);
+ if (dev->input_filename_ch2)
+ dev->_filename_ch2 = kstrdup(dev->input_filename_ch2,
+ GFP_KERNEL);
+ else
+ dev->_filename_ch2 = kstrdup(dev->_defaultname_ch2,
+ GFP_KERNEL);
- if (!dev->_filename_ch2)
- goto error;
+ if (!dev->_filename_ch2) {
+ err = -ENOENT;
+ goto error;
}
/* Default if filename is empty string */
@@ -773,7 +771,7 @@ int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, int channel_select,
}
}
- retval = cx25821_sram_channel_setup_upstream(dev, sram_ch,
+ err = cx25821_sram_channel_setup_upstream(dev, sram_ch,
dev->_line_size_ch2, 0);
/* setup fifo + format */
@@ -783,9 +781,9 @@ int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, int channel_select,
dev->upstream_databuf_size_ch2 = data_frame_size * 2;
/* Allocating buffers and prepare RISC program */
- retval = cx25821_upstream_buffer_prepare_ch2(dev, sram_ch,
+ err = cx25821_upstream_buffer_prepare_ch2(dev, sram_ch,
dev->_line_size_ch2);
- if (retval < 0) {
+ if (err < 0) {
pr_err("%s: Failed to set up Video upstream buffers!\n",
dev->name);
goto error;
diff --git a/drivers/media/pci/cx25821/cx25821-video-upstream.c b/drivers/media/pci/cx25821/cx25821-video-upstream.c
index 6759fff8eb6..7fc97110d97 100644
--- a/drivers/media/pci/cx25821/cx25821-video-upstream.c
+++ b/drivers/media/pci/cx25821/cx25821-video-upstream.c
@@ -173,10 +173,10 @@ static __le32 *cx25821_risc_field_upstream(struct cx25821_dev *dev, __le32 * rp,
return rp;
}
-int cx25821_risc_buffer_upstream(struct cx25821_dev *dev,
- struct pci_dev *pci,
- unsigned int top_offset,
- unsigned int bpl, unsigned int lines)
+static int cx25821_risc_buffer_upstream(struct cx25821_dev *dev,
+ struct pci_dev *pci,
+ unsigned int top_offset,
+ unsigned int bpl, unsigned int lines)
{
__le32 *rp;
int fifo_enable = 0;
@@ -300,7 +300,8 @@ void cx25821_free_mem_upstream_ch1(struct cx25821_dev *dev)
}
}
-int cx25821_get_frame(struct cx25821_dev *dev, struct sram_channel *sram_ch)
+static int cx25821_get_frame(struct cx25821_dev *dev,
+ struct sram_channel *sram_ch)
{
struct file *myfile;
int frame_index_temp = dev->_frame_index;
@@ -405,7 +406,8 @@ static void cx25821_vidups_handler(struct work_struct *work)
sram_channels);
}
-int cx25821_openfile(struct cx25821_dev *dev, struct sram_channel *sram_ch)
+static int cx25821_openfile(struct cx25821_dev *dev,
+ struct sram_channel *sram_ch)
{
struct file *myfile;
int i = 0, j = 0;
@@ -486,8 +488,9 @@ int cx25821_openfile(struct cx25821_dev *dev, struct sram_channel *sram_ch)
return 0;
}
-int cx25821_upstream_buffer_prepare(struct cx25821_dev *dev,
- struct sram_channel *sram_ch, int bpl)
+static int cx25821_upstream_buffer_prepare(struct cx25821_dev *dev,
+ struct sram_channel *sram_ch,
+ int bpl)
{
int ret = 0;
dma_addr_t dma_addr;
@@ -548,8 +551,8 @@ error:
return ret;
}
-int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num,
- u32 status)
+static int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num,
+ u32 status)
{
u32 int_msk_tmp;
struct sram_channel *channel = dev->channels[chan_num].sram_channels;
@@ -664,8 +667,9 @@ static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id)
return IRQ_RETVAL(handled);
}
-void cx25821_set_pixelengine(struct cx25821_dev *dev, struct sram_channel *ch,
- int pix_format)
+static void cx25821_set_pixelengine(struct cx25821_dev *dev,
+ struct sram_channel *ch,
+ int pix_format)
{
int width = WIDTH_D1;
int height = dev->_lines_count;
@@ -696,8 +700,8 @@ void cx25821_set_pixelengine(struct cx25821_dev *dev, struct sram_channel *ch,
cx_write(ch->vid_cdt_size, VID_CDT_SIZE >> 3);
}
-int cx25821_start_video_dma_upstream(struct cx25821_dev *dev,
- struct sram_channel *sram_ch)
+static int cx25821_start_video_dma_upstream(struct cx25821_dev *dev,
+ struct sram_channel *sram_ch)
{
u32 tmp = 0;
int err = 0;
@@ -753,7 +757,6 @@ int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, int channel_select,
{
struct sram_channel *sram_ch;
u32 tmp;
- int retval = 0;
int err = 0;
int data_frame_size = 0;
int risc_buffer_size = 0;
@@ -796,15 +799,19 @@ int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, int channel_select,
dev->_filename = kmemdup(dev->input_filename, str_length + 1,
GFP_KERNEL);
- if (!dev->_filename)
+ if (!dev->_filename) {
+ err = -ENOENT;
goto error;
+ }
} else {
str_length = strlen(dev->_defaultname);
dev->_filename = kmemdup(dev->_defaultname, str_length + 1,
GFP_KERNEL);
- if (!dev->_filename)
+ if (!dev->_filename) {
+ err = -ENOENT;
goto error;
+ }
}
/* Default if filename is empty string */
@@ -828,7 +835,7 @@ int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, int channel_select,
dev->_line_size = (dev->_pixel_format == PIXEL_FRMT_422) ?
(WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2;
- retval = cx25821_sram_channel_setup_upstream(dev, sram_ch,
+ err = cx25821_sram_channel_setup_upstream(dev, sram_ch,
dev->_line_size, 0);
/* setup fifo + format */
@@ -838,8 +845,8 @@ int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, int channel_select,
dev->upstream_databuf_size = data_frame_size * 2;
/* Allocating buffers and prepare RISC program */
- retval = cx25821_upstream_buffer_prepare(dev, sram_ch, dev->_line_size);
- if (retval < 0) {
+ err = cx25821_upstream_buffer_prepare(dev, sram_ch, dev->_line_size);
+ if (err < 0) {
pr_err("%s: Failed to set up Video upstream buffers!\n",
dev->name);
goto error;
diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c
index 0a80245165d..53b16dd7032 100644
--- a/drivers/media/pci/cx25821/cx25821-video.c
+++ b/drivers/media/pci/cx25821/cx25821-video.c
@@ -291,9 +291,9 @@ int cx25821_start_video_dma(struct cx25821_dev *dev,
return 0;
}
-int cx25821_restart_video_queue(struct cx25821_dev *dev,
- struct cx25821_dmaqueue *q,
- struct sram_channel *channel)
+static int cx25821_restart_video_queue(struct cx25821_dev *dev,
+ struct cx25821_dmaqueue *q,
+ struct sram_channel *channel)
{
struct cx25821_buffer *buf, *prev;
struct list_head *item;
@@ -342,7 +342,7 @@ int cx25821_restart_video_queue(struct cx25821_dev *dev,
}
}
-void cx25821_vid_timeout(unsigned long data)
+static void cx25821_vid_timeout(unsigned long data)
{
struct cx25821_data *timeout_data = (struct cx25821_data *)data;
struct cx25821_dev *dev = timeout_data->dev;
diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c
index 3aa6856ead3..d2de1a913e1 100644
--- a/drivers/media/pci/cx88/cx88-alsa.c
+++ b/drivers/media/pci/cx88/cx88-alsa.c
@@ -45,11 +45,15 @@
#include "cx88.h"
#include "cx88-reg.h"
-#define dprintk(level,fmt, arg...) if (debug >= level) \
- printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg)
-
-#define dprintk_core(level,fmt, arg...) if (debug >= level) \
- printk(KERN_DEBUG "%s/1: " fmt, chip->core->name , ## arg)
+#define dprintk(level, fmt, arg...) do { \
+ if (debug + 1 > level) \
+ printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg);\
+} while(0)
+
+#define dprintk_core(level, fmt, arg...) do { \
+ if (debug + 1 > level) \
+ printk(KERN_DEBUG "%s/1: " fmt, chip->core->name , ## arg);\
+} while(0)
/****************************************************************************
Data type declarations - Can be moded to a header file later
diff --git a/drivers/media/pci/cx88/cx88-blackbird.c b/drivers/media/pci/cx88/cx88-blackbird.c
index 62184eb919e..a6ff8a6f4fc 100644
--- a/drivers/media/pci/cx88/cx88-blackbird.c
+++ b/drivers/media/pci/cx88/cx88-blackbird.c
@@ -53,9 +53,10 @@ static unsigned int debug;
module_param(debug,int,0644);
MODULE_PARM_DESC(debug,"enable debug messages [blackbird]");
-#define dprintk(level,fmt, arg...) if (debug >= level) \
- printk(KERN_DEBUG "%s/2-bb: " fmt, dev->core->name , ## arg)
-
+#define dprintk(level, fmt, arg...) do { \
+ if (debug + 1 > level) \
+ printk(KERN_DEBUG "%s/2-bb: " fmt, dev->core->name , ## arg); \
+} while(0)
/* ------------------------------------------------------------------ */
diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c
index c97b174be3a..19a58754c6e 100644
--- a/drivers/media/pci/cx88/cx88-core.c
+++ b/drivers/media/pci/cx88/cx88-core.c
@@ -646,22 +646,22 @@ int cx88_reset(struct cx88_core *core)
/* ------------------------------------------------------------------ */
-static unsigned int inline norm_swidth(v4l2_std_id norm)
+static inline unsigned int norm_swidth(v4l2_std_id norm)
{
return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922;
}
-static unsigned int inline norm_hdelay(v4l2_std_id norm)
+static inline unsigned int norm_hdelay(v4l2_std_id norm)
{
return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 135 : 186;
}
-static unsigned int inline norm_vdelay(v4l2_std_id norm)
+static inline unsigned int norm_vdelay(v4l2_std_id norm)
{
return (norm & V4L2_STD_625_50) ? 0x24 : 0x18;
}
-static unsigned int inline norm_fsc8(v4l2_std_id norm)
+static inline unsigned int norm_fsc8(v4l2_std_id norm)
{
if (norm & V4L2_STD_PAL_M)
return 28604892; // 3.575611 MHz
@@ -681,7 +681,7 @@ static unsigned int inline norm_fsc8(v4l2_std_id norm)
return 35468950; // 4.43361875 MHz +/- 5 Hz
}
-static unsigned int inline norm_htotal(v4l2_std_id norm)
+static inline unsigned int norm_htotal(v4l2_std_id norm)
{
unsigned int fsc4=norm_fsc8(norm)/2;
@@ -692,7 +692,7 @@ static unsigned int inline norm_htotal(v4l2_std_id norm)
((fsc4+262)/525*1001+15000)/30000;
}
-static unsigned int inline norm_vbipack(v4l2_std_id norm)
+static inline unsigned int norm_vbipack(v4l2_std_id norm)
{
return (norm & V4L2_STD_625_50) ? 511 : 400;
}
diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c
index ebf448c48ca..f29e18c72f4 100644
--- a/drivers/media/pci/cx88/cx88-input.c
+++ b/drivers/media/pci/cx88/cx88-input.c
@@ -248,7 +248,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
struct cx88_IR *ir;
struct rc_dev *dev;
char *ir_codes = NULL;
- u64 rc_type = RC_TYPE_OTHER;
+ u64 rc_type = RC_BIT_OTHER;
int err = -ENOMEM;
u32 hardware_mask = 0; /* For devices with a hardware mask, when
* used with a full-code IR table
@@ -416,7 +416,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
break;
case CX88_BOARD_TWINHAN_VP1027_DVBS:
ir_codes = RC_MAP_TWINHAN_VP1027_DVBS;
- rc_type = RC_TYPE_NEC;
+ rc_type = RC_BIT_NEC;
ir->sampling = 0xff00; /* address */
break;
}
@@ -592,7 +592,7 @@ void cx88_i2c_init_ir(struct cx88_core *core)
case CX88_BOARD_LEADTEK_PVR2000:
addr_list = pvr2000_addr_list;
core->init_data.name = "cx88 Leadtek PVR 2000 remote";
- core->init_data.type = RC_TYPE_UNKNOWN;
+ core->init_data.type = RC_BIT_UNKNOWN;
core->init_data.get_key = get_key_pvr2000;
core->init_data.ir_codes = RC_MAP_EMPTY;
break;
@@ -613,7 +613,7 @@ void cx88_i2c_init_ir(struct cx88_core *core)
/* Hauppauge XVR */
core->init_data.name = "cx88 Hauppauge XVR remote";
core->init_data.ir_codes = RC_MAP_HAUPPAUGE;
- core->init_data.type = RC_TYPE_RC5;
+ core->init_data.type = RC_BIT_RC5;
core->init_data.internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
info.platform_data = &core->init_data;
diff --git a/drivers/media/pci/cx88/cx88-mpeg.c b/drivers/media/pci/cx88/cx88-mpeg.c
index d154bc19735..d46b008a46b 100644
--- a/drivers/media/pci/cx88/cx88-mpeg.c
+++ b/drivers/media/pci/cx88/cx88-mpeg.c
@@ -45,11 +45,15 @@ static unsigned int debug;
module_param(debug,int,0644);
MODULE_PARM_DESC(debug,"enable debug messages [mpeg]");
-#define dprintk(level,fmt, arg...) if (debug >= level) \
- printk(KERN_DEBUG "%s/2-mpeg: " fmt, dev->core->name, ## arg)
+#define dprintk(level, fmt, arg...) do { \
+ if (debug + 1 > level) \
+ printk(KERN_DEBUG "%s/2-mpeg: " fmt, dev->core->name, ## arg); \
+} while(0)
-#define mpeg_dbg(level,fmt, arg...) if (debug >= level) \
- printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg)
+#define mpeg_dbg(level, fmt, arg...) do { \
+ if (debug + 1 > level) \
+ printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg); \
+} while(0)
#if defined(CONFIG_MODULES) && defined(MODULE)
static void request_module_async(struct work_struct *work)
@@ -217,8 +221,7 @@ static int cx8802_restart_queue(struct cx8802_dev *dev,
return 0;
buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue);
if (NULL == prev) {
- list_del(&buf->vb.queue);
- list_add_tail(&buf->vb.queue,&q->active);
+ list_move_tail(&buf->vb.queue, &q->active);
cx8802_start_dma(dev, q, buf);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
@@ -229,8 +232,7 @@ static int cx8802_restart_queue(struct cx8802_dev *dev,
} else if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
- list_del(&buf->vb.queue);
- list_add_tail(&buf->vb.queue,&q->active);
+ list_move_tail(&buf->vb.queue, &q->active);
buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
diff --git a/drivers/media/pci/cx88/cx88.h b/drivers/media/pci/cx88/cx88.h
index 44ffc8b3d45..ba0dba4a4d2 100644
--- a/drivers/media/pci/cx88/cx88.h
+++ b/drivers/media/pci/cx88/cx88.h
@@ -94,13 +94,13 @@ enum cx8802_board_access {
/* ----------------------------------------------------------- */
/* tv norms */
-static unsigned int inline norm_maxw(v4l2_std_id norm)
+static inline unsigned int norm_maxw(v4l2_std_id norm)
{
return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768;
}
-static unsigned int inline norm_maxh(v4l2_std_id norm)
+static inline unsigned int norm_maxh(v4l2_std_id norm)
{
return (norm & V4L2_STD_625_50) ? 576 : 480;
}
diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c
index a609b3a9b14..f288ffcc4b6 100644
--- a/drivers/media/pci/dm1105/dm1105.c
+++ b/drivers/media/pci/dm1105/dm1105.c
@@ -736,7 +736,7 @@ static irqreturn_t dm1105_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
-int __devinit dm1105_ir_init(struct dm1105_dev *dm1105)
+static int __devinit dm1105_ir_init(struct dm1105_dev *dm1105)
{
struct rc_dev *dev;
int err = -ENOMEM;
@@ -776,7 +776,7 @@ int __devinit dm1105_ir_init(struct dm1105_dev *dm1105)
return 0;
}
-void __devexit dm1105_ir_exit(struct dm1105_dev *dm1105)
+static void __devexit dm1105_ir_exit(struct dm1105_dev *dm1105)
{
rc_unregister_device(dm1105->ir.dev);
}
@@ -1128,8 +1128,10 @@ static int __devinit dm1105_probe(struct pci_dev *pdev,
INIT_WORK(&dev->work, dm1105_dmx_buffer);
sprintf(dev->wqn, "%s/%d", dvb_adapter->name, dvb_adapter->num);
dev->wq = create_singlethread_workqueue(dev->wqn);
- if (!dev->wq)
+ if (!dev->wq) {
+ ret = -ENOMEM;
goto err_dvb_net;
+ }
ret = request_irq(pdev->irq, dm1105_irq, IRQF_SHARED,
DRIVER_NAME, dev);
diff --git a/drivers/media/pci/ivtv/ivtv-alsa-main.c b/drivers/media/pci/ivtv/ivtv-alsa-main.c
index 8deab1629b3..4a221c69399 100644
--- a/drivers/media/pci/ivtv/ivtv-alsa-main.c
+++ b/drivers/media/pci/ivtv/ivtv-alsa-main.c
@@ -205,7 +205,7 @@ err_exit:
return ret;
}
-int ivtv_alsa_load(struct ivtv *itv)
+static int __init ivtv_alsa_load(struct ivtv *itv)
{
struct v4l2_device *v4l2_dev = &itv->v4l2_dev;
struct ivtv_stream *s;
diff --git a/drivers/media/pci/ivtv/ivtv-alsa-pcm.c b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c
index f7022bd58ff..e1863dbf4ed 100644
--- a/drivers/media/pci/ivtv/ivtv-alsa-pcm.c
+++ b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c
@@ -37,6 +37,7 @@
#include "ivtv-streams.h"
#include "ivtv-fileops.h"
#include "ivtv-alsa.h"
+#include "ivtv-alsa-pcm.h"
static unsigned int pcm_debug;
module_param(pcm_debug, int, 0644);
@@ -69,8 +70,9 @@ static struct snd_pcm_hardware snd_ivtv_hw_capture = {
.periods_max = 98, /* 12544, */
};
-void ivtv_alsa_announce_pcm_data(struct snd_ivtv_card *itvsc, u8 *pcm_data,
- size_t num_bytes)
+static void ivtv_alsa_announce_pcm_data(struct snd_ivtv_card *itvsc,
+ u8 *pcm_data,
+ size_t num_bytes)
{
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
diff --git a/drivers/media/pci/ivtv/ivtv-alsa-pcm.h b/drivers/media/pci/ivtv/ivtv-alsa-pcm.h
index 5ab18319ea4..23dfe0d1240 100644
--- a/drivers/media/pci/ivtv/ivtv-alsa-pcm.h
+++ b/drivers/media/pci/ivtv/ivtv-alsa-pcm.h
@@ -21,7 +21,3 @@
*/
int __init snd_ivtv_pcm_create(struct snd_ivtv_card *itvsc);
-
-/* Used by ivtv driver to announce the PCM data to the module */
-void ivtv_alsa_announce_pcm_data(struct snd_ivtv_card *card, u8 *pcm_data,
- size_t num_bytes);
diff --git a/drivers/media/pci/ivtv/ivtv-firmware.c b/drivers/media/pci/ivtv/ivtv-firmware.c
index 6ec7705af55..68387d4369d 100644
--- a/drivers/media/pci/ivtv/ivtv-firmware.c
+++ b/drivers/media/pci/ivtv/ivtv-firmware.c
@@ -276,7 +276,7 @@ void ivtv_init_mpeg_decoder(struct ivtv *itv)
}
/* Try to restart the card & restore previous settings */
-int ivtv_firmware_restart(struct ivtv *itv)
+static int ivtv_firmware_restart(struct ivtv *itv)
{
int rc = 0;
v4l2_std_id std;
diff --git a/drivers/media/pci/ivtv/ivtv-i2c.c b/drivers/media/pci/ivtv/ivtv-i2c.c
index d47f41a0ef6..46e262becb6 100644
--- a/drivers/media/pci/ivtv/ivtv-i2c.c
+++ b/drivers/media/pci/ivtv/ivtv-i2c.c
@@ -200,21 +200,21 @@ static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr)
init_data->ir_codes = RC_MAP_AVERMEDIA_CARDBUS;
init_data->internal_get_key_func =
IR_KBD_GET_KEY_AVERMEDIA_CARDBUS;
- init_data->type = RC_TYPE_OTHER;
+ init_data->type = RC_BIT_OTHER;
init_data->name = "AVerMedia AVerTV card";
break;
case IVTV_HW_I2C_IR_RX_HAUP_EXT:
case IVTV_HW_I2C_IR_RX_HAUP_INT:
init_data->ir_codes = RC_MAP_HAUPPAUGE;
init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP;
- init_data->type = RC_TYPE_RC5;
+ init_data->type = RC_BIT_RC5;
init_data->name = itv->card_name;
break;
case IVTV_HW_Z8F0811_IR_RX_HAUP:
/* Default to grey remote */
init_data->ir_codes = RC_MAP_HAUPPAUGE;
init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
- init_data->type = RC_TYPE_RC5;
+ init_data->type = RC_BIT_RC5;
init_data->name = itv->card_name;
break;
case IVTV_HW_I2C_IR_RX_ADAPTEC:
@@ -222,7 +222,7 @@ static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr)
init_data->name = itv->card_name;
/* FIXME: The protocol and RC_MAP needs to be corrected */
init_data->ir_codes = RC_MAP_EMPTY;
- init_data->type = RC_TYPE_UNKNOWN;
+ init_data->type = RC_BIT_UNKNOWN;
break;
}
diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c
index 949ae230e11..7a8b0d0b612 100644
--- a/drivers/media/pci/ivtv/ivtv-ioctl.c
+++ b/drivers/media/pci/ivtv/ivtv-ioctl.c
@@ -993,7 +993,7 @@ int ivtv_s_input(struct file *file, void *fh, unsigned int inp)
v4l2_std_id std;
int i;
- if (inp < 0 || inp >= itv->nof_inputs)
+ if (inp >= itv->nof_inputs)
return -EINVAL;
if (inp == itv->active_input) {
@@ -1168,7 +1168,7 @@ void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id *std)
}
}
-int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std)
+static int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std)
{
struct ivtv *itv = fh2id(fh)->itv;
diff --git a/drivers/media/pci/mantis/mantis_input.c b/drivers/media/pci/mantis/mantis_input.c
index db6d54d3fec..0e5252e5c0e 100644
--- a/drivers/media/pci/mantis/mantis_input.c
+++ b/drivers/media/pci/mantis/mantis_input.c
@@ -18,6 +18,8 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#if 0 /* Currently unused */
+
#include <media/rc-core.h>
#include <linux/pci.h>
@@ -150,10 +152,11 @@ out:
return err;
}
-int mantis_exit(struct mantis_pci *mantis)
+int mantis_init_exit(struct mantis_pci *mantis)
{
rc_unregister_device(mantis->rc);
rc_map_unregister(&ir_mantis_map);
return 0;
}
+#endif
diff --git a/drivers/media/pci/mantis/mantis_uart.c b/drivers/media/pci/mantis/mantis_uart.c
index 85e977861b4..a7071921863 100644
--- a/drivers/media/pci/mantis/mantis_uart.c
+++ b/drivers/media/pci/mantis/mantis_uart.c
@@ -61,7 +61,7 @@ static struct {
#define UART_MAX_BUF 16
-int mantis_uart_read(struct mantis_pci *mantis, u8 *data)
+static int mantis_uart_read(struct mantis_pci *mantis, u8 *data)
{
struct mantis_hwconfig *config = mantis->hwconfig;
u32 stat = 0, i;
diff --git a/drivers/media/pci/mantis/mantis_vp1033.c b/drivers/media/pci/mantis/mantis_vp1033.c
index ad013e93ed1..115003e8d19 100644
--- a/drivers/media/pci/mantis/mantis_vp1033.c
+++ b/drivers/media/pci/mantis/mantis_vp1033.c
@@ -83,7 +83,7 @@ u8 lgtdqcs001f_inittab[] = {
#define MANTIS_MODEL_NAME "VP-1033"
#define MANTIS_DEV_TYPE "DVB-S/DSS"
-int lgtdqcs001f_tuner_set(struct dvb_frontend *fe)
+static int lgtdqcs001f_tuner_set(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct mantis_pci *mantis = fe->dvb->priv;
@@ -115,8 +115,8 @@ int lgtdqcs001f_tuner_set(struct dvb_frontend *fe)
return 0;
}
-int lgtdqcs001f_set_symbol_rate(struct dvb_frontend *fe,
- u32 srate, u32 ratio)
+static int lgtdqcs001f_set_symbol_rate(struct dvb_frontend *fe,
+ u32 srate, u32 ratio)
{
u8 aclk = 0;
u8 bclk = 0;
diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c
index e5a76da8608..ae7d32027bf 100644
--- a/drivers/media/pci/meye/meye.c
+++ b/drivers/media/pci/meye/meye.c
@@ -1945,7 +1945,7 @@ static struct pci_driver meye_driver = {
static int __init meye_init(void)
{
gbuffers = max(2, min((int)gbuffers, MEYE_MAX_BUFNBRS));
- if (gbufsize < 0 || gbufsize > MEYE_MAX_BUFSIZE)
+ if (gbufsize > MEYE_MAX_BUFSIZE)
gbufsize = MEYE_MAX_BUFSIZE;
gbufsize = PAGE_ALIGN(gbufsize);
printk(KERN_INFO "meye: using %d buffers with %dk (%dk total) "
diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c
index 96a13ed197d..b38bce52956 100644
--- a/drivers/media/pci/ngene/ngene-cards.c
+++ b/drivers/media/pci/ngene/ngene-cards.c
@@ -425,8 +425,10 @@ static int ReadEEProm(struct i2c_adapter *adapter,
status = i2c_read_eeprom(adapter, 0x50, Addr, data, Length);
if (!status) {
*pLength = EETag[2];
+#if 0
if (Length < EETag[2])
- ; /*status=STATUS_BUFFER_OVERFLOW; */
+ status = STATUS_BUFFER_OVERFLOW;
+#endif
}
}
return status;
diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c
index c8e0d5b99d4..8eeec4f50cc 100644
--- a/drivers/media/pci/ngene/ngene-core.c
+++ b/drivers/media/pci/ngene/ngene-core.c
@@ -752,8 +752,8 @@ void set_transfer(struct ngene_channel *chan, int state)
if (chan->mode & NGENE_IO_TSIN)
chan->pBufferExchange = tsin_exchange;
spin_unlock_irq(&chan->state_lock);
- } else
- ;/* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n",
+ }
+ /* else printk(KERN_INFO DEVICE_NAME ": lock=%08x\n",
ngreadl(0x9310)); */
ret = ngene_command_stream_control(dev, chan->number,
@@ -1691,7 +1691,8 @@ int __devinit ngene_probe(struct pci_dev *pci_dev,
dev->i2c_current_bus = -1;
/* Register DVB adapters and devices for both channels */
- if (init_channels(dev) < 0)
+ stat = init_channels(dev);
+ if (stat < 0)
goto fail2;
return 0;
diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c
index f2b37e05b96..8976d0e6581 100644
--- a/drivers/media/pci/saa7134/saa7134-core.c
+++ b/drivers/media/pci/saa7134/saa7134-core.c
@@ -944,8 +944,7 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
/* board config */
dev->board = pci_id->driver_data;
- if (card[dev->nr] >= 0 &&
- card[dev->nr] < saa7134_bcount)
+ if ((unsigned)card[dev->nr] < saa7134_bcount)
dev->board = card[dev->nr];
if (SAA7134_BOARD_UNKNOWN == dev->board)
must_configure_manually(0);
diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c
index 0f78f5e537e..e761262f747 100644
--- a/drivers/media/pci/saa7134/saa7134-input.c
+++ b/drivers/media/pci/saa7134/saa7134-input.c
@@ -990,7 +990,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev)
dev->init_data.name = "BeholdTV";
dev->init_data.get_key = get_key_beholdm6xx;
dev->init_data.ir_codes = RC_MAP_BEHOLD;
- dev->init_data.type = RC_TYPE_NEC;
+ dev->init_data.type = RC_BIT_NEC;
info.addr = 0x2d;
break;
case SAA7134_BOARD_AVERMEDIA_CARDBUS_501:
diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c
index 4a77124ee70..3abf52711e1 100644
--- a/drivers/media/pci/saa7134/saa7134-video.c
+++ b/drivers/media/pci/saa7134/saa7134-video.c
@@ -2511,7 +2511,7 @@ int saa7134_video_init1(struct saa7134_dev *dev)
/* sanitycheck insmod options */
if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
gbuffers = 2;
- if (gbufsize < 0 || gbufsize > gbufsize_max)
+ if (gbufsize > gbufsize_max)
gbufsize = gbufsize_max;
gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
diff --git a/drivers/media/pci/saa7164/saa7164-api.c b/drivers/media/pci/saa7164/saa7164-api.c
index eff7135cf0e..e042963d377 100644
--- a/drivers/media/pci/saa7164/saa7164-api.c
+++ b/drivers/media/pci/saa7164/saa7164-api.c
@@ -165,7 +165,7 @@ int saa7164_api_set_vbi_format(struct saa7164_port *port)
return ret;
}
-int saa7164_api_set_gop_size(struct saa7164_port *port)
+static int saa7164_api_set_gop_size(struct saa7164_port *port)
{
struct saa7164_dev *dev = port->dev;
struct tmComResEncVideoGopStructure gs;
@@ -619,7 +619,7 @@ int saa7164_api_get_videomux(struct saa7164_port *port)
return ret;
}
-int saa7164_api_set_dif(struct saa7164_port *port, u8 reg, u8 val)
+static int saa7164_api_set_dif(struct saa7164_port *port, u8 reg, u8 val)
{
struct saa7164_dev *dev = port->dev;
@@ -822,8 +822,8 @@ int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen)
&reg[0], 128, buf);
}
-int saa7164_api_configure_port_vbi(struct saa7164_dev *dev,
- struct saa7164_port *port)
+static int saa7164_api_configure_port_vbi(struct saa7164_dev *dev,
+ struct saa7164_port *port)
{
struct tmComResVBIFormatDescrHeader *fmt = &port->vbi_fmt_ntsc;
@@ -858,9 +858,10 @@ int saa7164_api_configure_port_vbi(struct saa7164_dev *dev,
return 0;
}
-int saa7164_api_configure_port_mpeg2ts(struct saa7164_dev *dev,
- struct saa7164_port *port,
- struct tmComResTSFormatDescrHeader *tsfmt)
+static int
+saa7164_api_configure_port_mpeg2ts(struct saa7164_dev *dev,
+ struct saa7164_port *port,
+ struct tmComResTSFormatDescrHeader *tsfmt)
{
dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", tsfmt->bFormatIndex);
dprintk(DBGLVL_API, " bDataOffset = 0x%x\n", tsfmt->bDataOffset);
@@ -892,9 +893,10 @@ int saa7164_api_configure_port_mpeg2ts(struct saa7164_dev *dev,
return 0;
}
-int saa7164_api_configure_port_mpeg2ps(struct saa7164_dev *dev,
- struct saa7164_port *port,
- struct tmComResPSFormatDescrHeader *fmt)
+static int
+saa7164_api_configure_port_mpeg2ps(struct saa7164_dev *dev,
+ struct saa7164_port *port,
+ struct tmComResPSFormatDescrHeader *fmt)
{
dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", fmt->bFormatIndex);
dprintk(DBGLVL_API, " wPacketLength= 0x%x\n", fmt->wPacketLength);
@@ -925,7 +927,7 @@ int saa7164_api_configure_port_mpeg2ps(struct saa7164_dev *dev,
return 0;
}
-int saa7164_api_dump_subdevs(struct saa7164_dev *dev, u8 *buf, int len)
+static int saa7164_api_dump_subdevs(struct saa7164_dev *dev, u8 *buf, int len)
{
struct saa7164_port *tsport = NULL;
struct saa7164_port *encport = NULL;
@@ -1486,7 +1488,7 @@ int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr, u32 datalen,
return ret == SAA_OK ? 0 : -EIO;
}
-int saa7164_api_modify_gpio(struct saa7164_dev *dev, u8 unitid,
+static int saa7164_api_modify_gpio(struct saa7164_dev *dev, u8 unitid,
u8 pin, u8 state)
{
int ret;
diff --git a/drivers/media/pci/saa7164/saa7164-bus.c b/drivers/media/pci/saa7164/saa7164-bus.c
index a7f58a99875..5f6f3094c44 100644
--- a/drivers/media/pci/saa7164/saa7164-bus.c
+++ b/drivers/media/pci/saa7164/saa7164-bus.c
@@ -81,7 +81,7 @@ void saa7164_bus_dump(struct saa7164_dev *dev)
}
/* Intensionally throw a BUG() if the state of the message bus looks corrupt */
-void saa7164_bus_verify(struct saa7164_dev *dev)
+static void saa7164_bus_verify(struct saa7164_dev *dev)
{
struct tmComResBusInfo *b = &dev->bus;
int bug = 0;
@@ -106,8 +106,8 @@ void saa7164_bus_verify(struct saa7164_dev *dev)
}
}
-void saa7164_bus_dumpmsg(struct saa7164_dev *dev, struct tmComResInfo* m,
- void *buf)
+static void saa7164_bus_dumpmsg(struct saa7164_dev *dev, struct tmComResInfo *m,
+ void *buf)
{
dprintk(DBGLVL_BUS, "Dumping msg structure:\n");
dprintk(DBGLVL_BUS, " .id = %d\n", m->id);
diff --git a/drivers/media/pci/saa7164/saa7164-cmd.c b/drivers/media/pci/saa7164/saa7164-cmd.c
index 62fac7f9d04..cfabcbacc33 100644
--- a/drivers/media/pci/saa7164/saa7164-cmd.c
+++ b/drivers/media/pci/saa7164/saa7164-cmd.c
@@ -23,7 +23,7 @@
#include "saa7164.h"
-int saa7164_cmd_alloc_seqno(struct saa7164_dev *dev)
+static int saa7164_cmd_alloc_seqno(struct saa7164_dev *dev)
{
int i, ret = -1;
@@ -42,7 +42,7 @@ int saa7164_cmd_alloc_seqno(struct saa7164_dev *dev)
return ret;
}
-void saa7164_cmd_free_seqno(struct saa7164_dev *dev, u8 seqno)
+static void saa7164_cmd_free_seqno(struct saa7164_dev *dev, u8 seqno)
{
mutex_lock(&dev->lock);
if ((dev->cmds[seqno].inuse == 1) &&
@@ -54,7 +54,7 @@ void saa7164_cmd_free_seqno(struct saa7164_dev *dev, u8 seqno)
mutex_unlock(&dev->lock);
}
-void saa7164_cmd_timeout_seqno(struct saa7164_dev *dev, u8 seqno)
+static void saa7164_cmd_timeout_seqno(struct saa7164_dev *dev, u8 seqno)
{
mutex_lock(&dev->lock);
if ((dev->cmds[seqno].inuse == 1) &&
@@ -64,7 +64,7 @@ void saa7164_cmd_timeout_seqno(struct saa7164_dev *dev, u8 seqno)
mutex_unlock(&dev->lock);
}
-u32 saa7164_cmd_timeout_get(struct saa7164_dev *dev, u8 seqno)
+static u32 saa7164_cmd_timeout_get(struct saa7164_dev *dev, u8 seqno)
{
int ret = 0;
@@ -132,7 +132,7 @@ int saa7164_irq_dequeue(struct saa7164_dev *dev)
/* Commands to the f/w get marshelled to/from this code then onto the PCI
* -bus/c running buffer. */
-int saa7164_cmd_dequeue(struct saa7164_dev *dev)
+static int saa7164_cmd_dequeue(struct saa7164_dev *dev)
{
int loop = 1;
int ret;
@@ -186,8 +186,8 @@ int saa7164_cmd_dequeue(struct saa7164_dev *dev)
return SAA_OK;
}
-int saa7164_cmd_set(struct saa7164_dev *dev, struct tmComResInfo *msg,
- void *buf)
+static int saa7164_cmd_set(struct saa7164_dev *dev, struct tmComResInfo *msg,
+ void *buf)
{
struct tmComResBusInfo *bus = &dev->bus;
u8 cmd_sent;
@@ -259,7 +259,7 @@ out:
/* Wait for a signal event, without holding a mutex. Either return TIMEOUT if
* the event never occurred, or SAA_OK if it was signaled during the wait.
*/
-int saa7164_cmd_wait(struct saa7164_dev *dev, u8 seqno)
+static int saa7164_cmd_wait(struct saa7164_dev *dev, u8 seqno)
{
wait_queue_head_t *q = NULL;
int ret = SAA_BUS_TIMEOUT;
diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c
index 2c9ad878bef..063047f5676 100644
--- a/drivers/media/pci/saa7164/saa7164-core.c
+++ b/drivers/media/pci/saa7164/saa7164-core.c
@@ -410,7 +410,7 @@ static void saa7164_work_enchandler(struct work_struct *w)
} else
rp = (port->last_svc_rp + 1) % 8;
- if ((rp < 0) || (rp > (port->hwcfg.buffercount - 1))) {
+ if (rp > (port->hwcfg.buffercount - 1)) {
printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp);
break;
}
@@ -486,7 +486,7 @@ static void saa7164_work_vbihandler(struct work_struct *w)
} else
rp = (port->last_svc_rp + 1) % 8;
- if ((rp < 0) || (rp > (port->hwcfg.buffercount - 1))) {
+ if (rp > (port->hwcfg.buffercount - 1)) {
printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp);
break;
}
diff --git a/drivers/media/pci/saa7164/saa7164-encoder.c b/drivers/media/pci/saa7164/saa7164-encoder.c
index a9ed686ad08..994018e2d0d 100644
--- a/drivers/media/pci/saa7164/saa7164-encoder.c
+++ b/drivers/media/pci/saa7164/saa7164-encoder.c
@@ -1101,7 +1101,8 @@ static int fops_release(struct file *file)
return 0;
}
-struct saa7164_user_buffer *saa7164_enc_next_buf(struct saa7164_port *port)
+static struct
+saa7164_user_buffer *saa7164_enc_next_buf(struct saa7164_port *port)
{
struct saa7164_user_buffer *ubuf = NULL;
struct saa7164_dev *dev = port->dev;
@@ -1287,8 +1288,8 @@ static const struct v4l2_file_operations mpeg_fops = {
.unlocked_ioctl = video_ioctl2,
};
-int saa7164_g_chip_ident(struct file *file, void *fh,
- struct v4l2_dbg_chip_ident *chip)
+static int saa7164_g_chip_ident(struct file *file, void *fh,
+ struct v4l2_dbg_chip_ident *chip)
{
struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port;
struct saa7164_dev *dev = port->dev;
@@ -1297,8 +1298,8 @@ int saa7164_g_chip_ident(struct file *file, void *fh,
return 0;
}
-int saa7164_g_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
+static int saa7164_g_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
{
struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port;
struct saa7164_dev *dev = port->dev;
@@ -1310,8 +1311,8 @@ int saa7164_g_register(struct file *file, void *fh,
return 0;
}
-int saa7164_s_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
+static int saa7164_s_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
{
struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port;
struct saa7164_dev *dev = port->dev;
diff --git a/drivers/media/pci/saa7164/saa7164-fw.c b/drivers/media/pci/saa7164/saa7164-fw.c
index a266bf0169e..86763203d61 100644
--- a/drivers/media/pci/saa7164/saa7164-fw.c
+++ b/drivers/media/pci/saa7164/saa7164-fw.c
@@ -37,7 +37,7 @@ struct fw_header {
u32 version;
};
-int saa7164_dl_wait_ack(struct saa7164_dev *dev, u32 reg)
+static int saa7164_dl_wait_ack(struct saa7164_dev *dev, u32 reg)
{
u32 timeout = SAA_DEVICE_TIMEOUT;
while ((saa7164_readl(reg) & 0x01) == 0) {
@@ -53,7 +53,7 @@ int saa7164_dl_wait_ack(struct saa7164_dev *dev, u32 reg)
return 0;
}
-int saa7164_dl_wait_clr(struct saa7164_dev *dev, u32 reg)
+static int saa7164_dl_wait_clr(struct saa7164_dev *dev, u32 reg)
{
u32 timeout = SAA_DEVICE_TIMEOUT;
while (saa7164_readl(reg) & 0x01) {
@@ -71,8 +71,8 @@ int saa7164_dl_wait_clr(struct saa7164_dev *dev, u32 reg)
/* TODO: move dlflags into dev-> and change to write/readl/b */
/* TODO: Excessive levels of debug */
-int saa7164_downloadimage(struct saa7164_dev *dev, u8 *src, u32 srcsize,
- u32 dlflags, u8 *dst, u32 dstsize)
+static int saa7164_downloadimage(struct saa7164_dev *dev, u8 *src, u32 srcsize,
+ u32 dlflags, u8 *dst, u32 dstsize)
{
u32 reg, timeout, offset;
u8 *srcbuf = NULL;
diff --git a/drivers/media/pci/saa7164/saa7164-vbi.c b/drivers/media/pci/saa7164/saa7164-vbi.c
index d8e6c8f1407..b4532299c0e 100644
--- a/drivers/media/pci/saa7164/saa7164-vbi.c
+++ b/drivers/media/pci/saa7164/saa7164-vbi.c
@@ -984,7 +984,8 @@ out:
return ret;
}
-int saa7164_vbi_fmt(struct file *file, void *priv, struct v4l2_format *f)
+static int saa7164_vbi_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
{
/* ntsc */
f->fmt.vbi.samples_per_line = 1600;
@@ -1047,7 +1048,8 @@ static int fops_release(struct file *file)
return 0;
}
-struct saa7164_user_buffer *saa7164_vbi_next_buf(struct saa7164_port *port)
+static struct
+saa7164_user_buffer *saa7164_vbi_next_buf(struct saa7164_port *port)
{
struct saa7164_user_buffer *ubuf = NULL;
struct saa7164_dev *dev = port->dev;
diff --git a/drivers/media/pci/ttpci/av7110.h b/drivers/media/pci/ttpci/av7110.h
index 88b3b2d6cc0..a378662b1dc 100644
--- a/drivers/media/pci/ttpci/av7110.h
+++ b/drivers/media/pci/ttpci/av7110.h
@@ -6,6 +6,7 @@
#include <linux/netdevice.h>
#include <linux/i2c.h>
#include <linux/input.h>
+#include <linux/time.h>
#include <linux/dvb/video.h>
#include <linux/dvb/audio.h>
diff --git a/drivers/media/pci/ttpci/budget-av.c b/drivers/media/pci/ttpci/budget-av.c
index 12ddb53c58d..1f8b1bb0bf9 100644
--- a/drivers/media/pci/ttpci/budget-av.c
+++ b/drivers/media/pci/ttpci/budget-av.c
@@ -1477,8 +1477,8 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
if (saa7113_init(budget_av) == 0) {
budget_av->has_saa7113 = 1;
-
- if (0 != saa7146_vv_init(dev, &vv_data)) {
+ err = saa7146_vv_init(dev, &vv_data);
+ if (err != 0) {
/* fixme: proper cleanup here */
ERR("cannot init vv subsystem\n");
return err;
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 181c7686e41..3dcfea612c4 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -109,6 +109,18 @@ config VIDEO_OMAP3_DEBUG
---help---
Enable debug messages on OMAP 3 camera controller driver.
+config VIDEO_S3C_CAMIF
+ tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver"
+ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
+ depends on (PLAT_S3C64XX || PLAT_S3C24XX) && PM_RUNTIME
+ select VIDEOBUF2_DMA_CONTIG
+ ---help---
+ This is a v4l2 driver for s3c24xx and s3c64xx SoC series camera
+ host interface (CAMIF).
+
+ To compile this driver as a module, choose M here: the module
+ will be called s3c-camif.
+
source "drivers/media/platform/soc_camera/Kconfig"
source "drivers/media/platform/s5p-fimc/Kconfig"
source "drivers/media/platform/s5p-tv/Kconfig"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index baaa55026c8..4817d280217 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_VIDEO_CODA) += coda.o
obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o
+obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/
diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c
index cb2eb26850b..ec476ef5b70 100644
--- a/drivers/media/platform/blackfin/bfin_capture.c
+++ b/drivers/media/platform/blackfin/bfin_capture.c
@@ -1050,19 +1050,7 @@ static struct platform_driver bcap_driver = {
.probe = bcap_probe,
.remove = __devexit_p(bcap_remove),
};
-
-static __init int bcap_init(void)
-{
- return platform_driver_register(&bcap_driver);
-}
-
-static __exit void bcap_exit(void)
-{
- platform_driver_unregister(&bcap_driver);
-}
-
-module_init(bcap_init);
-module_exit(bcap_exit);
+module_platform_driver(bcap_driver);
MODULE_DESCRIPTION("Analog Devices blackfin video capture driver");
MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c
index cd04ae252c3..7b8b547f2d5 100644
--- a/drivers/media/platform/coda.c
+++ b/drivers/media/platform/coda.c
@@ -1540,7 +1540,7 @@ static irqreturn_t coda_irq_handler(int irq, void *data)
u32 wr_ptr, start_ptr;
struct coda_ctx *ctx;
- __cancel_delayed_work(&dev->timeout);
+ cancel_delayed_work(&dev->timeout);
/* read status register to attend the IRQ */
coda_read(dev, CODA_REG_BIT_INT_STATUS);
@@ -1877,7 +1877,7 @@ static const struct coda_devtype coda_devdata[] = {
static struct platform_device_id coda_platform_ids[] = {
{ .name = "coda-imx27", .driver_data = CODA_IMX27 },
- { .name = "coda-imx53", .driver_data = CODA_7541 },
+ { .name = "coda-imx53", .driver_data = CODA_IMX53 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, coda_platform_ids);
diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig
index 78e26d24f63..3c56037c82f 100644
--- a/drivers/media/platform/davinci/Kconfig
+++ b/drivers/media/platform/davinci/Kconfig
@@ -101,7 +101,7 @@ config VIDEO_DM644X_VPBE
tristate "DM644X VPBE HW module"
depends on ARCH_DAVINCI_DM644x
select VIDEO_VPSS_SYSTEM
- select VIDEOBUF_DMA_CONTIG
+ select VIDEOBUF2_DMA_CONTIG
help
Enables VPBE modules used for display on a DM644x
SoC.
diff --git a/drivers/media/platform/davinci/dm355_ccdc.c b/drivers/media/platform/davinci/dm355_ccdc.c
index ce0e4131c06..030950dcfb1 100644
--- a/drivers/media/platform/davinci/dm355_ccdc.c
+++ b/drivers/media/platform/davinci/dm355_ccdc.c
@@ -1003,7 +1003,7 @@ static int __devinit dm355_ccdc_probe(struct platform_device *pdev)
status = PTR_ERR(ccdc_cfg.mclk);
goto fail_nomap;
}
- if (clk_enable(ccdc_cfg.mclk)) {
+ if (clk_prepare_enable(ccdc_cfg.mclk)) {
status = -ENODEV;
goto fail_mclk;
}
@@ -1014,7 +1014,7 @@ static int __devinit dm355_ccdc_probe(struct platform_device *pdev)
status = PTR_ERR(ccdc_cfg.sclk);
goto fail_mclk;
}
- if (clk_enable(ccdc_cfg.sclk)) {
+ if (clk_prepare_enable(ccdc_cfg.sclk)) {
status = -ENODEV;
goto fail_sclk;
}
@@ -1034,8 +1034,10 @@ static int __devinit dm355_ccdc_probe(struct platform_device *pdev)
printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name);
return 0;
fail_sclk:
+ clk_disable_unprepare(ccdc_cfg.sclk);
clk_put(ccdc_cfg.sclk);
fail_mclk:
+ clk_disable_unprepare(ccdc_cfg.mclk);
clk_put(ccdc_cfg.mclk);
fail_nomap:
iounmap(ccdc_cfg.base_addr);
@@ -1050,6 +1052,8 @@ static int dm355_ccdc_remove(struct platform_device *pdev)
{
struct resource *res;
+ clk_disable_unprepare(ccdc_cfg.sclk);
+ clk_disable_unprepare(ccdc_cfg.mclk);
clk_put(ccdc_cfg.mclk);
clk_put(ccdc_cfg.sclk);
iounmap(ccdc_cfg.base_addr);
diff --git a/drivers/media/platform/davinci/dm644x_ccdc.c b/drivers/media/platform/davinci/dm644x_ccdc.c
index ee7942b1996..0215ab6ebc9 100644
--- a/drivers/media/platform/davinci/dm644x_ccdc.c
+++ b/drivers/media/platform/davinci/dm644x_ccdc.c
@@ -994,7 +994,7 @@ static int __devinit dm644x_ccdc_probe(struct platform_device *pdev)
status = PTR_ERR(ccdc_cfg.mclk);
goto fail_nomap;
}
- if (clk_enable(ccdc_cfg.mclk)) {
+ if (clk_prepare_enable(ccdc_cfg.mclk)) {
status = -ENODEV;
goto fail_mclk;
}
@@ -1005,7 +1005,7 @@ static int __devinit dm644x_ccdc_probe(struct platform_device *pdev)
status = PTR_ERR(ccdc_cfg.sclk);
goto fail_mclk;
}
- if (clk_enable(ccdc_cfg.sclk)) {
+ if (clk_prepare_enable(ccdc_cfg.sclk)) {
status = -ENODEV;
goto fail_sclk;
}
@@ -1013,8 +1013,10 @@ static int __devinit dm644x_ccdc_probe(struct platform_device *pdev)
printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name);
return 0;
fail_sclk:
+ clk_disable_unprepare(ccdc_cfg.sclk);
clk_put(ccdc_cfg.sclk);
fail_mclk:
+ clk_disable_unprepare(ccdc_cfg.mclk);
clk_put(ccdc_cfg.mclk);
fail_nomap:
iounmap(ccdc_cfg.base_addr);
@@ -1029,6 +1031,8 @@ static int dm644x_ccdc_remove(struct platform_device *pdev)
{
struct resource *res;
+ clk_disable_unprepare(ccdc_cfg.mclk);
+ clk_disable_unprepare(ccdc_cfg.sclk);
clk_put(ccdc_cfg.mclk);
clk_put(ccdc_cfg.sclk);
iounmap(ccdc_cfg.base_addr);
@@ -1046,8 +1050,8 @@ static int dm644x_ccdc_suspend(struct device *dev)
/* Disable CCDC */
ccdc_enable(0);
/* Disable both master and slave clock */
- clk_disable(ccdc_cfg.mclk);
- clk_disable(ccdc_cfg.sclk);
+ clk_disable_unprepare(ccdc_cfg.mclk);
+ clk_disable_unprepare(ccdc_cfg.sclk);
return 0;
}
@@ -1055,8 +1059,8 @@ static int dm644x_ccdc_suspend(struct device *dev)
static int dm644x_ccdc_resume(struct device *dev)
{
/* Enable both master and slave clock */
- clk_enable(ccdc_cfg.mclk);
- clk_enable(ccdc_cfg.sclk);
+ clk_prepare_enable(ccdc_cfg.mclk);
+ clk_prepare_enable(ccdc_cfg.sclk);
/* Restore CCDC context */
ccdc_restore_context();
diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/davinci/isif.c
index b99d5423e3a..2c26c3e1837 100644
--- a/drivers/media/platform/davinci/isif.c
+++ b/drivers/media/platform/davinci/isif.c
@@ -1053,7 +1053,7 @@ static int __devinit isif_probe(struct platform_device *pdev)
status = PTR_ERR(isif_cfg.mclk);
goto fail_mclk;
}
- if (clk_enable(isif_cfg.mclk)) {
+ if (clk_prepare_enable(isif_cfg.mclk)) {
status = -ENODEV;
goto fail_mclk;
}
@@ -1125,6 +1125,7 @@ fail_nobase_res:
i--;
}
fail_mclk:
+ clk_disable_unprepare(isif_cfg.mclk);
clk_put(isif_cfg.mclk);
vpfe_unregister_ccdc_device(&isif_hw_dev);
return status;
@@ -1145,6 +1146,8 @@ static int isif_remove(struct platform_device *pdev)
i++;
}
vpfe_unregister_ccdc_device(&isif_hw_dev);
+ clk_disable_unprepare(isif_cfg.mclk);
+ clk_put(isif_cfg.mclk);
return 0;
}
diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c
index 69d7a58c92c..7f5cf9b347b 100644
--- a/drivers/media/platform/davinci/vpbe.c
+++ b/drivers/media/platform/davinci/vpbe.c
@@ -612,7 +612,7 @@ static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev)
ret = PTR_ERR(vpbe_dev->dac_clk);
goto fail_mutex_unlock;
}
- if (clk_enable(vpbe_dev->dac_clk)) {
+ if (clk_prepare_enable(vpbe_dev->dac_clk)) {
ret = -ENODEV;
goto fail_mutex_unlock;
}
@@ -759,8 +759,10 @@ fail_kfree_encoders:
fail_dev_unregister:
v4l2_device_unregister(&vpbe_dev->v4l2_dev);
fail_clk_put:
- if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0)
+ if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) {
+ clk_disable_unprepare(vpbe_dev->dac_clk);
clk_put(vpbe_dev->dac_clk);
+ }
fail_mutex_unlock:
mutex_unlock(&vpbe_dev->lock);
return ret;
@@ -777,8 +779,10 @@ fail_mutex_unlock:
static void vpbe_deinitialize(struct device *dev, struct vpbe_device *vpbe_dev)
{
v4l2_device_unregister(&vpbe_dev->v4l2_dev);
- if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0)
+ if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) {
+ clk_disable_unprepare(vpbe_dev->dac_clk);
clk_put(vpbe_dev->dac_clk);
+ }
kfree(vpbe_dev->amp);
kfree(vpbe_dev->encoders);
diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c
index 161c77650e2..2bfde7958fe 100644
--- a/drivers/media/platform/davinci/vpbe_display.c
+++ b/drivers/media/platform/davinci/vpbe_display.c
@@ -47,6 +47,9 @@ static int debug;
module_param(debug, int, 0644);
+static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev,
+ struct vpbe_layer *layer);
+
static int venc_is_second_field(struct vpbe_display *disp_dev)
{
struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
@@ -73,10 +76,11 @@ static void vpbe_isr_even_field(struct vpbe_display *disp_obj,
if (layer->cur_frm == layer->next_frm)
return;
ktime_get_ts(&timevalue);
- layer->cur_frm->ts.tv_sec = timevalue.tv_sec;
- layer->cur_frm->ts.tv_usec = timevalue.tv_nsec / NSEC_PER_USEC;
- layer->cur_frm->state = VIDEOBUF_DONE;
- wake_up_interruptible(&layer->cur_frm->done);
+ layer->cur_frm->vb.v4l2_buf.timestamp.tv_sec =
+ timevalue.tv_sec;
+ layer->cur_frm->vb.v4l2_buf.timestamp.tv_usec =
+ timevalue.tv_nsec / NSEC_PER_USEC;
+ vb2_buffer_done(&layer->cur_frm->vb, VB2_BUF_STATE_DONE);
/* Make cur_frm pointing to next_frm */
layer->cur_frm = layer->next_frm;
}
@@ -99,16 +103,14 @@ static void vpbe_isr_odd_field(struct vpbe_display *disp_obj,
* otherwise hold on current frame
* Get next from the buffer queue
*/
- layer->next_frm = list_entry(
- layer->dma_queue.next,
- struct videobuf_buffer,
- queue);
+ layer->next_frm = list_entry(layer->dma_queue.next,
+ struct vpbe_disp_buffer, list);
/* Remove that from the buffer queue */
- list_del(&layer->next_frm->queue);
+ list_del(&layer->next_frm->list);
spin_unlock(&disp_obj->dma_queue_lock);
/* Mark state of the frame to active */
- layer->next_frm->state = VIDEOBUF_ACTIVE;
- addr = videobuf_to_dma_contig(layer->next_frm);
+ layer->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+ addr = vb2_dma_contig_plane_dma_addr(&layer->next_frm->vb, 0);
osd_device->ops.start_layer(osd_device,
layer->layer_info.id,
addr,
@@ -199,39 +201,29 @@ static irqreturn_t venc_isr(int irq, void *arg)
/*
* vpbe_buffer_prepare()
- * This is the callback function called from videobuf_qbuf() function
+ * This is the callback function called from vb2_qbuf() function
* the buffer is prepared and user space virtual address is converted into
* physical address
*/
-static int vpbe_buffer_prepare(struct videobuf_queue *q,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
+static int vpbe_buffer_prepare(struct vb2_buffer *vb)
{
- struct vpbe_fh *fh = q->priv_data;
+ struct vpbe_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_queue *q = vb->vb2_queue;
struct vpbe_layer *layer = fh->layer;
struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
unsigned long addr;
- int ret;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
"vpbe_buffer_prepare\n");
- /* If buffer is not initialized, initialize it */
- if (VIDEOBUF_NEEDS_INIT == vb->state) {
- vb->width = layer->pix_fmt.width;
- vb->height = layer->pix_fmt.height;
- vb->size = layer->pix_fmt.sizeimage;
- vb->field = field;
-
- ret = videobuf_iolock(q, vb, NULL);
- if (ret < 0) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Failed to map \
- user address\n");
+ if (vb->state != VB2_BUF_STATE_ACTIVE &&
+ vb->state != VB2_BUF_STATE_PREPARED) {
+ vb2_set_plane_payload(vb, 0, layer->pix_fmt.sizeimage);
+ if (vb2_plane_vaddr(vb, 0) &&
+ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
return -EINVAL;
- }
-
- addr = videobuf_to_dma_contig(vb);
+ addr = vb2_dma_contig_plane_dma_addr(vb, 0);
if (q->streaming) {
if (!IS_ALIGNED(addr, 8)) {
v4l2_err(&vpbe_dev->v4l2_dev,
@@ -240,7 +232,6 @@ static int vpbe_buffer_prepare(struct videobuf_queue *q,
return -EINVAL;
}
}
- vb->state = VIDEOBUF_PREPARED;
}
return 0;
}
@@ -249,22 +240,26 @@ static int vpbe_buffer_prepare(struct videobuf_queue *q,
* vpbe_buffer_setup()
* This function allocates memory for the buffers
*/
-static int vpbe_buffer_setup(struct videobuf_queue *q,
- unsigned int *count,
- unsigned int *size)
+static int
+vpbe_buffer_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+
{
/* Get the file handle object and layer object */
- struct vpbe_fh *fh = q->priv_data;
+ struct vpbe_fh *fh = vb2_get_drv_priv(vq);
struct vpbe_layer *layer = fh->layer;
struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n");
- *size = layer->pix_fmt.sizeimage;
-
/* Store number of buffers allocated in numbuffer member */
- if (*count < VPBE_DEFAULT_NUM_BUFS)
- *count = layer->numbuffers = VPBE_DEFAULT_NUM_BUFS;
+ if (*nbuffers < VPBE_DEFAULT_NUM_BUFS)
+ *nbuffers = layer->numbuffers = VPBE_DEFAULT_NUM_BUFS;
+
+ *nplanes = 1;
+ sizes[0] = layer->pix_fmt.sizeimage;
+ alloc_ctxs[0] = layer->alloc_ctx;
return 0;
}
@@ -273,11 +268,12 @@ static int vpbe_buffer_setup(struct videobuf_queue *q,
* vpbe_buffer_queue()
* This function adds the buffer to DMA queue
*/
-static void vpbe_buffer_queue(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
+static void vpbe_buffer_queue(struct vb2_buffer *vb)
{
/* Get the file handle object and layer object */
- struct vpbe_fh *fh = q->priv_data;
+ struct vpbe_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+ struct vpbe_disp_buffer *buf = container_of(vb,
+ struct vpbe_disp_buffer, vb);
struct vpbe_layer *layer = fh->layer;
struct vpbe_display *disp = fh->disp_dev;
struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
@@ -288,39 +284,125 @@ static void vpbe_buffer_queue(struct videobuf_queue *q,
/* add the buffer to the DMA queue */
spin_lock_irqsave(&disp->dma_queue_lock, flags);
- list_add_tail(&vb->queue, &layer->dma_queue);
+ list_add_tail(&buf->list, &layer->dma_queue);
spin_unlock_irqrestore(&disp->dma_queue_lock, flags);
- /* Change state of the buffer */
- vb->state = VIDEOBUF_QUEUED;
}
/*
- * vpbe_buffer_release()
- * This function is called from the videobuf layer to free memory allocated to
+ * vpbe_buf_cleanup()
+ * This function is called from the vb2 layer to free memory allocated to
* the buffers
*/
-static void vpbe_buffer_release(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
+static void vpbe_buf_cleanup(struct vb2_buffer *vb)
{
/* Get the file handle object and layer object */
- struct vpbe_fh *fh = q->priv_data;
+ struct vpbe_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
struct vpbe_layer *layer = fh->layer;
struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct vpbe_disp_buffer *buf = container_of(vb,
+ struct vpbe_disp_buffer, vb);
+ unsigned long flags;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "vpbe_buffer_release\n");
+ "vpbe_buf_cleanup\n");
+
+ spin_lock_irqsave(&layer->irqlock, flags);
+ if (vb->state == VB2_BUF_STATE_ACTIVE)
+ list_del_init(&buf->list);
+ spin_unlock_irqrestore(&layer->irqlock, flags);
+}
+
+static void vpbe_wait_prepare(struct vb2_queue *vq)
+{
+ struct vpbe_fh *fh = vb2_get_drv_priv(vq);
+ struct vpbe_layer *layer = fh->layer;
+
+ mutex_unlock(&layer->opslock);
+}
+
+static void vpbe_wait_finish(struct vb2_queue *vq)
+{
+ struct vpbe_fh *fh = vb2_get_drv_priv(vq);
+ struct vpbe_layer *layer = fh->layer;
+
+ mutex_lock(&layer->opslock);
+}
+
+static int vpbe_buffer_init(struct vb2_buffer *vb)
+{
+ struct vpbe_disp_buffer *buf = container_of(vb,
+ struct vpbe_disp_buffer, vb);
+
+ INIT_LIST_HEAD(&buf->list);
+ return 0;
+}
+
+static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct vpbe_fh *fh = vb2_get_drv_priv(vq);
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ /* If buffer queue is empty, return error */
+ if (list_empty(&layer->dma_queue)) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n");
+ return -EINVAL;
+ }
+ /* Get the next frame from the buffer queue */
+ layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next,
+ struct vpbe_disp_buffer, list);
+ /* Remove buffer from the buffer queue */
+ list_del(&layer->cur_frm->list);
+ /* Mark state of the current frame to active */
+ layer->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+ /* Initialize field_id and started member */
+ layer->field_id = 0;
+
+ /* Set parameters in OSD and VENC */
+ ret = vpbe_set_osd_display_params(fh->disp_dev, layer);
+ if (ret < 0)
+ return ret;
- if (V4L2_MEMORY_USERPTR != layer->memory)
- videobuf_dma_contig_free(q, vb);
+ /*
+ * if request format is yuv420 semiplanar, need to
+ * enable both video windows
+ */
+ layer->started = 1;
+ layer->layer_first_int = 1;
+
+ return ret;
+}
+
+static int vpbe_stop_streaming(struct vb2_queue *vq)
+{
+ struct vpbe_fh *fh = vb2_get_drv_priv(vq);
+ struct vpbe_layer *layer = fh->layer;
+
+ if (!vb2_is_streaming(vq))
+ return 0;
+
+ /* release all active buffers */
+ while (!list_empty(&layer->dma_queue)) {
+ layer->next_frm = list_entry(layer->dma_queue.next,
+ struct vpbe_disp_buffer, list);
+ list_del(&layer->next_frm->list);
+ vb2_buffer_done(&layer->next_frm->vb, VB2_BUF_STATE_ERROR);
+ }
- vb->state = VIDEOBUF_NEEDS_INIT;
+ return 0;
}
-static struct videobuf_queue_ops video_qops = {
- .buf_setup = vpbe_buffer_setup,
+static struct vb2_ops video_qops = {
+ .queue_setup = vpbe_buffer_queue_setup,
+ .wait_prepare = vpbe_wait_prepare,
+ .wait_finish = vpbe_wait_finish,
+ .buf_init = vpbe_buffer_init,
.buf_prepare = vpbe_buffer_prepare,
+ .start_streaming = vpbe_start_streaming,
+ .stop_streaming = vpbe_stop_streaming,
+ .buf_cleanup = vpbe_buf_cleanup,
.buf_queue = vpbe_buffer_queue,
- .buf_release = vpbe_buffer_release,
};
static
@@ -345,7 +427,7 @@ static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev,
unsigned long addr;
int ret;
- addr = videobuf_to_dma_contig(layer->cur_frm);
+ addr = vb2_dma_contig_plane_dma_addr(&layer->cur_frm->vb, 0);
/* Set address in the display registers */
osd_device->ops.start_layer(osd_device,
layer->layer_info.id,
@@ -620,9 +702,12 @@ static int vpbe_display_querycap(struct file *file, void *priv,
struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
cap->version = VPBE_DISPLAY_VERSION_CODE;
- cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
- strlcpy(cap->driver, VPBE_DISPLAY_DRIVER, sizeof(cap->driver));
- strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info));
+ cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ snprintf(cap->driver, sizeof(cap->driver), "%s",
+ dev_name(vpbe_dev->pdev));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ dev_name(vpbe_dev->pdev));
strlcpy(cap->card, vpbe_dev->cfg->module_name, sizeof(cap->card));
return 0;
@@ -1161,7 +1246,7 @@ static int vpbe_display_streamoff(struct file *file, void *priv,
osd_device->ops.disable_layer(osd_device,
layer->layer_info.id);
layer->started = 0;
- ret = videobuf_streamoff(&layer->buffer_queue);
+ ret = vb2_streamoff(&layer->buffer_queue, buf_type);
return ret;
}
@@ -1199,46 +1284,15 @@ static int vpbe_display_streamon(struct file *file, void *priv,
}
/*
- * Call videobuf_streamon to start streaming
+ * Call vb2_streamon to start streaming
* in videobuf
*/
- ret = videobuf_streamon(&layer->buffer_queue);
+ ret = vb2_streamon(&layer->buffer_queue, buf_type);
if (ret) {
v4l2_err(&vpbe_dev->v4l2_dev,
- "error in videobuf_streamon\n");
+ "error in vb2_streamon\n");
return ret;
}
- /* If buffer queue is empty, return error */
- if (list_empty(&layer->dma_queue)) {
- v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n");
- goto streamoff;
- }
- /* Get the next frame from the buffer queue */
- layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next,
- struct videobuf_buffer, queue);
- /* Remove buffer from the buffer queue */
- list_del(&layer->cur_frm->queue);
- /* Mark state of the current frame to active */
- layer->cur_frm->state = VIDEOBUF_ACTIVE;
- /* Initialize field_id and started member */
- layer->field_id = 0;
-
- /* Set parameters in OSD and VENC */
- ret = vpbe_set_osd_display_params(disp_dev, layer);
- if (ret < 0)
- goto streamoff;
-
- /*
- * if request format is yuv420 semiplanar, need to
- * enable both video windows
- */
- layer->started = 1;
-
- layer->layer_first_int = 1;
-
- return ret;
-streamoff:
- ret = videobuf_streamoff(&layer->buffer_queue);
return ret;
}
@@ -1265,10 +1319,10 @@ static int vpbe_display_dqbuf(struct file *file, void *priv,
}
if (file->f_flags & O_NONBLOCK)
/* Call videobuf_dqbuf for non blocking mode */
- ret = videobuf_dqbuf(&layer->buffer_queue, buf, 1);
+ ret = vb2_dqbuf(&layer->buffer_queue, buf, 1);
else
/* Call videobuf_dqbuf for blocking mode */
- ret = videobuf_dqbuf(&layer->buffer_queue, buf, 0);
+ ret = vb2_dqbuf(&layer->buffer_queue, buf, 0);
return ret;
}
@@ -1295,7 +1349,7 @@ static int vpbe_display_qbuf(struct file *file, void *priv,
return -EACCES;
}
- return videobuf_qbuf(&layer->buffer_queue, p);
+ return vb2_qbuf(&layer->buffer_queue, p);
}
static int vpbe_display_querybuf(struct file *file, void *priv,
@@ -1304,7 +1358,6 @@ static int vpbe_display_querybuf(struct file *file, void *priv,
struct vpbe_fh *fh = file->private_data;
struct vpbe_layer *layer = fh->layer;
struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- int ret;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
"VIDIOC_QUERYBUF, layer id = %d\n",
@@ -1314,11 +1367,8 @@ static int vpbe_display_querybuf(struct file *file, void *priv,
v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
return -EINVAL;
}
-
- /* Call videobuf_querybuf to get information */
- ret = videobuf_querybuf(&layer->buffer_queue, buf);
-
- return ret;
+ /* Call vb2_querybuf to get information */
+ return vb2_querybuf(&layer->buffer_queue, buf);
}
static int vpbe_display_reqbufs(struct file *file, void *priv,
@@ -1327,8 +1377,8 @@ static int vpbe_display_reqbufs(struct file *file, void *priv,
struct vpbe_fh *fh = file->private_data;
struct vpbe_layer *layer = fh->layer;
struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct vb2_queue *q;
int ret;
-
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_reqbufs\n");
if (V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) {
@@ -1342,15 +1392,26 @@ static int vpbe_display_reqbufs(struct file *file, void *priv,
return -EBUSY;
}
/* Initialize videobuf queue as per the buffer type */
- videobuf_queue_dma_contig_init(&layer->buffer_queue,
- &video_qops,
- vpbe_dev->pdev,
- &layer->irqlock,
- V4L2_BUF_TYPE_VIDEO_OUTPUT,
- layer->pix_fmt.field,
- sizeof(struct videobuf_buffer),
- fh, NULL);
+ layer->alloc_ctx = vb2_dma_contig_init_ctx(vpbe_dev->pdev);
+ if (!layer->alloc_ctx) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Failed to get the context\n");
+ return -EINVAL;
+ }
+ q = &layer->buffer_queue;
+ memset(q, 0, sizeof(*q));
+ q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = fh;
+ q->ops = &video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct vpbe_disp_buffer);
+ ret = vb2_queue_init(q);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "vb2_queue_init() failed\n");
+ vb2_dma_contig_cleanup_ctx(layer->alloc_ctx);
+ return ret;
+ }
/* Set io allowed member of file handle to TRUE */
fh->io_allowed = 1;
/* Increment io usrs member of layer object to 1 */
@@ -1360,9 +1421,7 @@ static int vpbe_display_reqbufs(struct file *file, void *priv,
/* Initialize buffer queue */
INIT_LIST_HEAD(&layer->dma_queue);
/* Allocate buffers */
- ret = videobuf_reqbufs(&layer->buffer_queue, req_buf);
-
- return ret;
+ return vb2_reqbufs(q, req_buf);
}
/*
@@ -1381,7 +1440,7 @@ static int vpbe_display_mmap(struct file *filep, struct vm_area_struct *vma)
if (mutex_lock_interruptible(&layer->opslock))
return -ERESTARTSYS;
- ret = videobuf_mmap_mapper(&layer->buffer_queue, vma);
+ ret = vb2_mmap(&layer->buffer_queue, vma);
mutex_unlock(&layer->opslock);
return ret;
}
@@ -1398,7 +1457,7 @@ static unsigned int vpbe_display_poll(struct file *filep, poll_table *wait)
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_poll\n");
if (layer->started) {
mutex_lock(&layer->opslock);
- err = videobuf_poll_stream(filep, &layer->buffer_queue, wait);
+ err = vb2_poll(&layer->buffer_queue, filep, wait);
mutex_unlock(&layer->opslock);
}
return err;
@@ -1488,8 +1547,8 @@ static int vpbe_display_release(struct file *file)
layer->layer_info.id);
layer->started = 0;
/* Free buffers allocated */
- videobuf_queue_cancel(&layer->buffer_queue);
- videobuf_mmap_free(&layer->buffer_queue);
+ vb2_queue_release(&layer->buffer_queue);
+ vb2_dma_contig_cleanup_ctx(&layer->buffer_queue);
}
/* Decrement layer usrs counter */
diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c
index bba299dbf39..707f243f810 100644
--- a/drivers/media/platform/davinci/vpbe_osd.c
+++ b/drivers/media/platform/davinci/vpbe_osd.c
@@ -62,7 +62,7 @@ static inline u32 osd_set(struct osd_state *sd, u32 mask, u32 offset)
{
struct osd_state *osd = sd;
- u32 addr = osd->osd_base + offset;
+ void __iomem *addr = osd->osd_base + offset;
u32 val = readl(addr) | mask;
writel(val, addr);
@@ -74,7 +74,7 @@ static inline u32 osd_clear(struct osd_state *sd, u32 mask, u32 offset)
{
struct osd_state *osd = sd;
- u32 addr = osd->osd_base + offset;
+ void __iomem *addr = osd->osd_base + offset;
u32 val = readl(addr) & ~mask;
writel(val, addr);
@@ -87,7 +87,7 @@ static inline u32 osd_modify(struct osd_state *sd, u32 mask, u32 val,
{
struct osd_state *osd = sd;
- u32 addr = osd->osd_base + offset;
+ void __iomem *addr = osd->osd_base + offset;
u32 new_val = (readl(addr) & ~mask) | (val & mask);
writel(new_val, addr);
@@ -1559,8 +1559,7 @@ static int osd_probe(struct platform_device *pdev)
ret = -ENODEV;
goto free_mem;
}
- osd->osd_base = (unsigned long)ioremap_nocache(res->start,
- osd->osd_size);
+ osd->osd_base = ioremap_nocache(res->start, osd->osd_size);
if (!osd->osd_base) {
dev_err(osd->dev, "Unable to map the OSD region\n");
ret = -ENODEV;
diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c
index cff3c0ab501..0d6cc8e4deb 100644
--- a/drivers/media/platform/davinci/vpif.c
+++ b/drivers/media/platform/davinci/vpif.c
@@ -444,7 +444,7 @@ static int __devinit vpif_probe(struct platform_device *pdev)
status = PTR_ERR(vpif_clk);
goto clk_fail;
}
- clk_enable(vpif_clk);
+ clk_prepare_enable(vpif_clk);
spin_lock_init(&vpif_lock);
dev_info(&pdev->dev, "vpif probe success\n");
@@ -460,7 +460,7 @@ fail:
static int __devexit vpif_remove(struct platform_device *pdev)
{
if (vpif_clk) {
- clk_disable(vpif_clk);
+ clk_disable_unprepare(vpif_clk);
clk_put(vpif_clk);
}
@@ -472,13 +472,13 @@ static int __devexit vpif_remove(struct platform_device *pdev)
#ifdef CONFIG_PM
static int vpif_suspend(struct device *dev)
{
- clk_disable(vpif_clk);
+ clk_disable_unprepare(vpif_clk);
return 0;
}
static int vpif_resume(struct device *dev)
{
- clk_enable(vpif_clk);
+ clk_prepare_enable(vpif_clk);
return 0;
}
diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
index fcabc023885..a409ccefb38 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -201,13 +201,16 @@ static void vpif_buffer_queue(struct vb2_buffer *vb)
struct vpif_cap_buffer *buf = container_of(vb,
struct vpif_cap_buffer, vb);
struct common_obj *common;
+ unsigned long flags;
common = &ch->common[VPIF_VIDEO_INDEX];
vpif_dbg(2, debug, "vpif_buffer_queue\n");
+ spin_lock_irqsave(&common->irqlock, flags);
/* add the buffer to the DMA queue */
list_add_tail(&buf->list, &common->dma_queue);
+ spin_unlock_irqrestore(&common->irqlock, flags);
}
/**
@@ -278,10 +281,13 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
struct vpif_params *vpif = &ch->vpifparams;
unsigned long addr = 0;
+ unsigned long flags;
int ret;
- /* If buffer queue is empty, return error */
+ /* If buffer queue is empty, return error */
+ spin_lock_irqsave(&common->irqlock, flags);
if (list_empty(&common->dma_queue)) {
+ spin_unlock_irqrestore(&common->irqlock, flags);
vpif_dbg(1, debug, "buffer queue is empty\n");
return -EIO;
}
@@ -291,6 +297,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
struct vpif_cap_buffer, list);
/* Remove buffer from the buffer queue */
list_del(&common->cur_frm->list);
+ spin_unlock_irqrestore(&common->irqlock, flags);
/* Mark state of the current frame to active */
common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
/* Initialize field_id and started member */
@@ -362,6 +369,7 @@ static int vpif_stop_streaming(struct vb2_queue *vq)
struct vpif_fh *fh = vb2_get_drv_priv(vq);
struct channel_obj *ch = fh->channel;
struct common_obj *common;
+ unsigned long flags;
if (!vb2_is_streaming(vq))
return 0;
@@ -369,12 +377,14 @@ static int vpif_stop_streaming(struct vb2_queue *vq)
common = &ch->common[VPIF_VIDEO_INDEX];
/* release all active buffers */
+ spin_lock_irqsave(&common->irqlock, flags);
while (!list_empty(&common->dma_queue)) {
common->next_frm = list_entry(common->dma_queue.next,
struct vpif_cap_buffer, list);
list_del(&common->next_frm->list);
vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR);
}
+ spin_unlock_irqrestore(&common->irqlock, flags);
return 0;
}
@@ -420,10 +430,12 @@ static void vpif_schedule_next_buffer(struct common_obj *common)
{
unsigned long addr = 0;
+ spin_lock(&common->irqlock);
common->next_frm = list_entry(common->dma_queue.next,
struct vpif_cap_buffer, list);
/* Remove that buffer from the buffer queue */
list_del(&common->next_frm->list);
+ spin_unlock(&common->irqlock);
common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0);
@@ -468,8 +480,12 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
/* Check the field format */
if (1 == ch->vpifparams.std_info.frm_fmt) {
/* Progressive mode */
- if (list_empty(&common->dma_queue))
+ spin_lock(&common->irqlock);
+ if (list_empty(&common->dma_queue)) {
+ spin_unlock(&common->irqlock);
continue;
+ }
+ spin_unlock(&common->irqlock);
if (!channel_first_int[i][channel_id])
vpif_process_buffer_complete(common);
@@ -513,9 +529,13 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
vpif_process_buffer_complete(common);
} else if (1 == fid) {
/* odd field */
+ spin_lock(&common->irqlock);
if (list_empty(&common->dma_queue) ||
- (common->cur_frm != common->next_frm))
+ (common->cur_frm != common->next_frm)) {
+ spin_unlock(&common->irqlock);
continue;
+ }
+ spin_unlock(&common->irqlock);
vpif_schedule_next_buffer(common);
}
@@ -1004,9 +1024,9 @@ static int vpif_reqbufs(struct file *file, void *priv,
/* Initialize videobuf2 queue as per the buffer type */
common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
- if (!common->alloc_ctx) {
+ if (IS_ERR(common->alloc_ctx)) {
vpif_err("Failed to get the context\n");
- return -EINVAL;
+ return PTR_ERR(common->alloc_ctx);
}
q = &common->buffer_queue;
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -1715,7 +1735,7 @@ vpif_enum_dv_timings(struct file *file, void *priv,
int ret;
ret = v4l2_subdev_call(ch->sd, video, enum_dv_timings, timings);
- if (ret == -ENOIOCTLCMD && ret == -ENODEV)
+ if (ret == -ENOIOCTLCMD || ret == -ENODEV)
return -EINVAL;
return ret;
}
@@ -1735,7 +1755,7 @@ vpif_query_dv_timings(struct file *file, void *priv,
int ret;
ret = v4l2_subdev_call(ch->sd, video, query_dv_timings, timings);
- if (ret == -ENOIOCTLCMD && ret == -ENODEV)
+ if (ret == -ENOIOCTLCMD || ret == -ENODEV)
return -ENODATA;
return ret;
}
diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c
index b716fbd4241..9f2b603be9c 100644
--- a/drivers/media/platform/davinci/vpif_display.c
+++ b/drivers/media/platform/davinci/vpif_display.c
@@ -177,11 +177,14 @@ static void vpif_buffer_queue(struct vb2_buffer *vb)
struct vpif_disp_buffer, vb);
struct channel_obj *ch = fh->channel;
struct common_obj *common;
+ unsigned long flags;
common = &ch->common[VPIF_VIDEO_INDEX];
/* add the buffer to the DMA queue */
+ spin_lock_irqsave(&common->irqlock, flags);
list_add_tail(&buf->list, &common->dma_queue);
+ spin_unlock_irqrestore(&common->irqlock, flags);
}
/*
@@ -246,10 +249,13 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
struct vpif_params *vpif = &ch->vpifparams;
unsigned long addr = 0;
+ unsigned long flags;
int ret;
/* If buffer queue is empty, return error */
+ spin_lock_irqsave(&common->irqlock, flags);
if (list_empty(&common->dma_queue)) {
+ spin_unlock_irqrestore(&common->irqlock, flags);
vpif_err("buffer queue is empty\n");
return -EIO;
}
@@ -260,6 +266,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
struct vpif_disp_buffer, list);
list_del(&common->cur_frm->list);
+ spin_unlock_irqrestore(&common->irqlock, flags);
/* Mark state of the current frame to active */
common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
@@ -330,6 +337,7 @@ static int vpif_stop_streaming(struct vb2_queue *vq)
struct vpif_fh *fh = vb2_get_drv_priv(vq);
struct channel_obj *ch = fh->channel;
struct common_obj *common;
+ unsigned long flags;
if (!vb2_is_streaming(vq))
return 0;
@@ -337,12 +345,14 @@ static int vpif_stop_streaming(struct vb2_queue *vq)
common = &ch->common[VPIF_VIDEO_INDEX];
/* release all active buffers */
+ spin_lock_irqsave(&common->irqlock, flags);
while (!list_empty(&common->dma_queue)) {
common->next_frm = list_entry(common->dma_queue.next,
struct vpif_disp_buffer, list);
list_del(&common->next_frm->list);
vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR);
}
+ spin_unlock_irqrestore(&common->irqlock, flags);
return 0;
}
@@ -363,11 +373,13 @@ static void process_progressive_mode(struct common_obj *common)
{
unsigned long addr = 0;
+ spin_lock(&common->irqlock);
/* Get the next buffer from buffer queue */
common->next_frm = list_entry(common->dma_queue.next,
struct vpif_disp_buffer, list);
/* Remove that buffer from the buffer queue */
list_del(&common->next_frm->list);
+ spin_unlock(&common->irqlock);
/* Mark status of the buffer as active */
common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
@@ -398,16 +410,18 @@ static void process_interlaced_mode(int fid, struct common_obj *common)
common->cur_frm = common->next_frm;
} else if (1 == fid) { /* odd field */
+ spin_lock(&common->irqlock);
if (list_empty(&common->dma_queue)
|| (common->cur_frm != common->next_frm)) {
+ spin_unlock(&common->irqlock);
return;
}
+ spin_unlock(&common->irqlock);
/* one field is displayed configure the next
* frame if it is available else hold on current
* frame */
/* Get next from the buffer queue */
process_progressive_mode(common);
-
}
}
@@ -437,8 +451,12 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
continue;
if (1 == ch->vpifparams.std_info.frm_fmt) {
- if (list_empty(&common->dma_queue))
+ spin_lock(&common->irqlock);
+ if (list_empty(&common->dma_queue)) {
+ spin_unlock(&common->irqlock);
continue;
+ }
+ spin_unlock(&common->irqlock);
/* Progressive mode */
if (!channel_first_int[i][channel_id]) {
@@ -972,9 +990,9 @@ static int vpif_reqbufs(struct file *file, void *priv,
}
/* Initialize videobuf2 queue as per the buffer type */
common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
- if (!common->alloc_ctx) {
+ if (IS_ERR(common->alloc_ctx)) {
vpif_err("Failed to get the context\n");
- return -EINVAL;
+ return PTR_ERR(common->alloc_ctx);
}
q = &common->buffer_queue;
q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
@@ -1380,7 +1398,7 @@ vpif_enum_dv_timings(struct file *file, void *priv,
int ret;
ret = v4l2_subdev_call(ch->sd, video, enum_dv_timings, timings);
- if (ret == -ENOIOCTLCMD && ret == -ENODEV)
+ if (ret == -ENOIOCTLCMD || ret == -ENODEV)
return -EINVAL;
return ret;
}
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c
index 19cbb12a12a..cc7b218d047 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.c
+++ b/drivers/media/platform/exynos-gsc/gsc-core.c
@@ -982,7 +982,7 @@ static void *gsc_get_drv_data(struct platform_device *pdev)
match = of_match_node(of_match_ptr(exynos_gsc_match),
pdev->dev.of_node);
if (match)
- driver_data = match->data;
+ driver_data = (struct gsc_driverdata *)match->data;
} else {
driver_data = (struct gsc_driverdata *)
platform_get_device_id(pdev)->driver_data;
diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c
index c065d040ed9..c267c57c76f 100644
--- a/drivers/media/platform/exynos-gsc/gsc-m2m.c
+++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c
@@ -122,7 +122,7 @@ static void gsc_m2m_device_run(void *priv)
struct gsc_ctx *ctx = priv;
struct gsc_dev *gsc;
unsigned long flags;
- u32 ret;
+ int ret;
bool is_set = false;
if (WARN(!ctx, "null hardware context\n"))
diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c
index 31ac4dc6924..a8ddb0cacab 100644
--- a/drivers/media/platform/fsl-viu.c
+++ b/drivers/media/platform/fsl-viu.c
@@ -352,8 +352,7 @@ static int restart_video_queue(struct viu_dmaqueue *vidq)
return 0;
buf = list_entry(vidq->queued.next, struct viu_buf, vb.queue);
if (prev == NULL) {
- list_del(&buf->vb.queue);
- list_add_tail(&buf->vb.queue, &vidq->active);
+ list_move_tail(&buf->vb.queue, &vidq->active);
dprintk(1, "Restarting video dma\n");
viu_stop_dma(vidq->dev);
@@ -367,8 +366,7 @@ static int restart_video_queue(struct viu_dmaqueue *vidq)
} else if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
- list_del(&buf->vb.queue);
- list_add_tail(&buf->vb.queue, &vidq->active);
+ list_move_tail(&buf->vb.queue, &vidq->active);
buf->vb.state = VIDEOBUF_ACTIVE;
dprintk(2, "[%p/%d] restart_queue - move to active\n",
buf, buf->vb.i);
diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c
index 45164c4f845..05c560f2ef0 100644
--- a/drivers/media/platform/m2m-deinterlace.c
+++ b/drivers/media/platform/m2m-deinterlace.c
@@ -218,15 +218,14 @@ static void dma_callback(void *data)
static void deinterlace_issue_dma(struct deinterlace_ctx *ctx, int op,
int do_callback)
{
- struct deinterlace_q_data *s_q_data, *d_q_data;
+ struct deinterlace_q_data *s_q_data;
struct vb2_buffer *src_buf, *dst_buf;
struct deinterlace_dev *pcdev = ctx->dev;
struct dma_chan *chan = pcdev->dma_chan;
struct dma_device *dmadev = chan->device;
struct dma_async_tx_descriptor *tx;
unsigned int s_width, s_height;
- unsigned int d_width, d_height;
- unsigned int d_size, s_size;
+ unsigned int s_size;
dma_addr_t p_in, p_out;
enum dma_ctrl_flags flags;
@@ -238,11 +237,6 @@ static void deinterlace_issue_dma(struct deinterlace_ctx *ctx, int op,
s_height = s_q_data->height;
s_size = s_width * s_height;
- d_q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_CAPTURE);
- d_width = d_q_data->width;
- d_height = d_q_data->height;
- d_size = d_width * d_height;
-
p_in = (dma_addr_t)vb2_dma_contig_plane_dma_addr(src_buf, 0);
p_out = (dma_addr_t)vb2_dma_contig_plane_dma_addr(dst_buf, 0);
if (!p_in || !p_out) {
@@ -1108,17 +1102,5 @@ static struct platform_driver deinterlace_pdrv = {
.owner = THIS_MODULE,
},
};
-
-static void __exit deinterlace_exit(void)
-{
- platform_driver_unregister(&deinterlace_pdrv);
-}
-
-static int __init deinterlace_init(void)
-{
- return platform_driver_register(&deinterlace_pdrv);
-}
-
-module_init(deinterlace_init);
-module_exit(deinterlace_exit);
+module_platform_driver(deinterlace_pdrv);
diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c
index 2e2121e9813..7487d7208de 100644
--- a/drivers/media/platform/mem2mem_testdev.c
+++ b/drivers/media/platform/mem2mem_testdev.c
@@ -839,7 +839,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds
int ret;
src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- src_vq->io_modes = VB2_MMAP;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
src_vq->drv_priv = ctx;
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->ops = &m2mtest_qops;
@@ -850,7 +850,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds
return ret;
dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- dst_vq->io_modes = VB2_MMAP;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
dst_vq->drv_priv = ctx;
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
dst_vq->ops = &m2mtest_qops;
diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c
index 8f22ce543cf..6b155d7be8e 100644
--- a/drivers/media/platform/mx2_emmaprp.c
+++ b/drivers/media/platform/mx2_emmaprp.c
@@ -371,7 +371,7 @@ static irqreturn_t emmaprp_irq(int irq_emma, void *data)
if (!curr_ctx->aborting) {
if ((irqst & PRP_INTR_ST_RDERR) ||
(irqst & PRP_INTR_ST_CH2WERR)) {
- pr_err("PrP bus error ocurred, this transfer is probably corrupted\n");
+ pr_err("PrP bus error occurred, this transfer is probably corrupted\n");
writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL);
} else if (irqst & PRP_INTR_ST_CH2B1CI) { /* buffer ready */
src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
@@ -1013,16 +1013,4 @@ static struct platform_driver emmaprp_pdrv = {
.owner = THIS_MODULE,
},
};
-
-static void __exit emmaprp_exit(void)
-{
- platform_driver_unregister(&emmaprp_pdrv);
-}
-
-static int __init emmaprp_init(void)
-{
- return platform_driver_register(&emmaprp_pdrv);
-}
-
-module_init(emmaprp_init);
-module_exit(emmaprp_exit);
+module_platform_driver(emmaprp_pdrv);
diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c
index 99350401596..35cc526e6c9 100644
--- a/drivers/media/platform/omap/omap_vout.c
+++ b/drivers/media/platform/omap/omap_vout.c
@@ -44,8 +44,6 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
-#include <plat/cpu.h>
-#include <linux/omap-dma.h>
#include <video/omapvrfb.h>
#include <video/omapdss.h>
@@ -1174,13 +1172,6 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *fh,
/* set default crop and win */
omap_vout_new_format(&vout->pix, &vout->fbuf, &vout->crop, &vout->win);
- /* Save the changes in the overlay strcuture */
- ret = omapvid_init(vout, 0);
- if (ret) {
- v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode\n");
- goto s_fmt_vid_out_exit;
- }
-
ret = 0;
s_fmt_vid_out_exit:
@@ -1684,20 +1675,6 @@ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
omap_dispc_register_isr(omap_vout_isr, vout, mask);
- for (j = 0; j < ovid->num_overlays; j++) {
- struct omap_overlay *ovl = ovid->overlays[j];
-
- if (ovl->get_device(ovl)) {
- struct omap_overlay_info info;
- ovl->get_overlay_info(ovl, &info);
- info.paddr = addr;
- if (ovl->set_overlay_info(ovl, &info)) {
- ret = -EINVAL;
- goto streamon_err1;
- }
- }
- }
-
/* First save the configuration in ovelray structure */
ret = omapvid_init(vout, addr);
if (ret)
@@ -2064,7 +2041,7 @@ static int __init omap_vout_create_video_devices(struct platform_device *pdev)
vout->vid_info.id = k + 1;
/* Set VRFB as rotation_type for omap2 and omap3 */
- if (cpu_is_omap24xx() || cpu_is_omap34xx())
+ if (omap_vout_dss_omap24xx() || omap_vout_dss_omap34xx())
vout->vid_info.rotation_type = VOUT_ROT_VRFB;
/* Setup the default configuration for the video devices
@@ -2094,11 +2071,12 @@ static int __init omap_vout_create_video_devices(struct platform_device *pdev)
}
video_set_drvdata(vfd, vout);
- /* Configure the overlay structure */
- ret = omapvid_init(vid_dev->vouts[k], 0);
- if (!ret)
- goto success;
+ dev_info(&pdev->dev, ": registered and initialized"
+ " video device %d\n", vfd->minor);
+ if (k == (pdev->num_resources - 1))
+ return 0;
+ continue;
error2:
if (vout->vid_info.rotation_type == VOUT_ROT_VRFB)
omap_vout_release_vrfb(vout);
@@ -2108,12 +2086,6 @@ error1:
error:
kfree(vout);
return ret;
-
-success:
- dev_info(&pdev->dev, ": registered and initialized"
- " video device %d\n", vfd->minor);
- if (k == (pdev->num_resources - 1))
- return 0;
}
return -ENODEV;
@@ -2186,14 +2158,23 @@ static int __init omap_vout_probe(struct platform_device *pdev)
struct omap_dss_device *def_display;
struct omap2video_device *vid_dev = NULL;
+ ret = omapdss_compat_init();
+ if (ret) {
+ dev_err(&pdev->dev, "failed to init dss\n");
+ return ret;
+ }
+
if (pdev->num_resources == 0) {
dev_err(&pdev->dev, "probed for an unknown device\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto err_dss_init;
}
vid_dev = kzalloc(sizeof(struct omap2video_device), GFP_KERNEL);
- if (vid_dev == NULL)
- return -ENOMEM;
+ if (vid_dev == NULL) {
+ ret = -ENOMEM;
+ goto err_dss_init;
+ }
vid_dev->num_displays = 0;
for_each_dss_dev(dssdev) {
@@ -2288,6 +2269,8 @@ probe_err1:
}
probe_err0:
kfree(vid_dev);
+err_dss_init:
+ omapdss_compat_uninit();
return ret;
}
diff --git a/drivers/media/platform/omap/omap_voutlib.c b/drivers/media/platform/omap/omap_voutlib.c
index 115408b9274..80b0d88f125 100644
--- a/drivers/media/platform/omap/omap_voutlib.c
+++ b/drivers/media/platform/omap/omap_voutlib.c
@@ -26,7 +26,7 @@
#include <linux/dma-mapping.h>
-#include <plat/cpu.h>
+#include <video/omapdss.h>
#include "omap_voutlib.h"
@@ -124,7 +124,7 @@ int omap_vout_new_window(struct v4l2_rect *crop,
win->chromakey = new_win->chromakey;
/* Adjust the cropping window to allow for resizing limitation */
- if (cpu_is_omap24xx()) {
+ if (omap_vout_dss_omap24xx()) {
/* For 24xx limit is 8x to 1/2x scaling. */
if ((crop->height/win->w.height) >= 2)
crop->height = win->w.height * 2;
@@ -140,7 +140,7 @@ int omap_vout_new_window(struct v4l2_rect *crop,
if (crop->height != win->w.height)
crop->width = 768;
}
- } else if (cpu_is_omap34xx()) {
+ } else if (omap_vout_dss_omap34xx()) {
/* For 34xx limit is 8x to 1/4x scaling. */
if ((crop->height/win->w.height) >= 4)
crop->height = win->w.height * 4;
@@ -196,7 +196,7 @@ int omap_vout_new_crop(struct v4l2_pix_format *pix,
if (try_crop.width <= 0 || try_crop.height <= 0)
return -EINVAL;
- if (cpu_is_omap24xx()) {
+ if (omap_vout_dss_omap24xx()) {
if (try_crop.height != win->w.height) {
/* If we're resizing vertically, we can't support a
* crop width wider than 768 pixels.
@@ -207,9 +207,9 @@ int omap_vout_new_crop(struct v4l2_pix_format *pix,
}
/* vertical resizing */
vresize = (1024 * try_crop.height) / win->w.height;
- if (cpu_is_omap24xx() && (vresize > 2048))
+ if (omap_vout_dss_omap24xx() && (vresize > 2048))
vresize = 2048;
- else if (cpu_is_omap34xx() && (vresize > 4096))
+ else if (omap_vout_dss_omap34xx() && (vresize > 4096))
vresize = 4096;
win->w.height = ((1024 * try_crop.height) / vresize) & ~1;
@@ -226,9 +226,9 @@ int omap_vout_new_crop(struct v4l2_pix_format *pix,
}
/* horizontal resizing */
hresize = (1024 * try_crop.width) / win->w.width;
- if (cpu_is_omap24xx() && (hresize > 2048))
+ if (omap_vout_dss_omap24xx() && (hresize > 2048))
hresize = 2048;
- else if (cpu_is_omap34xx() && (hresize > 4096))
+ else if (omap_vout_dss_omap34xx() && (hresize > 4096))
hresize = 4096;
win->w.width = ((1024 * try_crop.width) / hresize) & ~1;
@@ -243,7 +243,7 @@ int omap_vout_new_crop(struct v4l2_pix_format *pix,
if (try_crop.width == 0)
try_crop.width = 2;
}
- if (cpu_is_omap24xx()) {
+ if (omap_vout_dss_omap24xx()) {
if ((try_crop.height/win->w.height) >= 2)
try_crop.height = win->w.height * 2;
@@ -258,7 +258,7 @@ int omap_vout_new_crop(struct v4l2_pix_format *pix,
if (try_crop.height != win->w.height)
try_crop.width = 768;
}
- } else if (cpu_is_omap34xx()) {
+ } else if (omap_vout_dss_omap34xx()) {
if ((try_crop.height/win->w.height) >= 4)
try_crop.height = win->w.height * 4;
@@ -337,3 +337,21 @@ void omap_vout_free_buffer(unsigned long virtaddr, u32 buf_size)
}
free_pages((unsigned long) virtaddr, order);
}
+
+bool omap_vout_dss_omap24xx(void)
+{
+ return omapdss_get_version() == OMAPDSS_VER_OMAP24xx;
+}
+
+bool omap_vout_dss_omap34xx(void)
+{
+ switch (omapdss_get_version()) {
+ case OMAPDSS_VER_OMAP34xx_ES1:
+ case OMAPDSS_VER_OMAP34xx_ES3:
+ case OMAPDSS_VER_OMAP3630:
+ case OMAPDSS_VER_AM35xx:
+ return true;
+ default:
+ return false;
+ }
+}
diff --git a/drivers/media/platform/omap/omap_voutlib.h b/drivers/media/platform/omap/omap_voutlib.h
index e51750a597e..f9d1c0779f3 100644
--- a/drivers/media/platform/omap/omap_voutlib.h
+++ b/drivers/media/platform/omap/omap_voutlib.h
@@ -32,5 +32,8 @@ void omap_vout_new_format(struct v4l2_pix_format *pix,
struct v4l2_window *win);
unsigned long omap_vout_alloc_buffer(u32 buf_size, u32 *phys_addr);
void omap_vout_free_buffer(unsigned long virtaddr, u32 buf_size);
+
+bool omap_vout_dss_omap24xx(void);
+bool omap_vout_dss_omap34xx(void);
#endif /* #ifndef OMAP_VOUTLIB_H */
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index 7f182f0ff3d..a9f6de5b69d 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -103,7 +103,8 @@ static const struct isp_res_mapping isp_res_maps[] = {
1 << OMAP3_ISP_IOMEM_RESZ |
1 << OMAP3_ISP_IOMEM_SBL |
1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 |
- 1 << OMAP3_ISP_IOMEM_CSIPHY2,
+ 1 << OMAP3_ISP_IOMEM_CSIPHY2 |
+ 1 << OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE,
},
{
.isp_rev = ISP_REVISION_15_0,
@@ -120,7 +121,8 @@ static const struct isp_res_mapping isp_res_maps[] = {
1 << OMAP3_ISP_IOMEM_CSI2A_REGS2 |
1 << OMAP3_ISP_IOMEM_CSI2C_REGS1 |
1 << OMAP3_ISP_IOMEM_CSIPHY1 |
- 1 << OMAP3_ISP_IOMEM_CSI2C_REGS2,
+ 1 << OMAP3_ISP_IOMEM_CSI2C_REGS2 |
+ 1 << OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL,
},
};
@@ -1331,7 +1333,8 @@ void omap3isp_subclk_disable(struct isp_device *isp,
* isp_enable_clocks - Enable ISP clocks
* @isp: OMAP3 ISP device
*
- * Return 0 if successful, or clk_enable return value if any of tthem fails.
+ * Return 0 if successful, or clk_prepare_enable return value if any of them
+ * fails.
*/
static int isp_enable_clocks(struct isp_device *isp)
{
@@ -1348,14 +1351,11 @@ static int isp_enable_clocks(struct isp_device *isp)
* has to be twice of what is set on OMAP3430 to get
* the required value for cam_mclk
*/
- if (cpu_is_omap3630())
- divisor = 1;
- else
- divisor = 2;
+ divisor = isp->revision == ISP_REVISION_15_0 ? 1 : 2;
- r = clk_enable(isp->clock[ISP_CLK_CAM_ICK]);
+ r = clk_prepare_enable(isp->clock[ISP_CLK_CAM_ICK]);
if (r) {
- dev_err(isp->dev, "clk_enable cam_ick failed\n");
+ dev_err(isp->dev, "failed to enable cam_ick clock\n");
goto out_clk_enable_ick;
}
r = clk_set_rate(isp->clock[ISP_CLK_DPLL4_M5_CK],
@@ -1364,9 +1364,9 @@ static int isp_enable_clocks(struct isp_device *isp)
dev_err(isp->dev, "clk_set_rate for dpll4_m5_ck failed\n");
goto out_clk_enable_mclk;
}
- r = clk_enable(isp->clock[ISP_CLK_CAM_MCLK]);
+ r = clk_prepare_enable(isp->clock[ISP_CLK_CAM_MCLK]);
if (r) {
- dev_err(isp->dev, "clk_enable cam_mclk failed\n");
+ dev_err(isp->dev, "failed to enable cam_mclk clock\n");
goto out_clk_enable_mclk;
}
rate = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]);
@@ -1374,17 +1374,17 @@ static int isp_enable_clocks(struct isp_device *isp)
dev_warn(isp->dev, "unexpected cam_mclk rate:\n"
" expected : %d\n"
" actual : %ld\n", CM_CAM_MCLK_HZ, rate);
- r = clk_enable(isp->clock[ISP_CLK_CSI2_FCK]);
+ r = clk_prepare_enable(isp->clock[ISP_CLK_CSI2_FCK]);
if (r) {
- dev_err(isp->dev, "clk_enable csi2_fck failed\n");
+ dev_err(isp->dev, "failed to enable csi2_fck clock\n");
goto out_clk_enable_csi2_fclk;
}
return 0;
out_clk_enable_csi2_fclk:
- clk_disable(isp->clock[ISP_CLK_CAM_MCLK]);
+ clk_disable_unprepare(isp->clock[ISP_CLK_CAM_MCLK]);
out_clk_enable_mclk:
- clk_disable(isp->clock[ISP_CLK_CAM_ICK]);
+ clk_disable_unprepare(isp->clock[ISP_CLK_CAM_ICK]);
out_clk_enable_ick:
return r;
}
@@ -1395,9 +1395,9 @@ out_clk_enable_ick:
*/
static void isp_disable_clocks(struct isp_device *isp)
{
- clk_disable(isp->clock[ISP_CLK_CAM_ICK]);
- clk_disable(isp->clock[ISP_CLK_CAM_MCLK]);
- clk_disable(isp->clock[ISP_CLK_CSI2_FCK]);
+ clk_disable_unprepare(isp->clock[ISP_CLK_CAM_ICK]);
+ clk_disable_unprepare(isp->clock[ISP_CLK_CAM_MCLK]);
+ clk_disable_unprepare(isp->clock[ISP_CLK_CSI2_FCK]);
}
static const char *isp_clocks[] = {
@@ -1678,7 +1678,7 @@ isp_register_subdev_group(struct isp_device *isp,
adapter = i2c_get_adapter(board_info->i2c_adapter_id);
if (adapter == NULL) {
- printk(KERN_ERR "%s: Unable to get I2C adapter %d for "
+ dev_err(isp->dev, "%s: Unable to get I2C adapter %d for "
"device %s\n", __func__,
board_info->i2c_adapter_id,
board_info->board_info->type);
@@ -1688,7 +1688,7 @@ isp_register_subdev_group(struct isp_device *isp,
subdev = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter,
board_info->board_info, NULL);
if (subdev == NULL) {
- printk(KERN_ERR "%s: Unable to register subdev %s\n",
+ dev_err(isp->dev, "%s: Unable to register subdev %s\n",
__func__, board_info->board_info->type);
continue;
}
@@ -1713,7 +1713,7 @@ static int isp_register_entities(struct isp_device *isp)
isp->media_dev.link_notify = isp_pipeline_link_notify;
ret = media_device_register(&isp->media_dev);
if (ret < 0) {
- printk(KERN_ERR "%s: Media device registration failed (%d)\n",
+ dev_err(isp->dev, "%s: Media device registration failed (%d)\n",
__func__, ret);
return ret;
}
@@ -1721,7 +1721,7 @@ static int isp_register_entities(struct isp_device *isp)
isp->v4l2_dev.mdev = &isp->media_dev;
ret = v4l2_device_register(isp->dev, &isp->v4l2_dev);
if (ret < 0) {
- printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n",
+ dev_err(isp->dev, "%s: V4L2 device registration failed (%d)\n",
__func__, ret);
goto done;
}
@@ -1766,6 +1766,7 @@ static int isp_register_entities(struct isp_device *isp)
struct media_entity *input;
unsigned int flags;
unsigned int pad;
+ unsigned int i;
sensor = isp_register_subdev_group(isp, subdevs->subdevs);
if (sensor == NULL)
@@ -1807,13 +1808,25 @@ static int isp_register_entities(struct isp_device *isp)
break;
default:
- printk(KERN_ERR "%s: invalid interface type %u\n",
- __func__, subdevs->interface);
+ dev_err(isp->dev, "%s: invalid interface type %u\n",
+ __func__, subdevs->interface);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ for (i = 0; i < sensor->entity.num_pads; i++) {
+ if (sensor->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE)
+ break;
+ }
+ if (i == sensor->entity.num_pads) {
+ dev_err(isp->dev,
+ "%s: no source pad in external entity\n",
+ __func__);
ret = -EINVAL;
goto done;
}
- ret = media_entity_create_link(&sensor->entity, 0, input, pad,
+ ret = media_entity_create_link(&sensor->entity, i, input, pad,
flags);
if (ret < 0)
goto done;
@@ -2096,7 +2109,11 @@ static int __devinit isp_probe(struct platform_device *pdev)
isp->isp_csiphy1.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY1");
isp->isp_csiphy2.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY2");
- /* Clocks */
+ /* Clocks
+ *
+ * The ISP clock tree is revision-dependent. We thus need to enable ICLK
+ * manually to read the revision before calling __omap3isp_get().
+ */
ret = isp_map_mem_resource(pdev, isp, OMAP3_ISP_IOMEM_MAIN);
if (ret < 0)
goto error;
@@ -2105,6 +2122,16 @@ static int __devinit isp_probe(struct platform_device *pdev)
if (ret < 0)
goto error;
+ ret = clk_enable(isp->clock[ISP_CLK_CAM_ICK]);
+ if (ret < 0)
+ goto error;
+
+ isp->revision = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
+ dev_info(isp->dev, "Revision %d.%d found\n",
+ (isp->revision & 0xf0) >> 4, isp->revision & 0x0f);
+
+ clk_disable(isp->clock[ISP_CLK_CAM_ICK]);
+
if (__omap3isp_get(isp, false) == NULL) {
ret = -ENODEV;
goto error;
@@ -2115,10 +2142,6 @@ static int __devinit isp_probe(struct platform_device *pdev)
goto error_isp;
/* Memory resources */
- isp->revision = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
- dev_info(isp->dev, "Revision %d.%d found\n",
- (isp->revision & 0xf0) >> 4, isp->revision & 0x0f);
-
for (m = 0; m < ARRAY_SIZE(isp_res_maps); m++)
if (isp->revision == isp_res_maps[m].isp_rev)
break;
diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h
index 8d6866942b8..517d348ce32 100644
--- a/drivers/media/platform/omap3isp/isp.h
+++ b/drivers/media/platform/omap3isp/isp.h
@@ -70,6 +70,8 @@ enum isp_mem_resources {
OMAP3_ISP_IOMEM_CSI2C_REGS1,
OMAP3_ISP_IOMEM_CSIPHY1,
OMAP3_ISP_IOMEM_CSI2C_REGS2,
+ OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE,
+ OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL,
OMAP3_ISP_IOMEM_LAST
};
@@ -125,9 +127,6 @@ struct isp_reg {
struct isp_platform_callback {
u32 (*set_xclk)(struct isp_device *isp, u32 xclk, u8 xclksel);
- int (*csiphy_config)(struct isp_csiphy *phy,
- struct isp_csiphy_dphy_cfg *dphy,
- struct isp_csiphy_lanes_cfg *lanes);
};
/*
diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c
index 6a3ff792af7..783f4b05b15 100644
--- a/drivers/media/platform/omap3isp/ispcsi2.c
+++ b/drivers/media/platform/omap3isp/ispcsi2.c
@@ -517,7 +517,7 @@ int omap3isp_csi2_reset(struct isp_csi2_device *csi2)
} while (soft_reset_retries < 5);
if (soft_reset_retries == 5) {
- printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n");
+ dev_err(isp->dev, "CSI2: Soft reset try count exceeded!\n");
return -EBUSY;
}
@@ -535,8 +535,8 @@ int omap3isp_csi2_reset(struct isp_csi2_device *csi2)
} while (--i > 0);
if (i == 0) {
- printk(KERN_ERR
- "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n");
+ dev_err(isp->dev,
+ "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n");
return -EBUSY;
}
diff --git a/drivers/media/platform/omap3isp/ispcsiphy.c b/drivers/media/platform/omap3isp/ispcsiphy.c
index 348f67ebbbc..3d56b33f85e 100644
--- a/drivers/media/platform/omap3isp/ispcsiphy.c
+++ b/drivers/media/platform/omap3isp/ispcsiphy.c
@@ -32,34 +32,92 @@
#include "ispreg.h"
#include "ispcsiphy.h"
-/*
- * csiphy_lanes_config - Configuration of CSIPHY lanes.
- *
- * Updates HW configuration.
- * Called with phy->mutex taken.
- */
-static void csiphy_lanes_config(struct isp_csiphy *phy)
+static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, u32 iface,
+ bool ccp2_strobe)
{
- unsigned int i;
- u32 reg;
+ u32 reg = isp_reg_readl(
+ phy->isp, OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL, 0);
+ u32 shift, mode;
+
+ switch (iface) {
+ case ISP_INTERFACE_CCP2B_PHY1:
+ reg &= ~OMAP3630_CONTROL_CAMERA_PHY_CTRL_CSI1_RX_SEL_PHY2;
+ shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY1_SHIFT;
+ break;
+ case ISP_INTERFACE_CSI2C_PHY1:
+ shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY1_SHIFT;
+ mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_DPHY;
+ break;
+ case ISP_INTERFACE_CCP2B_PHY2:
+ reg |= OMAP3630_CONTROL_CAMERA_PHY_CTRL_CSI1_RX_SEL_PHY2;
+ shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY2_SHIFT;
+ break;
+ case ISP_INTERFACE_CSI2A_PHY2:
+ shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY2_SHIFT;
+ mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_DPHY;
+ break;
+ }
- reg = isp_reg_readl(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG);
+ /* Select data/clock or data/strobe mode for CCP2 */
+ switch (iface) {
+ case ISP_INTERFACE_CCP2B_PHY1:
+ case ISP_INTERFACE_CCP2B_PHY2:
+ if (ccp2_strobe)
+ mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_CCP2_DATA_STROBE;
+ else
+ mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_CCP2_DATA_CLOCK;
+ }
- for (i = 0; i < phy->num_data_lanes; i++) {
- reg &= ~(ISPCSI2_PHY_CFG_DATA_POL_MASK(i + 1) |
- ISPCSI2_PHY_CFG_DATA_POSITION_MASK(i + 1));
- reg |= (phy->lanes.data[i].pol <<
- ISPCSI2_PHY_CFG_DATA_POL_SHIFT(i + 1));
- reg |= (phy->lanes.data[i].pos <<
- ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(i + 1));
+ reg &= ~(OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_MASK << shift);
+ reg |= mode << shift;
+
+ isp_reg_writel(phy->isp, reg,
+ OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL, 0);
+}
+
+static void csiphy_routing_cfg_3430(struct isp_csiphy *phy, u32 iface, bool on,
+ bool ccp2_strobe)
+{
+ u32 csirxfe = OMAP343X_CONTROL_CSIRXFE_PWRDNZ
+ | OMAP343X_CONTROL_CSIRXFE_RESET;
+
+ /* Only the CCP2B on PHY1 is configurable. */
+ if (iface != ISP_INTERFACE_CCP2B_PHY1)
+ return;
+
+ if (!on) {
+ isp_reg_writel(phy->isp, 0,
+ OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE, 0);
+ return;
}
- reg &= ~(ISPCSI2_PHY_CFG_CLOCK_POL_MASK |
- ISPCSI2_PHY_CFG_CLOCK_POSITION_MASK);
- reg |= phy->lanes.clk.pol << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT;
- reg |= phy->lanes.clk.pos << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT;
+ if (ccp2_strobe)
+ csirxfe |= OMAP343X_CONTROL_CSIRXFE_SELFORM;
- isp_reg_writel(phy->isp, reg, phy->cfg_regs, ISPCSI2_PHY_CFG);
+ isp_reg_writel(phy->isp, csirxfe,
+ OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE, 0);
+}
+
+/*
+ * Configure OMAP 3 CSI PHY routing.
+ * @phy: relevant phy device
+ * @iface: ISP_INTERFACE_*
+ * @on: power on or off
+ * @ccp2_strobe: false: data/clock, true: data/strobe
+ *
+ * Note that the underlying routing configuration registers are part of the
+ * control (SCM) register space and part of the CORE power domain on both 3430
+ * and 3630, so they will not hold their contents in off-mode. This isn't an
+ * issue since the MPU power domain is forced on whilst the ISP is in use.
+ */
+static void csiphy_routing_cfg(struct isp_csiphy *phy, u32 iface, bool on,
+ bool ccp2_strobe)
+{
+ if (phy->isp->mmio_base[OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL]
+ && on)
+ return csiphy_routing_cfg_3630(phy, iface, ccp2_strobe);
+ if (phy->isp->mmio_base[OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE])
+ return csiphy_routing_cfg_3430(phy, iface, on, ccp2_strobe);
}
/*
@@ -99,7 +157,7 @@ static int csiphy_set_power(struct isp_csiphy *phy, u32 power)
} while ((reg != power >> 2) && (retry_count < 100));
if (retry_count == 100) {
- printk(KERN_ERR "CSI2 CIO set power failed!\n");
+ dev_err(phy->isp->dev, "CSI2 CIO set power failed!\n");
return -EBUSY;
}
@@ -107,43 +165,28 @@ static int csiphy_set_power(struct isp_csiphy *phy, u32 power)
}
/*
- * csiphy_dphy_config - Configure CSI2 D-PHY parameters.
- *
- * Called with phy->mutex taken.
+ * TCLK values are OK at their reset values
*/
-static void csiphy_dphy_config(struct isp_csiphy *phy)
-{
- u32 reg;
-
- /* Set up ISPCSIPHY_REG0 */
- reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG0);
-
- reg &= ~(ISPCSIPHY_REG0_THS_TERM_MASK |
- ISPCSIPHY_REG0_THS_SETTLE_MASK);
- reg |= phy->dphy.ths_term << ISPCSIPHY_REG0_THS_TERM_SHIFT;
- reg |= phy->dphy.ths_settle << ISPCSIPHY_REG0_THS_SETTLE_SHIFT;
-
- isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG0);
-
- /* Set up ISPCSIPHY_REG1 */
- reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG1);
-
- reg &= ~(ISPCSIPHY_REG1_TCLK_TERM_MASK |
- ISPCSIPHY_REG1_TCLK_MISS_MASK |
- ISPCSIPHY_REG1_TCLK_SETTLE_MASK);
- reg |= phy->dphy.tclk_term << ISPCSIPHY_REG1_TCLK_TERM_SHIFT;
- reg |= phy->dphy.tclk_miss << ISPCSIPHY_REG1_TCLK_MISS_SHIFT;
- reg |= phy->dphy.tclk_settle << ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT;
-
- isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG1);
-}
+#define TCLK_TERM 0
+#define TCLK_MISS 1
+#define TCLK_SETTLE 14
-static int csiphy_config(struct isp_csiphy *phy,
- struct isp_csiphy_dphy_cfg *dphy,
- struct isp_csiphy_lanes_cfg *lanes)
+static int omap3isp_csiphy_config(struct isp_csiphy *phy)
{
+ struct isp_csi2_device *csi2 = phy->csi2;
+ struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity);
+ struct isp_v4l2_subdevs_group *subdevs = pipe->external->host_priv;
+ struct isp_csiphy_lanes_cfg *lanes;
+ int csi2_ddrclk_khz;
unsigned int used_lanes = 0;
unsigned int i;
+ u32 reg;
+
+ if (subdevs->interface == ISP_INTERFACE_CCP2B_PHY1
+ || subdevs->interface == ISP_INTERFACE_CCP2B_PHY2)
+ lanes = &subdevs->bus.ccp2.lanecfg;
+ else
+ lanes = &subdevs->bus.csi2.lanecfg;
/* Clock and data lanes verification */
for (i = 0; i < phy->num_data_lanes; i++) {
@@ -162,10 +205,61 @@ static int csiphy_config(struct isp_csiphy *phy,
if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos))
return -EINVAL;
- mutex_lock(&phy->mutex);
- phy->dphy = *dphy;
- phy->lanes = *lanes;
- mutex_unlock(&phy->mutex);
+ /*
+ * The PHY configuration is lost in off mode, that's not an
+ * issue since the MPU power domain is forced on whilst the
+ * ISP is in use.
+ */
+ csiphy_routing_cfg(phy, subdevs->interface, true,
+ subdevs->bus.ccp2.phy_layer);
+
+ /* DPHY timing configuration */
+ /* CSI-2 is DDR and we only count used lanes. */
+ csi2_ddrclk_khz = pipe->external_rate / 1000
+ / (2 * hweight32(used_lanes)) * pipe->external_width;
+
+ reg = isp_reg_readl(csi2->isp, phy->phy_regs, ISPCSIPHY_REG0);
+
+ reg &= ~(ISPCSIPHY_REG0_THS_TERM_MASK |
+ ISPCSIPHY_REG0_THS_SETTLE_MASK);
+ /* THS_TERM: Programmed value = ceil(12.5 ns/DDRClk period) - 1. */
+ reg |= (DIV_ROUND_UP(25 * csi2_ddrclk_khz, 2000000) - 1)
+ << ISPCSIPHY_REG0_THS_TERM_SHIFT;
+ /* THS_SETTLE: Programmed value = ceil(90 ns/DDRClk period) + 3. */
+ reg |= (DIV_ROUND_UP(90 * csi2_ddrclk_khz, 1000000) + 3)
+ << ISPCSIPHY_REG0_THS_SETTLE_SHIFT;
+
+ isp_reg_writel(csi2->isp, reg, phy->phy_regs, ISPCSIPHY_REG0);
+
+ reg = isp_reg_readl(csi2->isp, phy->phy_regs, ISPCSIPHY_REG1);
+
+ reg &= ~(ISPCSIPHY_REG1_TCLK_TERM_MASK |
+ ISPCSIPHY_REG1_TCLK_MISS_MASK |
+ ISPCSIPHY_REG1_TCLK_SETTLE_MASK);
+ reg |= TCLK_TERM << ISPCSIPHY_REG1_TCLK_TERM_SHIFT;
+ reg |= TCLK_MISS << ISPCSIPHY_REG1_TCLK_MISS_SHIFT;
+ reg |= TCLK_SETTLE << ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT;
+
+ isp_reg_writel(csi2->isp, reg, phy->phy_regs, ISPCSIPHY_REG1);
+
+ /* DPHY lane configuration */
+ reg = isp_reg_readl(csi2->isp, phy->cfg_regs, ISPCSI2_PHY_CFG);
+
+ for (i = 0; i < phy->num_data_lanes; i++) {
+ reg &= ~(ISPCSI2_PHY_CFG_DATA_POL_MASK(i + 1) |
+ ISPCSI2_PHY_CFG_DATA_POSITION_MASK(i + 1));
+ reg |= (lanes->data[i].pol <<
+ ISPCSI2_PHY_CFG_DATA_POL_SHIFT(i + 1));
+ reg |= (lanes->data[i].pos <<
+ ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(i + 1));
+ }
+
+ reg &= ~(ISPCSI2_PHY_CFG_CLOCK_POL_MASK |
+ ISPCSI2_PHY_CFG_CLOCK_POSITION_MASK);
+ reg |= lanes->clk.pol << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT;
+ reg |= lanes->clk.pos << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT;
+
+ isp_reg_writel(csi2->isp, reg, phy->cfg_regs, ISPCSI2_PHY_CFG);
return 0;
}
@@ -190,8 +284,9 @@ int omap3isp_csiphy_acquire(struct isp_csiphy *phy)
if (rval < 0)
goto done;
- csiphy_dphy_config(phy);
- csiphy_lanes_config(phy);
+ rval = omap3isp_csiphy_config(phy);
+ if (rval < 0)
+ goto done;
rval = csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_ON);
if (rval) {
@@ -211,6 +306,14 @@ void omap3isp_csiphy_release(struct isp_csiphy *phy)
{
mutex_lock(&phy->mutex);
if (phy->phy_in_use) {
+ struct isp_csi2_device *csi2 = phy->csi2;
+ struct isp_pipeline *pipe =
+ to_isp_pipeline(&csi2->subdev.entity);
+ struct isp_v4l2_subdevs_group *subdevs =
+ pipe->external->host_priv;
+
+ csiphy_routing_cfg(phy, subdevs->interface, false,
+ subdevs->bus.ccp2.phy_layer);
csiphy_power_autoswitch_enable(phy, false);
csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_OFF);
regulator_disable(phy->vdd);
@@ -227,8 +330,6 @@ int omap3isp_csiphy_init(struct isp_device *isp)
struct isp_csiphy *phy1 = &isp->isp_csiphy1;
struct isp_csiphy *phy2 = &isp->isp_csiphy2;
- isp->platform_cb.csiphy_config = csiphy_config;
-
phy2->isp = isp;
phy2->csi2 = &isp->isp_csi2a;
phy2->num_data_lanes = ISP_CSIPHY2_NUM_DATA_LANES;
diff --git a/drivers/media/platform/omap3isp/ispcsiphy.h b/drivers/media/platform/omap3isp/ispcsiphy.h
index e93a661e65d..14551fd7769 100644
--- a/drivers/media/platform/omap3isp/ispcsiphy.h
+++ b/drivers/media/platform/omap3isp/ispcsiphy.h
@@ -32,14 +32,6 @@
struct isp_csi2_device;
struct regulator;
-struct isp_csiphy_dphy_cfg {
- u8 ths_term;
- u8 ths_settle;
- u8 tclk_term;
- unsigned tclk_miss:1;
- u8 tclk_settle;
-};
-
struct isp_csiphy {
struct isp_device *isp;
struct mutex mutex; /* serialize csiphy configuration */
@@ -52,8 +44,6 @@ struct isp_csiphy {
unsigned int phy_regs;
u8 num_data_lanes; /* number of CSI2 Data Lanes supported */
- struct isp_csiphy_lanes_cfg lanes;
- struct isp_csiphy_dphy_cfg dphy;
};
int omap3isp_csiphy_acquire(struct isp_csiphy *phy);
diff --git a/drivers/media/platform/omap3isp/isphist.c b/drivers/media/platform/omap3isp/isphist.c
index e7f9c4292cc..2d759c56f37 100644
--- a/drivers/media/platform/omap3isp/isphist.c
+++ b/drivers/media/platform/omap3isp/isphist.c
@@ -74,11 +74,14 @@ static void hist_reset_mem(struct ispstat *hist)
static void hist_dma_config(struct ispstat *hist)
{
+ struct isp_device *isp = hist->isp;
+
hist->dma_config.data_type = OMAP_DMA_DATA_TYPE_S32;
hist->dma_config.sync_mode = OMAP_DMA_SYNC_ELEMENT;
hist->dma_config.frame_count = 1;
hist->dma_config.src_amode = OMAP_DMA_AMODE_CONSTANT;
- hist->dma_config.src_start = OMAP3ISP_HIST_REG_BASE + ISPHIST_DATA;
+ hist->dma_config.src_start = isp->mmio_base_phys[OMAP3_ISP_IOMEM_HIST]
+ + ISPHIST_DATA;
hist->dma_config.dst_amode = OMAP_DMA_AMODE_POST_INC;
hist->dma_config.src_or_dst_synch = OMAP_DMA_SRC_SYNC;
}
@@ -479,6 +482,8 @@ int omap3isp_hist_init(struct isp_device *isp)
return -ENOMEM;
memset(hist, 0, sizeof(*hist));
+ hist->isp = isp;
+
if (HIST_CONFIG_DMA)
ret = omap_request_dma(OMAP24XX_DMA_NO_DEVICE, "DMA_ISP_HIST",
hist_dma_cb, hist, &hist->dma_ch);
@@ -496,7 +501,6 @@ int omap3isp_hist_init(struct isp_device *isp)
hist->ops = &hist_ops;
hist->priv = hist_cfg;
hist->event_type = V4L2_EVENT_OMAP3ISP_HIST;
- hist->isp = isp;
ret = omap3isp_stat_init(hist, "histogram", &hist_subdev_ops);
if (ret) {
diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c
index 1ae1c0909ed..691b92a3c3e 100644
--- a/drivers/media/platform/omap3isp/isppreview.c
+++ b/drivers/media/platform/omap3isp/isppreview.c
@@ -200,10 +200,10 @@ static void preview_enable_invalaw(struct isp_prev_device *prev, bool enable)
if (enable)
isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW);
+ ISPPRV_PCR_INVALAW);
else
isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW);
+ ISPPRV_PCR_INVALAW);
}
/*
@@ -1014,7 +1014,7 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average)
/*
* preview_config_input_format - Configure the input format
* @prev: The preview engine
- * @format: Format on the preview engine sink pad
+ * @info: Sink pad format information
*
* Enable and configure CFA interpolation for Bayer formats and disable it for
* greyscale formats.
@@ -1025,22 +1025,29 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average)
* reordered to support non-GRBG Bayer patterns.
*/
static void preview_config_input_format(struct isp_prev_device *prev,
- const struct v4l2_mbus_framefmt *format)
+ const struct isp_format_info *info)
{
struct isp_device *isp = to_isp_device(prev);
struct prev_params *params;
- switch (format->code) {
- case V4L2_MBUS_FMT_SGRBG10_1X10:
+ if (info->width == 8)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_WIDTH);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_WIDTH);
+
+ switch (info->flavor) {
+ case V4L2_MBUS_FMT_SGRBG8_1X8:
prev->params.cfa_order = 0;
break;
- case V4L2_MBUS_FMT_SRGGB10_1X10:
+ case V4L2_MBUS_FMT_SRGGB8_1X8:
prev->params.cfa_order = 1;
break;
- case V4L2_MBUS_FMT_SBGGR10_1X10:
+ case V4L2_MBUS_FMT_SBGGR8_1X8:
prev->params.cfa_order = 2;
break;
- case V4L2_MBUS_FMT_SGBRG10_1X10:
+ case V4L2_MBUS_FMT_SGBRG8_1X8:
prev->params.cfa_order = 3;
break;
default:
@@ -1081,7 +1088,8 @@ static void preview_config_input_size(struct isp_prev_device *prev, u32 active)
unsigned int elv = prev->crop.top + prev->crop.height - 1;
u32 features;
- if (format->code != V4L2_MBUS_FMT_Y10_1X10) {
+ if (format->code != V4L2_MBUS_FMT_Y8_1X8 &&
+ format->code != V4L2_MBUS_FMT_Y10_1X10) {
sph -= 2;
eph += 2;
slv -= 2;
@@ -1389,6 +1397,7 @@ static unsigned int preview_max_out_width(struct isp_prev_device *prev)
static void preview_configure(struct isp_prev_device *prev)
{
struct isp_device *isp = to_isp_device(prev);
+ const struct isp_format_info *info;
struct v4l2_mbus_framefmt *format;
unsigned long flags;
u32 update;
@@ -1402,17 +1411,18 @@ static void preview_configure(struct isp_prev_device *prev)
/* PREV_PAD_SINK */
format = &prev->formats[PREV_PAD_SINK];
+ info = omap3isp_video_format_info(format->code);
preview_adjust_bandwidth(prev);
- preview_config_input_format(prev, format);
+ preview_config_input_format(prev, info);
preview_config_input_size(prev, active);
if (prev->input == PREVIEW_INPUT_CCDC)
preview_config_inlineoffset(prev, 0);
else
- preview_config_inlineoffset(prev,
- ALIGN(format->width, 0x20) * 2);
+ preview_config_inlineoffset(prev, ALIGN(format->width, 0x20) *
+ info->bpp);
preview_setup_hw(prev, update, active);
@@ -1709,6 +1719,11 @@ __preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
/* previewer format descriptions */
static const unsigned int preview_input_fmts[] = {
+ V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_MBUS_FMT_SGBRG8_1X8,
V4L2_MBUS_FMT_Y10_1X10,
V4L2_MBUS_FMT_SGRBG10_1X10,
V4L2_MBUS_FMT_SRGGB10_1X10,
diff --git a/drivers/media/platform/omap3isp/ispreg.h b/drivers/media/platform/omap3isp/ispreg.h
index e2c57f334c5..b7d90e6fb01 100644
--- a/drivers/media/platform/omap3isp/ispreg.h
+++ b/drivers/media/platform/omap3isp/ispreg.h
@@ -29,83 +29,6 @@
#define CM_CAM_MCLK_HZ 172800000 /* Hz */
-/* ISP Submodules offset */
-
-#define L4_34XX_BASE 0x48000000
-#define OMAP3430_ISP_BASE (L4_34XX_BASE + 0xBC000)
-
-#define OMAP3ISP_REG_BASE OMAP3430_ISP_BASE
-#define OMAP3ISP_REG(offset) (OMAP3ISP_REG_BASE + (offset))
-
-#define OMAP3ISP_CCP2_REG_OFFSET 0x0400
-#define OMAP3ISP_CCP2_REG_BASE (OMAP3ISP_REG_BASE + \
- OMAP3ISP_CCP2_REG_OFFSET)
-#define OMAP3ISP_CCP2_REG(offset) (OMAP3ISP_CCP2_REG_BASE + (offset))
-
-#define OMAP3ISP_CCDC_REG_OFFSET 0x0600
-#define OMAP3ISP_CCDC_REG_BASE (OMAP3ISP_REG_BASE + \
- OMAP3ISP_CCDC_REG_OFFSET)
-#define OMAP3ISP_CCDC_REG(offset) (OMAP3ISP_CCDC_REG_BASE + (offset))
-
-#define OMAP3ISP_HIST_REG_OFFSET 0x0A00
-#define OMAP3ISP_HIST_REG_BASE (OMAP3ISP_REG_BASE + \
- OMAP3ISP_HIST_REG_OFFSET)
-#define OMAP3ISP_HIST_REG(offset) (OMAP3ISP_HIST_REG_BASE + (offset))
-
-#define OMAP3ISP_H3A_REG_OFFSET 0x0C00
-#define OMAP3ISP_H3A_REG_BASE (OMAP3ISP_REG_BASE + \
- OMAP3ISP_H3A_REG_OFFSET)
-#define OMAP3ISP_H3A_REG(offset) (OMAP3ISP_H3A_REG_BASE + (offset))
-
-#define OMAP3ISP_PREV_REG_OFFSET 0x0E00
-#define OMAP3ISP_PREV_REG_BASE (OMAP3ISP_REG_BASE + \
- OMAP3ISP_PREV_REG_OFFSET)
-#define OMAP3ISP_PREV_REG(offset) (OMAP3ISP_PREV_REG_BASE + (offset))
-
-#define OMAP3ISP_RESZ_REG_OFFSET 0x1000
-#define OMAP3ISP_RESZ_REG_BASE (OMAP3ISP_REG_BASE + \
- OMAP3ISP_RESZ_REG_OFFSET)
-#define OMAP3ISP_RESZ_REG(offset) (OMAP3ISP_RESZ_REG_BASE + (offset))
-
-#define OMAP3ISP_SBL_REG_OFFSET 0x1200
-#define OMAP3ISP_SBL_REG_BASE (OMAP3ISP_REG_BASE + \
- OMAP3ISP_SBL_REG_OFFSET)
-#define OMAP3ISP_SBL_REG(offset) (OMAP3ISP_SBL_REG_BASE + (offset))
-
-#define OMAP3ISP_CSI2A_REGS1_REG_OFFSET 0x1800
-#define OMAP3ISP_CSI2A_REGS1_REG_BASE (OMAP3ISP_REG_BASE + \
- OMAP3ISP_CSI2A_REGS1_REG_OFFSET)
-#define OMAP3ISP_CSI2A_REGS1_REG(offset) \
- (OMAP3ISP_CSI2A_REGS1_REG_BASE + (offset))
-
-#define OMAP3ISP_CSIPHY2_REG_OFFSET 0x1970
-#define OMAP3ISP_CSIPHY2_REG_BASE (OMAP3ISP_REG_BASE + \
- OMAP3ISP_CSIPHY2_REG_OFFSET)
-#define OMAP3ISP_CSIPHY2_REG(offset) (OMAP3ISP_CSIPHY2_REG_BASE + (offset))
-
-#define OMAP3ISP_CSI2A_REGS2_REG_OFFSET 0x19C0
-#define OMAP3ISP_CSI2A_REGS2_REG_BASE (OMAP3ISP_REG_BASE + \
- OMAP3ISP_CSI2A_REGS2_REG_OFFSET)
-#define OMAP3ISP_CSI2A_REGS2_REG(offset) \
- (OMAP3ISP_CSI2A_REGS2_REG_BASE + (offset))
-
-#define OMAP3ISP_CSI2C_REGS1_REG_OFFSET 0x1C00
-#define OMAP3ISP_CSI2C_REGS1_REG_BASE (OMAP3ISP_REG_BASE + \
- OMAP3ISP_CSI2C_REGS1_REG_OFFSET)
-#define OMAP3ISP_CSI2C_REGS1_REG(offset) \
- (OMAP3ISP_CSI2C_REGS1_REG_BASE + (offset))
-
-#define OMAP3ISP_CSIPHY1_REG_OFFSET 0x1D70
-#define OMAP3ISP_CSIPHY1_REG_BASE (OMAP3ISP_REG_BASE + \
- OMAP3ISP_CSIPHY1_REG_OFFSET)
-#define OMAP3ISP_CSIPHY1_REG(offset) (OMAP3ISP_CSIPHY1_REG_BASE + (offset))
-
-#define OMAP3ISP_CSI2C_REGS2_REG_OFFSET 0x1DC0
-#define OMAP3ISP_CSI2C_REGS2_REG_BASE (OMAP3ISP_REG_BASE + \
- OMAP3ISP_CSI2C_REGS2_REG_OFFSET)
-#define OMAP3ISP_CSI2C_REGS2_REG(offset) \
- (OMAP3ISP_CSI2C_REGS2_REG_BASE + (offset))
-
/* ISP module register offset */
#define ISP_REVISION (0x000)
@@ -1583,4 +1506,26 @@
#define ISPCSIPHY_REG2_CCP2_SYNC_PATTERN_MASK \
(0x7fffff << ISPCSIPHY_REG2_CCP2_SYNC_PATTERN_SHIFT)
+/* -----------------------------------------------------------------------------
+ * CONTROL registers for CSI-2 phy routing
+ */
+
+/* OMAP343X_CONTROL_CSIRXFE */
+#define OMAP343X_CONTROL_CSIRXFE_CSIB_INV (1 << 7)
+#define OMAP343X_CONTROL_CSIRXFE_RESENABLE (1 << 8)
+#define OMAP343X_CONTROL_CSIRXFE_SELFORM (1 << 10)
+#define OMAP343X_CONTROL_CSIRXFE_PWRDNZ (1 << 12)
+#define OMAP343X_CONTROL_CSIRXFE_RESET (1 << 13)
+
+/* OMAP3630_CONTROL_CAMERA_PHY_CTRL */
+#define OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY1_SHIFT 2
+#define OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY2_SHIFT 0
+#define OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_DPHY 0x0
+#define OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_CCP2_DATA_STROBE 0x1
+#define OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_CCP2_DATA_CLOCK 0x2
+#define OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_GPI 0x3
+#define OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_MASK 0x3
+/* CCP2B: set to receive data from PHY2 instead of PHY1 */
+#define OMAP3630_CONTROL_CAMERA_PHY_CTRL_CSI1_RX_SEL_PHY2 (1 << 4)
+
#endif /* OMAP3_ISP_REG_H */
diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c
index e7939869bda..61e17f9bd8b 100644
--- a/drivers/media/platform/omap3isp/ispstat.c
+++ b/drivers/media/platform/omap3isp/ispstat.c
@@ -257,7 +257,7 @@ static int isp_stat_buf_queue(struct ispstat *stat)
if (!stat->active_buf)
return STAT_NO_BUF;
- do_gettimeofday(&stat->active_buf->ts);
+ ktime_get_ts(&stat->active_buf->ts);
stat->active_buf->buf_size = stat->buf_size;
if (isp_stat_buf_check_magic(stat, stat->active_buf)) {
@@ -537,7 +537,8 @@ int omap3isp_stat_request_statistics(struct ispstat *stat,
return PTR_ERR(buf);
}
- data->ts = buf->ts;
+ data->ts.tv_sec = buf->ts.tv_sec;
+ data->ts.tv_usec = buf->ts.tv_nsec / NSEC_PER_USEC;
data->config_counter = buf->config_counter;
data->frame_number = buf->frame_number;
data->buf_size = buf->buf_size;
diff --git a/drivers/media/platform/omap3isp/ispstat.h b/drivers/media/platform/omap3isp/ispstat.h
index fd15094de34..9a047c929b9 100644
--- a/drivers/media/platform/omap3isp/ispstat.h
+++ b/drivers/media/platform/omap3isp/ispstat.h
@@ -50,7 +50,7 @@ struct ispstat_buffer {
struct iovm_struct *iovm;
void *virt_addr;
dma_addr_t dma_addr;
- struct timeval ts;
+ struct timespec ts;
u32 buf_size;
u32 frame_number;
u16 config_counter;
diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c
index 3311d6bb345..e0d73a64218 100644
--- a/drivers/media/platform/omap3isp/ispvideo.c
+++ b/drivers/media/platform/omap3isp/ispvideo.c
@@ -1392,7 +1392,8 @@ int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev)
ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1);
if (ret < 0)
- printk(KERN_ERR "%s: could not register video device (%d)\n",
+ dev_err(video->isp->dev,
+ "%s: could not register video device (%d)\n",
__func__, ret);
return ret;
diff --git a/drivers/media/platform/s3c-camif/Makefile b/drivers/media/platform/s3c-camif/Makefile
new file mode 100644
index 00000000000..50bf8c59b99
--- /dev/null
+++ b/drivers/media/platform/s3c-camif/Makefile
@@ -0,0 +1,5 @@
+# Makefile for s3c244x/s3c64xx CAMIF driver
+
+s3c-camif-objs := camif-core.o camif-capture.o camif-regs.o
+
+obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif.o
diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c
new file mode 100644
index 00000000000..a55793c3d81
--- /dev/null
+++ b/drivers/media/platform/s3c-camif/camif-capture.c
@@ -0,0 +1,1672 @@
+/*
+ * s3c24xx/s3c64xx SoC series Camera Interface (CAMIF) driver
+ *
+ * Copyright (C) 2012 Sylwester Nawrocki <sylvester.nawrocki@gmail.com>
+ * Copyright (C) 2012 Tomasz Figa <tomasz.figa@gmail.com>
+ *
+ * Based on drivers/media/platform/s5p-fimc,
+ * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__
+
+#include <linux/bug.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/ratelimit.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include <media/media-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "camif-core.h"
+#include "camif-regs.h"
+
+static int debug;
+module_param(debug, int, 0644);
+
+/* Locking: called with vp->camif->slock spinlock held */
+static void camif_cfg_video_path(struct camif_vp *vp)
+{
+ WARN_ON(s3c_camif_get_scaler_config(vp, &vp->scaler));
+ camif_hw_set_scaler(vp);
+ camif_hw_set_flip(vp);
+ camif_hw_set_target_format(vp);
+ camif_hw_set_output_dma(vp);
+}
+
+static void camif_prepare_dma_offset(struct camif_vp *vp)
+{
+ struct camif_frame *f = &vp->out_frame;
+
+ f->dma_offset.initial = f->rect.top * f->f_width + f->rect.left;
+ f->dma_offset.line = f->f_width - (f->rect.left + f->rect.width);
+
+ pr_debug("dma_offset: initial: %d, line: %d\n",
+ f->dma_offset.initial, f->dma_offset.line);
+}
+
+/* Locking: called with camif->slock spinlock held */
+static int s3c_camif_hw_init(struct camif_dev *camif, struct camif_vp *vp)
+{
+ const struct s3c_camif_variant *variant = camif->variant;
+
+ if (camif->sensor.sd == NULL || vp->out_fmt == NULL)
+ return -EINVAL;
+
+ if (variant->ip_revision == S3C244X_CAMIF_IP_REV)
+ camif_hw_clear_fifo_overflow(vp);
+ camif_hw_set_camera_bus(camif);
+ camif_hw_set_source_format(camif);
+ camif_hw_set_camera_crop(camif);
+ camif_hw_set_test_pattern(camif, camif->test_pattern);
+ if (variant->has_img_effect)
+ camif_hw_set_effect(camif, camif->colorfx,
+ camif->colorfx_cb, camif->colorfx_cr);
+ if (variant->ip_revision == S3C6410_CAMIF_IP_REV)
+ camif_hw_set_input_path(vp);
+ camif_cfg_video_path(vp);
+ vp->state &= ~ST_VP_CONFIG;
+
+ return 0;
+}
+
+/*
+ * Initialize the video path, only up from the scaler stage. The camera
+ * input interface set up is skipped. This is useful to enable one of the
+ * video paths when the other is already running.
+ * Locking: called with camif->slock spinlock held.
+ */
+static int s3c_camif_hw_vp_init(struct camif_dev *camif, struct camif_vp *vp)
+{
+ unsigned int ip_rev = camif->variant->ip_revision;
+
+ if (vp->out_fmt == NULL)
+ return -EINVAL;
+
+ camif_prepare_dma_offset(vp);
+ if (ip_rev == S3C244X_CAMIF_IP_REV)
+ camif_hw_clear_fifo_overflow(vp);
+ camif_cfg_video_path(vp);
+ vp->state &= ~ST_VP_CONFIG;
+ return 0;
+}
+
+static int sensor_set_power(struct camif_dev *camif, int on)
+{
+ struct cam_sensor *sensor = &camif->sensor;
+ int err = 0;
+
+ if (!on == camif->sensor.power_count)
+ err = v4l2_subdev_call(sensor->sd, core, s_power, on);
+ if (!err)
+ sensor->power_count += on ? 1 : -1;
+
+ pr_debug("on: %d, power_count: %d, err: %d\n",
+ on, sensor->power_count, err);
+
+ return err;
+}
+
+static int sensor_set_streaming(struct camif_dev *camif, int on)
+{
+ struct cam_sensor *sensor = &camif->sensor;
+ int err = 0;
+
+ if (!on == camif->sensor.stream_count)
+ err = v4l2_subdev_call(sensor->sd, video, s_stream, on);
+ if (!err)
+ sensor->stream_count += on ? 1 : -1;
+
+ pr_debug("on: %d, stream_count: %d, err: %d\n",
+ on, sensor->stream_count, err);
+
+ return err;
+}
+
+/*
+ * Reinitialize the driver so it is ready to start streaming again.
+ * Return any buffers to vb2, perform CAMIF software reset and
+ * turn off streaming at the data pipeline (sensor) if required.
+ */
+static int camif_reinitialize(struct camif_vp *vp)
+{
+ struct camif_dev *camif = vp->camif;
+ struct camif_buffer *buf;
+ unsigned long flags;
+ bool streaming;
+
+ spin_lock_irqsave(&camif->slock, flags);
+ streaming = vp->state & ST_VP_SENSOR_STREAMING;
+
+ vp->state &= ~(ST_VP_PENDING | ST_VP_RUNNING | ST_VP_OFF |
+ ST_VP_ABORTING | ST_VP_STREAMING |
+ ST_VP_SENSOR_STREAMING | ST_VP_LASTIRQ);
+
+ /* Release unused buffers */
+ while (!list_empty(&vp->pending_buf_q)) {
+ buf = camif_pending_queue_pop(vp);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ while (!list_empty(&vp->active_buf_q)) {
+ buf = camif_active_queue_pop(vp);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ spin_unlock_irqrestore(&camif->slock, flags);
+
+ if (!streaming)
+ return 0;
+
+ return sensor_set_streaming(camif, 0);
+}
+
+static bool s3c_vp_active(struct camif_vp *vp)
+{
+ struct camif_dev *camif = vp->camif;
+ unsigned long flags;
+ bool ret;
+
+ spin_lock_irqsave(&camif->slock, flags);
+ ret = (vp->state & ST_VP_RUNNING) || (vp->state & ST_VP_PENDING);
+ spin_unlock_irqrestore(&camif->slock, flags);
+
+ return ret;
+}
+
+static bool camif_is_streaming(struct camif_dev *camif)
+{
+ unsigned long flags;
+ bool status;
+
+ spin_lock_irqsave(&camif->slock, flags);
+ status = camif->stream_count > 0;
+ spin_unlock_irqrestore(&camif->slock, flags);
+
+ return status;
+}
+
+static int camif_stop_capture(struct camif_vp *vp)
+{
+ struct camif_dev *camif = vp->camif;
+ unsigned long flags;
+ int ret;
+
+ if (!s3c_vp_active(vp))
+ return 0;
+
+ spin_lock_irqsave(&camif->slock, flags);
+ vp->state &= ~(ST_VP_OFF | ST_VP_LASTIRQ);
+ vp->state |= ST_VP_ABORTING;
+ spin_unlock_irqrestore(&camif->slock, flags);
+
+ ret = wait_event_timeout(vp->irq_queue,
+ !(vp->state & ST_VP_ABORTING),
+ msecs_to_jiffies(CAMIF_STOP_TIMEOUT));
+
+ spin_lock_irqsave(&camif->slock, flags);
+
+ if (ret == 0 && !(vp->state & ST_VP_OFF)) {
+ /* Timed out, forcibly stop capture */
+ vp->state &= ~(ST_VP_OFF | ST_VP_ABORTING |
+ ST_VP_LASTIRQ);
+
+ camif_hw_disable_capture(vp);
+ camif_hw_enable_scaler(vp, false);
+ }
+
+ spin_unlock_irqrestore(&camif->slock, flags);
+
+ return camif_reinitialize(vp);
+}
+
+static int camif_prepare_addr(struct camif_vp *vp, struct vb2_buffer *vb,
+ struct camif_addr *paddr)
+{
+ struct camif_frame *frame = &vp->out_frame;
+ u32 pix_size;
+
+ if (vb == NULL || frame == NULL)
+ return -EINVAL;
+
+ pix_size = frame->rect.width * frame->rect.height;
+
+ pr_debug("colplanes: %d, pix_size: %u\n",
+ vp->out_fmt->colplanes, pix_size);
+
+ paddr->y = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ switch (vp->out_fmt->colplanes) {
+ case 1:
+ paddr->cb = 0;
+ paddr->cr = 0;
+ break;
+ case 2:
+ /* decompose Y into Y/Cb */
+ paddr->cb = (u32)(paddr->y + pix_size);
+ paddr->cr = 0;
+ break;
+ case 3:
+ paddr->cb = (u32)(paddr->y + pix_size);
+ /* decompose Y into Y/Cb/Cr */
+ if (vp->out_fmt->color == IMG_FMT_YCBCR422P)
+ paddr->cr = (u32)(paddr->cb + (pix_size >> 1));
+ else /* 420 */
+ paddr->cr = (u32)(paddr->cb + (pix_size >> 2));
+
+ if (vp->out_fmt->color == IMG_FMT_YCRCB420)
+ swap(paddr->cb, paddr->cr);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ pr_debug("DMA address: y: %#x cb: %#x cr: %#x\n",
+ paddr->y, paddr->cb, paddr->cr);
+
+ return 0;
+}
+
+irqreturn_t s3c_camif_irq_handler(int irq, void *priv)
+{
+ struct camif_vp *vp = priv;
+ struct camif_dev *camif = vp->camif;
+ unsigned int ip_rev = camif->variant->ip_revision;
+ unsigned int status;
+
+ spin_lock(&camif->slock);
+
+ if (ip_rev == S3C6410_CAMIF_IP_REV)
+ camif_hw_clear_pending_irq(vp);
+
+ status = camif_hw_get_status(vp);
+
+ if (ip_rev == S3C244X_CAMIF_IP_REV && (status & CISTATUS_OVF_MASK)) {
+ camif_hw_clear_fifo_overflow(vp);
+ goto unlock;
+ }
+
+ if (vp->state & ST_VP_ABORTING) {
+ if (vp->state & ST_VP_OFF) {
+ /* Last IRQ */
+ vp->state &= ~(ST_VP_OFF | ST_VP_ABORTING |
+ ST_VP_LASTIRQ);
+ wake_up(&vp->irq_queue);
+ goto unlock;
+ } else if (vp->state & ST_VP_LASTIRQ) {
+ camif_hw_disable_capture(vp);
+ camif_hw_enable_scaler(vp, false);
+ camif_hw_set_lastirq(vp, false);
+ vp->state |= ST_VP_OFF;
+ } else {
+ /* Disable capture, enable last IRQ */
+ camif_hw_set_lastirq(vp, true);
+ vp->state |= ST_VP_LASTIRQ;
+ }
+ }
+
+ if (!list_empty(&vp->pending_buf_q) && (vp->state & ST_VP_RUNNING) &&
+ !list_empty(&vp->active_buf_q)) {
+ unsigned int index;
+ struct camif_buffer *vbuf;
+ struct timeval *tv;
+ struct timespec ts;
+ /*
+ * Get previous DMA write buffer index:
+ * 0 => DMA buffer 0, 2;
+ * 1 => DMA buffer 1, 3.
+ */
+ index = (CISTATUS_FRAMECNT(status) + 2) & 1;
+
+ ktime_get_ts(&ts);
+ vbuf = camif_active_queue_peek(vp, index);
+
+ if (!WARN_ON(vbuf == NULL)) {
+ /* Dequeue a filled buffer */
+ tv = &vbuf->vb.v4l2_buf.timestamp;
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+ vbuf->vb.v4l2_buf.sequence = vp->frame_sequence++;
+ vb2_buffer_done(&vbuf->vb, VB2_BUF_STATE_DONE);
+
+ /* Set up an empty buffer at the DMA engine */
+ vbuf = camif_pending_queue_pop(vp);
+ vbuf->index = index;
+ camif_hw_set_output_addr(vp, &vbuf->paddr, index);
+ camif_hw_set_output_addr(vp, &vbuf->paddr, index + 2);
+
+ /* Scheduled in H/W, add to the queue */
+ camif_active_queue_add(vp, vbuf);
+ }
+ } else if (!(vp->state & ST_VP_ABORTING) &&
+ (vp->state & ST_VP_PENDING)) {
+ vp->state |= ST_VP_RUNNING;
+ }
+
+ if (vp->state & ST_VP_CONFIG) {
+ camif_prepare_dma_offset(vp);
+ camif_hw_set_camera_crop(camif);
+ camif_hw_set_scaler(vp);
+ camif_hw_set_flip(vp);
+ camif_hw_set_test_pattern(camif, camif->test_pattern);
+ if (camif->variant->has_img_effect)
+ camif_hw_set_effect(camif, camif->colorfx,
+ camif->colorfx_cb, camif->colorfx_cr);
+ vp->state &= ~ST_VP_CONFIG;
+ }
+unlock:
+ spin_unlock(&camif->slock);
+ return IRQ_HANDLED;
+}
+
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct camif_vp *vp = vb2_get_drv_priv(vq);
+ struct camif_dev *camif = vp->camif;
+ unsigned long flags;
+ int ret;
+
+ /*
+ * We assume the codec capture path is always activated
+ * first, before the preview path starts streaming.
+ * This is required to avoid internal FIFO overflow and
+ * a need for CAMIF software reset.
+ */
+ spin_lock_irqsave(&camif->slock, flags);
+
+ if (camif->stream_count == 0) {
+ camif_hw_reset(camif);
+ ret = s3c_camif_hw_init(camif, vp);
+ } else {
+ ret = s3c_camif_hw_vp_init(camif, vp);
+ }
+ spin_unlock_irqrestore(&camif->slock, flags);
+
+ if (ret < 0) {
+ camif_reinitialize(vp);
+ return ret;
+ }
+
+ spin_lock_irqsave(&camif->slock, flags);
+ vp->frame_sequence = 0;
+ vp->state |= ST_VP_PENDING;
+
+ if (!list_empty(&vp->pending_buf_q) &&
+ (!(vp->state & ST_VP_STREAMING) ||
+ !(vp->state & ST_VP_SENSOR_STREAMING))) {
+
+ camif_hw_enable_scaler(vp, vp->scaler.enable);
+ camif_hw_enable_capture(vp);
+ vp->state |= ST_VP_STREAMING;
+
+ if (!(vp->state & ST_VP_SENSOR_STREAMING)) {
+ vp->state |= ST_VP_SENSOR_STREAMING;
+ spin_unlock_irqrestore(&camif->slock, flags);
+ ret = sensor_set_streaming(camif, 1);
+ if (ret)
+ v4l2_err(&vp->vdev, "Sensor s_stream failed\n");
+ if (debug)
+ camif_hw_dump_regs(camif, __func__);
+
+ return ret;
+ }
+ }
+
+ spin_unlock_irqrestore(&camif->slock, flags);
+ return 0;
+}
+
+static int stop_streaming(struct vb2_queue *vq)
+{
+ struct camif_vp *vp = vb2_get_drv_priv(vq);
+ return camif_stop_capture(vp);
+}
+
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *allocators[])
+{
+ const struct v4l2_pix_format *pix = NULL;
+ struct camif_vp *vp = vb2_get_drv_priv(vq);
+ struct camif_dev *camif = vp->camif;
+ struct camif_frame *frame = &vp->out_frame;
+ const struct camif_fmt *fmt = vp->out_fmt;
+ unsigned int size;
+
+ if (pfmt) {
+ pix = &pfmt->fmt.pix;
+ fmt = s3c_camif_find_format(vp, &pix->pixelformat, -1);
+ size = (pix->width * pix->height * fmt->depth) / 8;
+ } else {
+ size = (frame->f_width * frame->f_height * fmt->depth) / 8;
+ }
+
+ if (fmt == NULL)
+ return -EINVAL;
+ *num_planes = 1;
+
+ if (pix)
+ sizes[0] = max(size, pix->sizeimage);
+ else
+ sizes[0] = size;
+ allocators[0] = camif->alloc_ctx;
+
+ pr_debug("size: %u\n", sizes[0]);
+ return 0;
+}
+
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+ struct camif_vp *vp = vb2_get_drv_priv(vb->vb2_queue);
+
+ if (vp->out_fmt == NULL)
+ return -EINVAL;
+
+ if (vb2_plane_size(vb, 0) < vp->payload) {
+ v4l2_err(&vp->vdev, "buffer too small: %lu, required: %u\n",
+ vb2_plane_size(vb, 0), vp->payload);
+ return -EINVAL;
+ }
+ vb2_set_plane_payload(vb, 0, vp->payload);
+
+ return 0;
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+ struct camif_buffer *buf = container_of(vb, struct camif_buffer, vb);
+ struct camif_vp *vp = vb2_get_drv_priv(vb->vb2_queue);
+ struct camif_dev *camif = vp->camif;
+ unsigned long flags;
+
+ spin_lock_irqsave(&camif->slock, flags);
+ WARN_ON(camif_prepare_addr(vp, &buf->vb, &buf->paddr));
+
+ if (!(vp->state & ST_VP_STREAMING) && vp->active_buffers < 2) {
+ /* Schedule an empty buffer in H/W */
+ buf->index = vp->buf_index;
+
+ camif_hw_set_output_addr(vp, &buf->paddr, buf->index);
+ camif_hw_set_output_addr(vp, &buf->paddr, buf->index + 2);
+
+ camif_active_queue_add(vp, buf);
+ vp->buf_index = !vp->buf_index;
+ } else {
+ camif_pending_queue_add(vp, buf);
+ }
+
+ if (vb2_is_streaming(&vp->vb_queue) && !list_empty(&vp->pending_buf_q)
+ && !(vp->state & ST_VP_STREAMING)) {
+
+ vp->state |= ST_VP_STREAMING;
+ camif_hw_enable_scaler(vp, vp->scaler.enable);
+ camif_hw_enable_capture(vp);
+ spin_unlock_irqrestore(&camif->slock, flags);
+
+ if (!(vp->state & ST_VP_SENSOR_STREAMING)) {
+ if (sensor_set_streaming(camif, 1) == 0)
+ vp->state |= ST_VP_SENSOR_STREAMING;
+ else
+ v4l2_err(&vp->vdev, "Sensor s_stream failed\n");
+
+ if (debug)
+ camif_hw_dump_regs(camif, __func__);
+ }
+ return;
+ }
+ spin_unlock_irqrestore(&camif->slock, flags);
+}
+
+static void camif_lock(struct vb2_queue *vq)
+{
+ struct camif_vp *vp = vb2_get_drv_priv(vq);
+ mutex_lock(&vp->camif->lock);
+}
+
+static void camif_unlock(struct vb2_queue *vq)
+{
+ struct camif_vp *vp = vb2_get_drv_priv(vq);
+ mutex_unlock(&vp->camif->lock);
+}
+
+static const struct vb2_ops s3c_camif_qops = {
+ .queue_setup = queue_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .wait_prepare = camif_unlock,
+ .wait_finish = camif_lock,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+};
+
+static int s3c_camif_open(struct file *file)
+{
+ struct camif_vp *vp = video_drvdata(file);
+ struct camif_dev *camif = vp->camif;
+ int ret;
+
+ pr_debug("[vp%d] state: %#x, owner: %p, pid: %d\n", vp->id,
+ vp->state, vp->owner, task_pid_nr(current));
+
+ if (mutex_lock_interruptible(&camif->lock))
+ return -ERESTARTSYS;
+
+ ret = v4l2_fh_open(file);
+ if (ret < 0)
+ goto unlock;
+
+ ret = pm_runtime_get_sync(camif->dev);
+ if (ret < 0)
+ goto err_pm;
+
+ ret = sensor_set_power(camif, 1);
+ if (!ret)
+ goto unlock;
+
+ pm_runtime_put(camif->dev);
+err_pm:
+ v4l2_fh_release(file);
+unlock:
+ mutex_unlock(&camif->lock);
+ return ret;
+}
+
+static int s3c_camif_close(struct file *file)
+{
+ struct camif_vp *vp = video_drvdata(file);
+ struct camif_dev *camif = vp->camif;
+ int ret;
+
+ pr_debug("[vp%d] state: %#x, owner: %p, pid: %d\n", vp->id,
+ vp->state, vp->owner, task_pid_nr(current));
+
+ mutex_lock(&camif->lock);
+
+ if (vp->owner == file->private_data) {
+ camif_stop_capture(vp);
+ vb2_queue_release(&vp->vb_queue);
+ vp->owner = NULL;
+ }
+
+ sensor_set_power(camif, 0);
+
+ pm_runtime_put(camif->dev);
+ ret = v4l2_fh_release(file);
+
+ mutex_unlock(&camif->lock);
+ return ret;
+}
+
+static unsigned int s3c_camif_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct camif_vp *vp = video_drvdata(file);
+ struct camif_dev *camif = vp->camif;
+ int ret;
+
+ mutex_lock(&camif->lock);
+ if (vp->owner && vp->owner != file->private_data)
+ ret = -EBUSY;
+ else
+ ret = vb2_poll(&vp->vb_queue, file, wait);
+
+ mutex_unlock(&camif->lock);
+ return ret;
+}
+
+static int s3c_camif_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct camif_vp *vp = video_drvdata(file);
+ int ret;
+
+ if (vp->owner && vp->owner != file->private_data)
+ ret = -EBUSY;
+ else
+ ret = vb2_mmap(&vp->vb_queue, vma);
+
+ return ret;
+}
+
+static const struct v4l2_file_operations s3c_camif_fops = {
+ .owner = THIS_MODULE,
+ .open = s3c_camif_open,
+ .release = s3c_camif_close,
+ .poll = s3c_camif_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = s3c_camif_mmap,
+};
+
+/*
+ * Video node IOCTLs
+ */
+
+static int s3c_camif_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct camif_vp *vp = video_drvdata(file);
+
+ strlcpy(cap->driver, S3C_CAMIF_DRIVER_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, S3C_CAMIF_DRIVER_NAME, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s.%d",
+ dev_name(vp->camif->dev), vp->id);
+
+ cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static int s3c_camif_vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *input)
+{
+ struct camif_vp *vp = video_drvdata(file);
+ struct v4l2_subdev *sensor = vp->camif->sensor.sd;
+
+ if (input->index || sensor == NULL)
+ return -EINVAL;
+
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ strlcpy(input->name, sensor->name, sizeof(input->name));
+ return 0;
+}
+
+static int s3c_camif_vidioc_s_input(struct file *file, void *priv,
+ unsigned int i)
+{
+ return i == 0 ? 0 : -EINVAL;
+}
+
+static int s3c_camif_vidioc_g_input(struct file *file, void *priv,
+ unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int s3c_camif_vidioc_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct camif_vp *vp = video_drvdata(file);
+ const struct camif_fmt *fmt;
+
+ fmt = s3c_camif_find_format(vp, NULL, f->index);
+ if (!fmt)
+ return -EINVAL;
+
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+
+ pr_debug("fmt(%d): %s\n", f->index, f->description);
+ return 0;
+}
+
+static int s3c_camif_vidioc_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct camif_vp *vp = video_drvdata(file);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct camif_frame *frame = &vp->out_frame;
+ const struct camif_fmt *fmt = vp->out_fmt;
+
+ pix->bytesperline = frame->f_width * fmt->ybpp;
+ pix->sizeimage = vp->payload;
+
+ pix->pixelformat = fmt->fourcc;
+ pix->width = frame->f_width;
+ pix->height = frame->f_height;
+ pix->field = V4L2_FIELD_NONE;
+ pix->colorspace = V4L2_COLORSPACE_JPEG;
+
+ return 0;
+}
+
+static int __camif_video_try_format(struct camif_vp *vp,
+ struct v4l2_pix_format *pix,
+ const struct camif_fmt **ffmt)
+{
+ struct camif_dev *camif = vp->camif;
+ struct v4l2_rect *crop = &camif->camif_crop;
+ unsigned int wmin, hmin, sc_hrmax, sc_vrmax;
+ const struct vp_pix_limits *pix_lim;
+ const struct camif_fmt *fmt;
+
+ fmt = s3c_camif_find_format(vp, &pix->pixelformat, 0);
+
+ if (WARN_ON(fmt == NULL))
+ return -EINVAL;
+
+ if (ffmt)
+ *ffmt = fmt;
+
+ pix_lim = &camif->variant->vp_pix_limits[vp->id];
+
+ pr_debug("fmt: %ux%u, crop: %ux%u, bytesperline: %u\n",
+ pix->width, pix->height, crop->width, crop->height,
+ pix->bytesperline);
+ /*
+ * Calculate minimum width and height according to the configured
+ * camera input interface crop rectangle and the resizer's capabilities.
+ */
+ sc_hrmax = min(SCALER_MAX_RATIO, 1 << (ffs(crop->width) - 3));
+ sc_vrmax = min(SCALER_MAX_RATIO, 1 << (ffs(crop->height) - 1));
+
+ wmin = max_t(u32, pix_lim->min_out_width, crop->width / sc_hrmax);
+ wmin = round_up(wmin, pix_lim->out_width_align);
+ hmin = max_t(u32, 8, crop->height / sc_vrmax);
+ hmin = round_up(hmin, 8);
+
+ v4l_bound_align_image(&pix->width, wmin, pix_lim->max_sc_out_width,
+ ffs(pix_lim->out_width_align) - 1,
+ &pix->height, hmin, pix_lim->max_height, 0, 0);
+
+ pix->bytesperline = pix->width * fmt->ybpp;
+ pix->sizeimage = (pix->width * pix->height * fmt->depth) / 8;
+ pix->pixelformat = fmt->fourcc;
+ pix->colorspace = V4L2_COLORSPACE_JPEG;
+ pix->field = V4L2_FIELD_NONE;
+
+ pr_debug("%ux%u, wmin: %d, hmin: %d, sc_hrmax: %d, sc_vrmax: %d\n",
+ pix->width, pix->height, wmin, hmin, sc_hrmax, sc_vrmax);
+
+ return 0;
+}
+
+static int s3c_camif_vidioc_try_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct camif_vp *vp = video_drvdata(file);
+ return __camif_video_try_format(vp, &f->fmt.pix, NULL);
+}
+
+static int s3c_camif_vidioc_s_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct camif_vp *vp = video_drvdata(file);
+ struct camif_frame *out_frame = &vp->out_frame;
+ const struct camif_fmt *fmt = NULL;
+ int ret;
+
+ pr_debug("[vp%d]\n", vp->id);
+
+ if (vb2_is_busy(&vp->vb_queue))
+ return -EBUSY;
+
+ ret = __camif_video_try_format(vp, &f->fmt.pix, &fmt);
+ if (ret < 0)
+ return ret;
+
+ vp->out_fmt = fmt;
+ vp->payload = pix->sizeimage;
+ out_frame->f_width = pix->width;
+ out_frame->f_height = pix->height;
+
+ /* Reset composition rectangle */
+ out_frame->rect.width = pix->width;
+ out_frame->rect.height = pix->height;
+ out_frame->rect.left = 0;
+ out_frame->rect.top = 0;
+
+ if (vp->owner == NULL)
+ vp->owner = priv;
+
+ pr_debug("%ux%u. payload: %u. fmt: %s. %d %d. sizeimage: %d. bpl: %d\n",
+ out_frame->f_width, out_frame->f_height, vp->payload, fmt->name,
+ pix->width * pix->height * fmt->depth, fmt->depth,
+ pix->sizeimage, pix->bytesperline);
+
+ return 0;
+}
+
+/* Only check pixel formats at the sensor and the camif subdev pads */
+static int camif_pipeline_validate(struct camif_dev *camif)
+{
+ struct v4l2_subdev_format src_fmt;
+ struct media_pad *pad;
+ int ret;
+
+ /* Retrieve format at the sensor subdev source pad */
+ pad = media_entity_remote_source(&camif->pads[0]);
+ if (!pad || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ return -EPIPE;
+
+ src_fmt.pad = pad->index;
+ src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(camif->sensor.sd, pad, get_fmt, NULL, &src_fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+
+ if (src_fmt.format.width != camif->mbus_fmt.width ||
+ src_fmt.format.height != camif->mbus_fmt.height ||
+ src_fmt.format.code != camif->mbus_fmt.code)
+ return -EPIPE;
+
+ return 0;
+}
+
+static int s3c_camif_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct camif_vp *vp = video_drvdata(file);
+ struct camif_dev *camif = vp->camif;
+ struct media_entity *sensor = &camif->sensor.sd->entity;
+ int ret;
+
+ pr_debug("[vp%d]\n", vp->id);
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (vp->owner && vp->owner != priv)
+ return -EBUSY;
+
+ if (s3c_vp_active(vp))
+ return 0;
+
+ ret = media_entity_pipeline_start(sensor, camif->m_pipeline);
+ if (ret < 0)
+ return ret;
+
+ ret = camif_pipeline_validate(camif);
+ if (ret < 0) {
+ media_entity_pipeline_stop(sensor);
+ return ret;
+ }
+
+ return vb2_streamon(&vp->vb_queue, type);
+}
+
+static int s3c_camif_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct camif_vp *vp = video_drvdata(file);
+ struct camif_dev *camif = vp->camif;
+ int ret;
+
+ pr_debug("[vp%d]\n", vp->id);
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (vp->owner && vp->owner != priv)
+ return -EBUSY;
+
+ ret = vb2_streamoff(&vp->vb_queue, type);
+ if (ret == 0)
+ media_entity_pipeline_stop(&camif->sensor.sd->entity);
+ return ret;
+}
+
+static int s3c_camif_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct camif_vp *vp = video_drvdata(file);
+ int ret;
+
+ pr_debug("[vp%d] rb count: %d, owner: %p, priv: %p\n",
+ vp->id, rb->count, vp->owner, priv);
+
+ if (vp->owner && vp->owner != priv)
+ return -EBUSY;
+
+ if (rb->count)
+ rb->count = max_t(u32, CAMIF_REQ_BUFS_MIN, rb->count);
+ else
+ vp->owner = NULL;
+
+ ret = vb2_reqbufs(&vp->vb_queue, rb);
+ if (!ret) {
+ vp->reqbufs_count = rb->count;
+ if (vp->owner == NULL && rb->count > 0)
+ vp->owner = priv;
+ }
+
+ return ret;
+}
+
+static int s3c_camif_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct camif_vp *vp = video_drvdata(file);
+ return vb2_querybuf(&vp->vb_queue, buf);
+}
+
+static int s3c_camif_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct camif_vp *vp = video_drvdata(file);
+
+ pr_debug("[vp%d]\n", vp->id);
+
+ if (vp->owner && vp->owner != priv)
+ return -EBUSY;
+
+ return vb2_qbuf(&vp->vb_queue, buf);
+}
+
+static int s3c_camif_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct camif_vp *vp = video_drvdata(file);
+
+ pr_debug("[vp%d] sequence: %d\n", vp->id, vp->frame_sequence);
+
+ if (vp->owner && vp->owner != priv)
+ return -EBUSY;
+
+ return vb2_dqbuf(&vp->vb_queue, buf, file->f_flags & O_NONBLOCK);
+}
+
+static int s3c_camif_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *create)
+{
+ struct camif_vp *vp = video_drvdata(file);
+ int ret;
+
+ if (vp->owner && vp->owner != priv)
+ return -EBUSY;
+
+ create->count = max_t(u32, 1, create->count);
+ ret = vb2_create_bufs(&vp->vb_queue, create);
+
+ if (!ret && vp->owner == NULL)
+ vp->owner = priv;
+
+ return ret;
+}
+
+static int s3c_camif_prepare_buf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct camif_vp *vp = video_drvdata(file);
+ return vb2_prepare_buf(&vp->vb_queue, b);
+}
+
+static int s3c_camif_g_selection(struct file *file, void *priv,
+ struct v4l2_selection *sel)
+{
+ struct camif_vp *vp = video_drvdata(file);
+
+ if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = vp->out_frame.f_width;
+ sel->r.height = vp->out_frame.f_height;
+ return 0;
+
+ case V4L2_SEL_TGT_COMPOSE:
+ sel->r = vp->out_frame.rect;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static void __camif_try_compose(struct camif_dev *camif, struct camif_vp *vp,
+ struct v4l2_rect *r)
+{
+ /* s3c244x doesn't support composition */
+ if (camif->variant->ip_revision == S3C244X_CAMIF_IP_REV) {
+ *r = vp->out_frame.rect;
+ return;
+ }
+
+ /* TODO: s3c64xx */
+}
+
+static int s3c_camif_s_selection(struct file *file, void *priv,
+ struct v4l2_selection *sel)
+{
+ struct camif_vp *vp = video_drvdata(file);
+ struct camif_dev *camif = vp->camif;
+ struct v4l2_rect rect = sel->r;
+ unsigned long flags;
+
+ if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ sel->target != V4L2_SEL_TGT_COMPOSE)
+ return -EINVAL;
+
+ __camif_try_compose(camif, vp, &rect);
+
+ sel->r = rect;
+ spin_lock_irqsave(&camif->slock, flags);
+ vp->out_frame.rect = rect;
+ vp->state |= ST_VP_CONFIG;
+ spin_unlock_irqrestore(&camif->slock, flags);
+
+ pr_debug("type: %#x, target: %#x, flags: %#x, (%d,%d)/%dx%d\n",
+ sel->type, sel->target, sel->flags,
+ sel->r.left, sel->r.top, sel->r.width, sel->r.height);
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops s3c_camif_ioctl_ops = {
+ .vidioc_querycap = s3c_camif_vidioc_querycap,
+ .vidioc_enum_input = s3c_camif_vidioc_enum_input,
+ .vidioc_g_input = s3c_camif_vidioc_g_input,
+ .vidioc_s_input = s3c_camif_vidioc_s_input,
+ .vidioc_enum_fmt_vid_cap = s3c_camif_vidioc_enum_fmt,
+ .vidioc_try_fmt_vid_cap = s3c_camif_vidioc_try_fmt,
+ .vidioc_s_fmt_vid_cap = s3c_camif_vidioc_s_fmt,
+ .vidioc_g_fmt_vid_cap = s3c_camif_vidioc_g_fmt,
+ .vidioc_g_selection = s3c_camif_g_selection,
+ .vidioc_s_selection = s3c_camif_s_selection,
+ .vidioc_reqbufs = s3c_camif_reqbufs,
+ .vidioc_querybuf = s3c_camif_querybuf,
+ .vidioc_prepare_buf = s3c_camif_prepare_buf,
+ .vidioc_create_bufs = s3c_camif_create_bufs,
+ .vidioc_qbuf = s3c_camif_qbuf,
+ .vidioc_dqbuf = s3c_camif_dqbuf,
+ .vidioc_streamon = s3c_camif_streamon,
+ .vidioc_streamoff = s3c_camif_streamoff,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+};
+
+/*
+ * Video node controls
+ */
+static int s3c_camif_video_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct camif_vp *vp = ctrl->priv;
+ struct camif_dev *camif = vp->camif;
+ unsigned long flags;
+
+ pr_debug("[vp%d] ctrl: %s, value: %d\n", vp->id,
+ ctrl->name, ctrl->val);
+
+ spin_lock_irqsave(&camif->slock, flags);
+
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ vp->hflip = ctrl->val;
+ break;
+
+ case V4L2_CID_VFLIP:
+ vp->vflip = ctrl->val;
+ break;
+ }
+
+ vp->state |= ST_VP_CONFIG;
+ spin_unlock_irqrestore(&camif->slock, flags);
+ return 0;
+}
+
+/* Codec and preview video node control ops */
+static const struct v4l2_ctrl_ops s3c_camif_video_ctrl_ops = {
+ .s_ctrl = s3c_camif_video_s_ctrl,
+};
+
+int s3c_camif_register_video_node(struct camif_dev *camif, int idx)
+{
+ struct camif_vp *vp = &camif->vp[idx];
+ struct vb2_queue *q = &vp->vb_queue;
+ struct video_device *vfd = &vp->vdev;
+ struct v4l2_ctrl *ctrl;
+ int ret;
+
+ memset(vfd, 0, sizeof(*vfd));
+ snprintf(vfd->name, sizeof(vfd->name), "camif-%s",
+ vp->id == 0 ? "codec" : "preview");
+
+ vfd->fops = &s3c_camif_fops;
+ vfd->ioctl_ops = &s3c_camif_ioctl_ops;
+ vfd->v4l2_dev = &camif->v4l2_dev;
+ vfd->minor = -1;
+ vfd->release = video_device_release_empty;
+ vfd->lock = &camif->lock;
+ vp->reqbufs_count = 0;
+
+ INIT_LIST_HEAD(&vp->pending_buf_q);
+ INIT_LIST_HEAD(&vp->active_buf_q);
+
+ memset(q, 0, sizeof(*q));
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->ops = &s3c_camif_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct camif_buffer);
+ q->drv_priv = vp;
+
+ ret = vb2_queue_init(q);
+ if (ret)
+ goto err_vd_rel;
+
+ vp->pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_init(&vfd->entity, 1, &vp->pad, 0);
+ if (ret)
+ goto err_vd_rel;
+
+ video_set_drvdata(vfd, vp);
+ set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
+
+ v4l2_ctrl_handler_init(&vp->ctrl_handler, 1);
+ ctrl = v4l2_ctrl_new_std(&vp->ctrl_handler, &s3c_camif_video_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ if (ctrl)
+ ctrl->priv = vp;
+ ctrl = v4l2_ctrl_new_std(&vp->ctrl_handler, &s3c_camif_video_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ if (ctrl)
+ ctrl->priv = vp;
+
+ ret = vp->ctrl_handler.error;
+ if (ret < 0)
+ goto err_me_cleanup;
+
+ vfd->ctrl_handler = &vp->ctrl_handler;
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ goto err_ctrlh_free;
+
+ v4l2_info(&camif->v4l2_dev, "registered %s as /dev/%s\n",
+ vfd->name, video_device_node_name(vfd));
+ return 0;
+
+err_ctrlh_free:
+ v4l2_ctrl_handler_free(&vp->ctrl_handler);
+err_me_cleanup:
+ media_entity_cleanup(&vfd->entity);
+err_vd_rel:
+ video_device_release(vfd);
+ return ret;
+}
+
+void s3c_camif_unregister_video_node(struct camif_dev *camif, int idx)
+{
+ struct video_device *vfd = &camif->vp[idx].vdev;
+
+ if (video_is_registered(vfd)) {
+ video_unregister_device(vfd);
+ media_entity_cleanup(&vfd->entity);
+ v4l2_ctrl_handler_free(vfd->ctrl_handler);
+ }
+}
+
+/* Media bus pixel formats supported at the camif input */
+static const enum v4l2_mbus_pixelcode camif_mbus_formats[] = {
+ V4L2_MBUS_FMT_YUYV8_2X8,
+ V4L2_MBUS_FMT_YVYU8_2X8,
+ V4L2_MBUS_FMT_UYVY8_2X8,
+ V4L2_MBUS_FMT_VYUY8_2X8,
+};
+
+/*
+ * Camera input interface subdev operations
+ */
+
+static int s3c_camif_subdev_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(camif_mbus_formats))
+ return -EINVAL;
+
+ code->code = camif_mbus_formats[code->index];
+ return 0;
+}
+
+static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct camif_dev *camif = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *mf = &fmt->format;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ fmt->format = *mf;
+ return 0;
+ }
+
+ mutex_lock(&camif->lock);
+
+ switch (fmt->pad) {
+ case CAMIF_SD_PAD_SINK:
+ /* full camera input pixel size */
+ *mf = camif->mbus_fmt;
+ break;
+
+ case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ /* crop rectangle at camera interface input */
+ mf->width = camif->camif_crop.width;
+ mf->height = camif->camif_crop.height;
+ mf->code = camif->mbus_fmt.code;
+ break;
+ }
+
+ mutex_unlock(&camif->lock);
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ return 0;
+}
+
+static void __camif_subdev_try_format(struct camif_dev *camif,
+ struct v4l2_mbus_framefmt *mf, int pad)
+{
+ const struct s3c_camif_variant *variant = camif->variant;
+ const struct vp_pix_limits *pix_lim;
+ int i = ARRAY_SIZE(camif_mbus_formats);
+
+ /* FIXME: constraints against codec or preview path ? */
+ pix_lim = &variant->vp_pix_limits[VP_CODEC];
+
+ while (i-- >= 0)
+ if (camif_mbus_formats[i] == mf->code)
+ break;
+
+ mf->code = camif_mbus_formats[i];
+
+ if (pad == CAMIF_SD_PAD_SINK) {
+ v4l_bound_align_image(&mf->width, 8, CAMIF_MAX_PIX_WIDTH,
+ ffs(pix_lim->out_width_align) - 1,
+ &mf->height, 8, CAMIF_MAX_PIX_HEIGHT, 0,
+ 0);
+ } else {
+ struct v4l2_rect *crop = &camif->camif_crop;
+ v4l_bound_align_image(&mf->width, 8, crop->width,
+ ffs(pix_lim->out_width_align) - 1,
+ &mf->height, 8, crop->height,
+ 0, 0);
+ }
+
+ v4l2_dbg(1, debug, &camif->subdev, "%ux%u\n", mf->width, mf->height);
+}
+
+static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct camif_dev *camif = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *mf = &fmt->format;
+ struct v4l2_rect *crop = &camif->camif_crop;
+ int i;
+
+ v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %ux%u\n",
+ fmt->pad, mf->code, mf->width, mf->height);
+
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ mutex_lock(&camif->lock);
+
+ /*
+ * No pixel format change at the camera input is allowed
+ * while streaming.
+ */
+ if (vb2_is_busy(&camif->vp[VP_CODEC].vb_queue) ||
+ vb2_is_busy(&camif->vp[VP_PREVIEW].vb_queue)) {
+ mutex_unlock(&camif->lock);
+ return -EBUSY;
+ }
+
+ __camif_subdev_try_format(camif, mf, fmt->pad);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ *mf = fmt->format;
+ mutex_unlock(&camif->lock);
+ return 0;
+ }
+
+ switch (fmt->pad) {
+ case CAMIF_SD_PAD_SINK:
+ camif->mbus_fmt = *mf;
+ /* Reset sink crop rectangle. */
+ crop->width = mf->width;
+ crop->height = mf->height;
+ crop->left = 0;
+ crop->top = 0;
+ /*
+ * Reset source format (the camif's crop rectangle)
+ * and the video output resolution.
+ */
+ for (i = 0; i < CAMIF_VP_NUM; i++) {
+ struct camif_frame *frame = &camif->vp[i].out_frame;
+ frame->rect = *crop;
+ frame->f_width = mf->width;
+ frame->f_height = mf->height;
+ }
+ break;
+
+ case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ /* Pixel format can be only changed on the sink pad. */
+ mf->code = camif->mbus_fmt.code;
+ mf->width = crop->width;
+ mf->height = crop->height;
+ break;
+ }
+
+ mutex_unlock(&camif->lock);
+ return 0;
+}
+
+static int s3c_camif_subdev_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct camif_dev *camif = v4l2_get_subdevdata(sd);
+ struct v4l2_rect *crop = &camif->camif_crop;
+ struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt;
+
+ if ((sel->target != V4L2_SEL_TGT_CROP &&
+ sel->target != V4L2_SEL_TGT_CROP_BOUNDS) ||
+ sel->pad != CAMIF_SD_PAD_SINK)
+ return -EINVAL;
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad);
+ return 0;
+ }
+
+ mutex_lock(&camif->lock);
+
+ if (sel->target == V4L2_SEL_TGT_CROP) {
+ sel->r = *crop;
+ } else { /* crop bounds */
+ sel->r.width = mf->width;
+ sel->r.height = mf->height;
+ sel->r.left = 0;
+ sel->r.top = 0;
+ }
+
+ mutex_unlock(&camif->lock);
+
+ v4l2_dbg(1, debug, sd, "%s: crop: (%d,%d) %dx%d, size: %ux%u\n",
+ __func__, crop->left, crop->top, crop->width,
+ crop->height, mf->width, mf->height);
+
+ return 0;
+}
+
+static void __camif_try_crop(struct camif_dev *camif, struct v4l2_rect *r)
+{
+ struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt;
+ const struct camif_pix_limits *pix_lim = &camif->variant->pix_limits;
+ unsigned int left = 2 * r->left;
+ unsigned int top = 2 * r->top;
+
+ /*
+ * Following constraints must be met:
+ * - r->width + 2 * r->left = mf->width;
+ * - r->height + 2 * r->top = mf->height;
+ * - crop rectangle size and position must be aligned
+ * to 8 or 2 pixels, depending on SoC version.
+ */
+ v4l_bound_align_image(&r->width, 0, mf->width,
+ ffs(pix_lim->win_hor_offset_align) - 1,
+ &r->height, 0, mf->height, 1, 0);
+
+ v4l_bound_align_image(&left, 0, mf->width - r->width,
+ ffs(pix_lim->win_hor_offset_align),
+ &top, 0, mf->height - r->height, 2, 0);
+
+ r->left = left / 2;
+ r->top = top / 2;
+ r->width = mf->width - left;
+ r->height = mf->height - top;
+ /*
+ * Make sure we either downscale or upscale both the pixel
+ * width and height. Just return current crop rectangle if
+ * this scaler constraint is not met.
+ */
+ if (camif->variant->ip_revision == S3C244X_CAMIF_IP_REV &&
+ camif_is_streaming(camif)) {
+ unsigned int i;
+
+ for (i = 0; i < CAMIF_VP_NUM; i++) {
+ struct v4l2_rect *or = &camif->vp[i].out_frame.rect;
+ if ((or->width > r->width) == (or->height > r->height))
+ continue;
+ *r = camif->camif_crop;
+ pr_debug("Width/height scaling direction limitation\n");
+ break;
+ }
+ }
+
+ v4l2_dbg(1, debug, &camif->v4l2_dev, "crop: (%d,%d)/%dx%d, fmt: %ux%u\n",
+ r->left, r->top, r->width, r->height, mf->width, mf->height);
+}
+
+static int s3c_camif_subdev_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct camif_dev *camif = v4l2_get_subdevdata(sd);
+ struct v4l2_rect *crop = &camif->camif_crop;
+ struct camif_scaler scaler;
+
+ if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != CAMIF_SD_PAD_SINK)
+ return -EINVAL;
+
+ mutex_lock(&camif->lock);
+ __camif_try_crop(camif, &sel->r);
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ *v4l2_subdev_get_try_crop(fh, sel->pad) = sel->r;
+ } else {
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&camif->slock, flags);
+ *crop = sel->r;
+
+ for (i = 0; i < CAMIF_VP_NUM; i++) {
+ struct camif_vp *vp = &camif->vp[i];
+ scaler = vp->scaler;
+ if (s3c_camif_get_scaler_config(vp, &scaler))
+ continue;
+ vp->scaler = scaler;
+ vp->state |= ST_VP_CONFIG;
+ }
+
+ spin_unlock_irqrestore(&camif->slock, flags);
+ }
+ mutex_unlock(&camif->lock);
+
+ v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %u, f_h: %u\n",
+ __func__, crop->left, crop->top, crop->width, crop->height,
+ camif->mbus_fmt.width, camif->mbus_fmt.height);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops s3c_camif_subdev_pad_ops = {
+ .enum_mbus_code = s3c_camif_subdev_enum_mbus_code,
+ .get_selection = s3c_camif_subdev_get_selection,
+ .set_selection = s3c_camif_subdev_set_selection,
+ .get_fmt = s3c_camif_subdev_get_fmt,
+ .set_fmt = s3c_camif_subdev_set_fmt,
+};
+
+static struct v4l2_subdev_ops s3c_camif_subdev_ops = {
+ .pad = &s3c_camif_subdev_pad_ops,
+};
+
+static int s3c_camif_subdev_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct camif_dev *camif = container_of(ctrl->handler, struct camif_dev,
+ ctrl_handler);
+ unsigned long flags;
+
+ spin_lock_irqsave(&camif->slock, flags);
+
+ switch (ctrl->id) {
+ case V4L2_CID_COLORFX:
+ camif->colorfx = camif->ctrl_colorfx->val;
+ /* Set Cb, Cr */
+ switch (ctrl->val) {
+ case V4L2_COLORFX_SEPIA:
+ camif->colorfx_cb = 115;
+ camif->colorfx_cr = 145;
+ break;
+ case V4L2_COLORFX_SET_CBCR:
+ camif->colorfx_cb = camif->ctrl_colorfx_cbcr->val >> 8;
+ camif->colorfx_cr = camif->ctrl_colorfx_cbcr->val & 0xff;
+ break;
+ default:
+ /* for V4L2_COLORFX_BW and others */
+ camif->colorfx_cb = 128;
+ camif->colorfx_cr = 128;
+ }
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ camif->test_pattern = camif->ctrl_test_pattern->val;
+ break;
+ default:
+ WARN_ON(1);
+ }
+
+ camif->vp[VP_CODEC].state |= ST_VP_CONFIG;
+ camif->vp[VP_PREVIEW].state |= ST_VP_CONFIG;
+ spin_unlock_irqrestore(&camif->slock, flags);
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops s3c_camif_subdev_ctrl_ops = {
+ .s_ctrl = s3c_camif_subdev_s_ctrl,
+};
+
+static const char * const s3c_camif_test_pattern_menu[] = {
+ "Disabled",
+ "Color bars",
+ "Horizontal increment",
+ "Vertical increment",
+};
+
+int s3c_camif_create_subdev(struct camif_dev *camif)
+{
+ struct v4l2_ctrl_handler *handler = &camif->ctrl_handler;
+ struct v4l2_subdev *sd = &camif->subdev;
+ int ret;
+
+ v4l2_subdev_init(sd, &s3c_camif_subdev_ops);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ strlcpy(sd->name, "S3C-CAMIF", sizeof(sd->name));
+
+ camif->pads[CAMIF_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ camif->pads[CAMIF_SD_PAD_SOURCE_C].flags = MEDIA_PAD_FL_SOURCE;
+ camif->pads[CAMIF_SD_PAD_SOURCE_P].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_init(&sd->entity, CAMIF_SD_PADS_NUM,
+ camif->pads, 0);
+ if (ret)
+ return ret;
+
+ v4l2_ctrl_handler_init(handler, 3);
+ camif->ctrl_test_pattern = v4l2_ctrl_new_std_menu_items(handler,
+ &s3c_camif_subdev_ctrl_ops, V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(s3c_camif_test_pattern_menu) - 1, 0, 0,
+ s3c_camif_test_pattern_menu);
+
+ camif->ctrl_colorfx = v4l2_ctrl_new_std_menu(handler,
+ &s3c_camif_subdev_ctrl_ops,
+ V4L2_CID_COLORFX, V4L2_COLORFX_SET_CBCR,
+ ~0x981f, V4L2_COLORFX_NONE);
+
+ camif->ctrl_colorfx_cbcr = v4l2_ctrl_new_std(handler,
+ &s3c_camif_subdev_ctrl_ops,
+ V4L2_CID_COLORFX_CBCR, 0, 0xffff, 1, 0);
+ if (handler->error) {
+ v4l2_ctrl_handler_free(handler);
+ media_entity_cleanup(&sd->entity);
+ return handler->error;
+ }
+
+ v4l2_ctrl_auto_cluster(2, &camif->ctrl_colorfx,
+ V4L2_COLORFX_SET_CBCR, false);
+ if (!camif->variant->has_img_effect) {
+ camif->ctrl_colorfx->flags |= V4L2_CTRL_FLAG_DISABLED;
+ camif->ctrl_colorfx_cbcr->flags |= V4L2_CTRL_FLAG_DISABLED;
+ }
+ sd->ctrl_handler = handler;
+ v4l2_set_subdevdata(sd, camif);
+
+ return 0;
+}
+
+void s3c_camif_unregister_subdev(struct camif_dev *camif)
+{
+ struct v4l2_subdev *sd = &camif->subdev;
+
+ /* Return if not registered */
+ if (v4l2_get_subdevdata(sd) == NULL)
+ return;
+
+ v4l2_device_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(&camif->ctrl_handler);
+ v4l2_set_subdevdata(sd, NULL);
+}
+
+int s3c_camif_set_defaults(struct camif_dev *camif)
+{
+ unsigned int ip_rev = camif->variant->ip_revision;
+ int i;
+
+ for (i = 0; i < CAMIF_VP_NUM; i++) {
+ struct camif_vp *vp = &camif->vp[i];
+ struct camif_frame *f = &vp->out_frame;
+
+ vp->camif = camif;
+ vp->id = i;
+ vp->offset = camif->variant->vp_offset;
+
+ if (ip_rev == S3C244X_CAMIF_IP_REV)
+ vp->fmt_flags = i ? FMT_FL_S3C24XX_PREVIEW :
+ FMT_FL_S3C24XX_CODEC;
+ else
+ vp->fmt_flags = FMT_FL_S3C64XX;
+
+ vp->out_fmt = s3c_camif_find_format(vp, NULL, 0);
+ BUG_ON(vp->out_fmt == NULL);
+
+ memset(f, 0, sizeof(*f));
+ f->f_width = CAMIF_DEF_WIDTH;
+ f->f_height = CAMIF_DEF_HEIGHT;
+ f->rect.width = CAMIF_DEF_WIDTH;
+ f->rect.height = CAMIF_DEF_HEIGHT;
+
+ /* Scaler is always enabled */
+ vp->scaler.enable = 1;
+
+ vp->payload = (f->f_width * f->f_height *
+ vp->out_fmt->depth) / 8;
+ }
+
+ memset(&camif->mbus_fmt, 0, sizeof(camif->mbus_fmt));
+ camif->mbus_fmt.width = CAMIF_DEF_WIDTH;
+ camif->mbus_fmt.height = CAMIF_DEF_HEIGHT;
+ camif->mbus_fmt.code = camif_mbus_formats[0];
+
+ memset(&camif->camif_crop, 0, sizeof(camif->camif_crop));
+ camif->camif_crop.width = CAMIF_DEF_WIDTH;
+ camif->camif_crop.height = CAMIF_DEF_HEIGHT;
+
+ return 0;
+}
diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c
new file mode 100644
index 00000000000..0dd65376c06
--- /dev/null
+++ b/drivers/media/platform/s3c-camif/camif-core.c
@@ -0,0 +1,662 @@
+/*
+ * s3c24xx/s3c64xx SoC series Camera Interface (CAMIF) driver
+ *
+ * Copyright (C) 2012 Sylwester Nawrocki <sylvester.nawrocki@gmail.com>
+ * Copyright (C) 2012 Tomasz Figa <tomasz.figa@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation, either version 2 of the License,
+ * or (at your option) any later version.
+ */
+#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__
+
+#include <linux/bug.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <media/media-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "camif-core.h"
+
+static char *camif_clocks[CLK_MAX_NUM] = {
+ /* HCLK CAMIF clock */
+ [CLK_GATE] = "camif",
+ /* CAMIF / external camera sensor master clock */
+ [CLK_CAM] = "camera",
+};
+
+static const struct camif_fmt camif_formats[] = {
+ {
+ .name = "YUV 4:2:2 planar, Y/Cb/Cr",
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .depth = 16,
+ .ybpp = 1,
+ .color = IMG_FMT_YCBCR422P,
+ .colplanes = 3,
+ .flags = FMT_FL_S3C24XX_CODEC |
+ FMT_FL_S3C64XX,
+ }, {
+ .name = "YUV 4:2:0 planar, Y/Cb/Cr",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .depth = 12,
+ .ybpp = 1,
+ .color = IMG_FMT_YCBCR420,
+ .colplanes = 3,
+ .flags = FMT_FL_S3C24XX_CODEC |
+ FMT_FL_S3C64XX,
+ }, {
+ .name = "YVU 4:2:0 planar, Y/Cr/Cb",
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .depth = 12,
+ .ybpp = 1,
+ .color = IMG_FMT_YCRCB420,
+ .colplanes = 3,
+ .flags = FMT_FL_S3C24XX_CODEC |
+ FMT_FL_S3C64XX,
+ }, {
+ .name = "RGB565, 16 bpp",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .depth = 16,
+ .ybpp = 2,
+ .color = IMG_FMT_RGB565,
+ .colplanes = 1,
+ .flags = FMT_FL_S3C24XX_PREVIEW |
+ FMT_FL_S3C64XX,
+ }, {
+ .name = "XRGB8888, 32 bpp",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .depth = 32,
+ .ybpp = 4,
+ .color = IMG_FMT_XRGB8888,
+ .colplanes = 1,
+ .flags = FMT_FL_S3C24XX_PREVIEW |
+ FMT_FL_S3C64XX,
+ }, {
+ .name = "BGR666",
+ .fourcc = V4L2_PIX_FMT_BGR666,
+ .depth = 32,
+ .ybpp = 4,
+ .color = IMG_FMT_RGB666,
+ .colplanes = 1,
+ .flags = FMT_FL_S3C64XX,
+ }
+};
+
+/**
+ * s3c_camif_find_format() - lookup camif color format by fourcc or an index
+ * @pixelformat: fourcc to match, ignored if null
+ * @index: index to the camif_formats array, ignored if negative
+ */
+const struct camif_fmt *s3c_camif_find_format(struct camif_vp *vp,
+ const u32 *pixelformat,
+ int index)
+{
+ const struct camif_fmt *fmt, *def_fmt = NULL;
+ unsigned int i;
+ int id = 0;
+
+ if (index >= (int)ARRAY_SIZE(camif_formats))
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE(camif_formats); ++i) {
+ fmt = &camif_formats[i];
+ if (vp && !(vp->fmt_flags & fmt->flags))
+ continue;
+ if (pixelformat && fmt->fourcc == *pixelformat)
+ return fmt;
+ if (index == id)
+ def_fmt = fmt;
+ id++;
+ }
+ return def_fmt;
+}
+
+static int camif_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift)
+{
+ unsigned int sh = 6;
+
+ if (src >= 64 * tar)
+ return -EINVAL;
+
+ while (sh--) {
+ unsigned int tmp = 1 << sh;
+ if (src >= tar * tmp) {
+ *shift = sh, *ratio = tmp;
+ return 0;
+ }
+ }
+ *shift = 0, *ratio = 1;
+ return 0;
+}
+
+int s3c_camif_get_scaler_config(struct camif_vp *vp,
+ struct camif_scaler *scaler)
+{
+ struct v4l2_rect *camif_crop = &vp->camif->camif_crop;
+ int source_x = camif_crop->width;
+ int source_y = camif_crop->height;
+ int target_x = vp->out_frame.rect.width;
+ int target_y = vp->out_frame.rect.height;
+ int ret;
+
+ if (vp->rotation == 90 || vp->rotation == 270)
+ swap(target_x, target_y);
+
+ ret = camif_get_scaler_factor(source_x, target_x, &scaler->pre_h_ratio,
+ &scaler->h_shift);
+ if (ret < 0)
+ return ret;
+
+ ret = camif_get_scaler_factor(source_y, target_y, &scaler->pre_v_ratio,
+ &scaler->v_shift);
+ if (ret < 0)
+ return ret;
+
+ scaler->pre_dst_width = source_x / scaler->pre_h_ratio;
+ scaler->pre_dst_height = source_y / scaler->pre_v_ratio;
+
+ scaler->main_h_ratio = (source_x << 8) / (target_x << scaler->h_shift);
+ scaler->main_v_ratio = (source_y << 8) / (target_y << scaler->v_shift);
+
+ scaler->scaleup_h = (target_x >= source_x);
+ scaler->scaleup_v = (target_y >= source_y);
+
+ scaler->copy = 0;
+
+ pr_debug("H: ratio: %u, shift: %u. V: ratio: %u, shift: %u.\n",
+ scaler->pre_h_ratio, scaler->h_shift,
+ scaler->pre_v_ratio, scaler->v_shift);
+
+ pr_debug("Source: %dx%d, Target: %dx%d, scaleup_h/v: %d/%d\n",
+ source_x, source_y, target_x, target_y,
+ scaler->scaleup_h, scaler->scaleup_v);
+
+ return 0;
+}
+
+static int camif_register_sensor(struct camif_dev *camif)
+{
+ struct s3c_camif_sensor_info *sensor = &camif->pdata.sensor;
+ struct v4l2_device *v4l2_dev = &camif->v4l2_dev;
+ struct i2c_adapter *adapter;
+ struct v4l2_subdev_format format;
+ struct v4l2_subdev *sd;
+ int ret;
+
+ camif->sensor.sd = NULL;
+
+ if (sensor->i2c_board_info.addr == 0)
+ return -EINVAL;
+
+ adapter = i2c_get_adapter(sensor->i2c_bus_num);
+ if (adapter == NULL) {
+ v4l2_warn(v4l2_dev, "failed to get I2C adapter %d\n",
+ sensor->i2c_bus_num);
+ return -EPROBE_DEFER;
+ }
+
+ sd = v4l2_i2c_new_subdev_board(v4l2_dev, adapter,
+ &sensor->i2c_board_info, NULL);
+ if (sd == NULL) {
+ i2c_put_adapter(adapter);
+ v4l2_warn(v4l2_dev, "failed to acquire subdev %s\n",
+ sensor->i2c_board_info.type);
+ return -EPROBE_DEFER;
+ }
+ camif->sensor.sd = sd;
+
+ v4l2_info(v4l2_dev, "registered sensor subdevice %s\n", sd->name);
+
+ /* Get initial pixel format and set it at the camif sink pad */
+ format.pad = 0;
+ format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &format);
+
+ if (ret < 0)
+ return 0;
+
+ format.pad = CAMIF_SD_PAD_SINK;
+ v4l2_subdev_call(&camif->subdev, pad, set_fmt, NULL, &format);
+
+ v4l2_info(sd, "Initial format from sensor: %dx%d, %#x\n",
+ format.format.width, format.format.height,
+ format.format.code);
+ return 0;
+}
+
+static void camif_unregister_sensor(struct camif_dev *camif)
+{
+ struct v4l2_subdev *sd = camif->sensor.sd;
+ struct i2c_client *client = sd ? v4l2_get_subdevdata(sd) : NULL;
+ struct i2c_adapter *adapter;
+
+ if (client == NULL)
+ return;
+
+ adapter = client->adapter;
+ v4l2_device_unregister_subdev(sd);
+ camif->sensor.sd = NULL;
+ i2c_unregister_device(client);
+ if (adapter)
+ i2c_put_adapter(adapter);
+}
+
+static int camif_create_media_links(struct camif_dev *camif)
+{
+ int i, ret;
+
+ ret = media_entity_create_link(&camif->sensor.sd->entity, 0,
+ &camif->subdev.entity, CAMIF_SD_PAD_SINK,
+ MEDIA_LNK_FL_IMMUTABLE |
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+
+ for (i = 1; i < CAMIF_SD_PADS_NUM && !ret; i++) {
+ ret = media_entity_create_link(&camif->subdev.entity, i,
+ &camif->vp[i - 1].vdev.entity, 0,
+ MEDIA_LNK_FL_IMMUTABLE |
+ MEDIA_LNK_FL_ENABLED);
+ }
+
+ return ret;
+}
+
+static int camif_register_video_nodes(struct camif_dev *camif)
+{
+ int ret = s3c_camif_register_video_node(camif, VP_CODEC);
+ if (ret < 0)
+ return ret;
+
+ return s3c_camif_register_video_node(camif, VP_PREVIEW);
+}
+
+static void camif_unregister_video_nodes(struct camif_dev *camif)
+{
+ s3c_camif_unregister_video_node(camif, VP_CODEC);
+ s3c_camif_unregister_video_node(camif, VP_PREVIEW);
+}
+
+static void camif_unregister_media_entities(struct camif_dev *camif)
+{
+ camif_unregister_video_nodes(camif);
+ camif_unregister_sensor(camif);
+ s3c_camif_unregister_subdev(camif);
+}
+
+/*
+ * Media device
+ */
+static int camif_media_dev_register(struct camif_dev *camif)
+{
+ struct media_device *md = &camif->media_dev;
+ struct v4l2_device *v4l2_dev = &camif->v4l2_dev;
+ unsigned int ip_rev = camif->variant->ip_revision;
+ int ret;
+
+ memset(md, 0, sizeof(*md));
+ snprintf(md->model, sizeof(md->model), "SAMSUNG S3C%s CAMIF",
+ ip_rev == S3C6410_CAMIF_IP_REV ? "6410" : "244X");
+ strlcpy(md->bus_info, "platform", sizeof(md->bus_info));
+ md->hw_revision = ip_rev;
+ md->driver_version = KERNEL_VERSION(1, 0, 0);
+
+ md->dev = camif->dev;
+
+ strlcpy(v4l2_dev->name, "s3c-camif", sizeof(v4l2_dev->name));
+ v4l2_dev->mdev = md;
+
+ ret = v4l2_device_register(camif->dev, v4l2_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = media_device_register(md);
+ if (ret < 0)
+ v4l2_device_unregister(v4l2_dev);
+
+ return ret;
+}
+
+static void camif_clk_put(struct camif_dev *camif)
+{
+ int i;
+
+ for (i = 0; i < CLK_MAX_NUM; i++) {
+ if (IS_ERR_OR_NULL(camif->clock[i]))
+ continue;
+ clk_unprepare(camif->clock[i]);
+ clk_put(camif->clock[i]);
+ }
+}
+
+static int camif_clk_get(struct camif_dev *camif)
+{
+ int ret, i;
+
+ for (i = 0; i < CLK_MAX_NUM; i++) {
+ camif->clock[i] = clk_get(camif->dev, camif_clocks[i]);
+ if (IS_ERR(camif->clock[i])) {
+ ret = PTR_ERR(camif->clock[i]);
+ goto err;
+ }
+ ret = clk_prepare(camif->clock[i]);
+ if (ret < 0) {
+ clk_put(camif->clock[i]);
+ camif->clock[i] = NULL;
+ goto err;
+ }
+ }
+ return 0;
+err:
+ camif_clk_put(camif);
+ dev_err(camif->dev, "failed to get clock: %s\n",
+ camif_clocks[i]);
+ return ret;
+}
+
+/*
+ * The CAMIF device has two relatively independent data processing paths
+ * that can source data from memory or the common camera input frontend.
+ * Register interrupts for each data processing path (camif_vp).
+ */
+static int camif_request_irqs(struct platform_device *pdev,
+ struct camif_dev *camif)
+{
+ int irq, ret, i;
+
+ for (i = 0; i < CAMIF_VP_NUM; i++) {
+ struct camif_vp *vp = &camif->vp[i];
+
+ init_waitqueue_head(&vp->irq_queue);
+
+ irq = platform_get_irq(pdev, i);
+ if (irq <= 0) {
+ dev_err(&pdev->dev, "failed to get IRQ %d\n", i);
+ return -ENXIO;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, s3c_camif_irq_handler,
+ 0, dev_name(&pdev->dev), vp);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to install IRQ: %d\n", ret);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int s3c_camif_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct s3c_camif_plat_data *pdata = dev->platform_data;
+ struct s3c_camif_drvdata *drvdata;
+ struct camif_dev *camif;
+ struct resource *mres;
+ int ret = 0;
+
+ camif = devm_kzalloc(dev, sizeof(*camif), GFP_KERNEL);
+ if (!camif)
+ return -ENOMEM;
+
+ spin_lock_init(&camif->slock);
+ mutex_init(&camif->lock);
+
+ camif->dev = dev;
+
+ if (!pdata || !pdata->gpio_get || !pdata->gpio_put) {
+ dev_err(dev, "wrong platform data\n");
+ return -EINVAL;
+ }
+
+ camif->pdata = *pdata;
+ drvdata = (void *)platform_get_device_id(pdev)->driver_data;
+ camif->variant = drvdata->variant;
+
+ mres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ camif->io_base = devm_request_and_ioremap(dev, mres);
+ if (!camif->io_base) {
+ dev_err(dev, "failed to obtain I/O memory\n");
+ return -ENOENT;
+ }
+
+ ret = camif_request_irqs(pdev, camif);
+ if (ret < 0)
+ return ret;
+
+ ret = pdata->gpio_get();
+ if (ret < 0)
+ return ret;
+
+ ret = s3c_camif_create_subdev(camif);
+ if (ret < 0)
+ goto err_sd;
+
+ ret = camif_clk_get(camif);
+ if (ret < 0)
+ goto err_clk;
+
+ platform_set_drvdata(pdev, camif);
+ clk_set_rate(camif->clock[CLK_CAM],
+ camif->pdata.sensor.clock_frequency);
+
+ dev_info(dev, "sensor clock frequency: %lu\n",
+ clk_get_rate(camif->clock[CLK_CAM]));
+ /*
+ * Set initial pixel format, resolution and crop rectangle.
+ * Must be done before a sensor subdev is registered as some
+ * settings are overrode with values from sensor subdev.
+ */
+ s3c_camif_set_defaults(camif);
+
+ pm_runtime_enable(dev);
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0)
+ goto err_pm;
+
+ /* Initialize contiguous memory allocator */
+ camif->alloc_ctx = vb2_dma_contig_init_ctx(dev);
+ if (IS_ERR(camif->alloc_ctx)) {
+ ret = PTR_ERR(camif->alloc_ctx);
+ goto err_alloc;
+ }
+
+ ret = camif_media_dev_register(camif);
+ if (ret < 0)
+ goto err_mdev;
+
+ ret = camif_register_sensor(camif);
+ if (ret < 0)
+ goto err_sens;
+
+ ret = v4l2_device_register_subdev(&camif->v4l2_dev, &camif->subdev);
+ if (ret < 0)
+ goto err_sens;
+
+ mutex_lock(&camif->media_dev.graph_mutex);
+
+ ret = v4l2_device_register_subdev_nodes(&camif->v4l2_dev);
+ if (ret < 0)
+ goto err_unlock;
+
+ ret = camif_register_video_nodes(camif);
+ if (ret < 0)
+ goto err_unlock;
+
+ ret = camif_create_media_links(camif);
+ if (ret < 0)
+ goto err_unlock;
+
+ mutex_unlock(&camif->media_dev.graph_mutex);
+ pm_runtime_put(dev);
+ return 0;
+
+err_unlock:
+ mutex_unlock(&camif->media_dev.graph_mutex);
+err_sens:
+ v4l2_device_unregister(&camif->v4l2_dev);
+ media_device_unregister(&camif->media_dev);
+ camif_unregister_media_entities(camif);
+err_mdev:
+ vb2_dma_contig_cleanup_ctx(camif->alloc_ctx);
+err_alloc:
+ pm_runtime_put(dev);
+ pm_runtime_disable(dev);
+err_pm:
+ camif_clk_put(camif);
+err_clk:
+ s3c_camif_unregister_subdev(camif);
+err_sd:
+ pdata->gpio_put();
+ return ret;
+}
+
+static int __devexit s3c_camif_remove(struct platform_device *pdev)
+{
+ struct camif_dev *camif = platform_get_drvdata(pdev);
+ struct s3c_camif_plat_data *pdata = &camif->pdata;
+
+ media_device_unregister(&camif->media_dev);
+ camif_unregister_media_entities(camif);
+ v4l2_device_unregister(&camif->v4l2_dev);
+
+ pm_runtime_disable(&pdev->dev);
+ camif_clk_put(camif);
+ pdata->gpio_put();
+
+ return 0;
+}
+
+static int s3c_camif_runtime_resume(struct device *dev)
+{
+ struct camif_dev *camif = dev_get_drvdata(dev);
+
+ clk_enable(camif->clock[CLK_GATE]);
+ /* null op on s3c244x */
+ clk_enable(camif->clock[CLK_CAM]);
+ return 0;
+}
+
+static int s3c_camif_runtime_suspend(struct device *dev)
+{
+ struct camif_dev *camif = dev_get_drvdata(dev);
+
+ /* null op on s3c244x */
+ clk_disable(camif->clock[CLK_CAM]);
+
+ clk_disable(camif->clock[CLK_GATE]);
+ return 0;
+}
+
+static const struct s3c_camif_variant s3c244x_camif_variant = {
+ .vp_pix_limits = {
+ [VP_CODEC] = {
+ .max_out_width = 4096,
+ .max_sc_out_width = 2048,
+ .out_width_align = 16,
+ .min_out_width = 16,
+ .max_height = 4096,
+ },
+ [VP_PREVIEW] = {
+ .max_out_width = 640,
+ .max_sc_out_width = 640,
+ .out_width_align = 16,
+ .min_out_width = 16,
+ .max_height = 480,
+ }
+ },
+ .pix_limits = {
+ .win_hor_offset_align = 8,
+ },
+ .ip_revision = S3C244X_CAMIF_IP_REV,
+};
+
+static struct s3c_camif_drvdata s3c244x_camif_drvdata = {
+ .variant = &s3c244x_camif_variant,
+ .bus_clk_freq = 24000000UL,
+};
+
+static const struct s3c_camif_variant s3c6410_camif_variant = {
+ .vp_pix_limits = {
+ [VP_CODEC] = {
+ .max_out_width = 4096,
+ .max_sc_out_width = 2048,
+ .out_width_align = 16,
+ .min_out_width = 16,
+ .max_height = 4096,
+ },
+ [VP_PREVIEW] = {
+ .max_out_width = 4096,
+ .max_sc_out_width = 720,
+ .out_width_align = 16,
+ .min_out_width = 16,
+ .max_height = 4096,
+ }
+ },
+ .pix_limits = {
+ .win_hor_offset_align = 8,
+ },
+ .ip_revision = S3C6410_CAMIF_IP_REV,
+ .has_img_effect = 1,
+ .vp_offset = 0x20,
+};
+
+static struct s3c_camif_drvdata s3c6410_camif_drvdata = {
+ .variant = &s3c6410_camif_variant,
+ .bus_clk_freq = 133000000UL,
+};
+
+static struct platform_device_id s3c_camif_driver_ids[] = {
+ {
+ .name = "s3c2440-camif",
+ .driver_data = (unsigned long)&s3c244x_camif_drvdata,
+ }, {
+ .name = "s3c6410-camif",
+ .driver_data = (unsigned long)&s3c6410_camif_drvdata,
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(platform, s3c_camif_driver_ids);
+
+static const struct dev_pm_ops s3c_camif_pm_ops = {
+ .runtime_suspend = s3c_camif_runtime_suspend,
+ .runtime_resume = s3c_camif_runtime_resume,
+};
+
+static struct platform_driver s3c_camif_driver = {
+ .probe = s3c_camif_probe,
+ .remove = __devexit_p(s3c_camif_remove),
+ .id_table = s3c_camif_driver_ids,
+ .driver = {
+ .name = S3C_CAMIF_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = &s3c_camif_pm_ops,
+ }
+};
+
+module_platform_driver(s3c_camif_driver);
+
+MODULE_AUTHOR("Sylwester Nawrocki <sylvester.nawrocki@gmail.com>");
+MODULE_AUTHOR("Tomasz Figa <tomasz.figa@gmail.com>");
+MODULE_DESCRIPTION("S3C24XX/S3C64XX SoC camera interface driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/s3c-camif/camif-core.h b/drivers/media/platform/s3c-camif/camif-core.h
new file mode 100644
index 00000000000..261134baa65
--- /dev/null
+++ b/drivers/media/platform/s3c-camif/camif-core.h
@@ -0,0 +1,393 @@
+/*
+ * s3c24xx/s3c64xx SoC series Camera Interface (CAMIF) driver
+ *
+ * Copyright (C) 2012 Sylwester Nawrocki <sylvester.nawrocki@gmail.com>
+ * Copyright (C) 2012 Tomasz Figa <tomasz.figa@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef CAMIF_CORE_H_
+#define CAMIF_CORE_H_
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+#include <media/videobuf2-core.h>
+#include <media/s3c_camif.h>
+
+#define S3C_CAMIF_DRIVER_NAME "s3c-camif"
+#define CAMIF_REQ_BUFS_MIN 3
+#define CAMIF_MAX_OUT_BUFS 4
+#define CAMIF_MAX_PIX_WIDTH 4096
+#define CAMIF_MAX_PIX_HEIGHT 4096
+#define SCALER_MAX_RATIO 64
+#define CAMIF_DEF_WIDTH 640
+#define CAMIF_DEF_HEIGHT 480
+#define CAMIF_STOP_TIMEOUT 1500 /* ms */
+
+#define S3C244X_CAMIF_IP_REV 0x20 /* 2.0 */
+#define S3C2450_CAMIF_IP_REV 0x30 /* 3.0 - not implemented, not tested */
+#define S3C6400_CAMIF_IP_REV 0x31 /* 3.1 - not implemented, not tested */
+#define S3C6410_CAMIF_IP_REV 0x32 /* 3.2 */
+
+/* struct camif_vp::state */
+
+#define ST_VP_PENDING (1 << 0)
+#define ST_VP_RUNNING (1 << 1)
+#define ST_VP_STREAMING (1 << 2)
+#define ST_VP_SENSOR_STREAMING (1 << 3)
+
+#define ST_VP_ABORTING (1 << 4)
+#define ST_VP_OFF (1 << 5)
+#define ST_VP_LASTIRQ (1 << 6)
+
+#define ST_VP_CONFIG (1 << 8)
+
+#define CAMIF_SD_PAD_SINK 0
+#define CAMIF_SD_PAD_SOURCE_C 1
+#define CAMIF_SD_PAD_SOURCE_P 2
+#define CAMIF_SD_PADS_NUM 3
+
+enum img_fmt {
+ IMG_FMT_RGB565 = 0x0010,
+ IMG_FMT_RGB666,
+ IMG_FMT_XRGB8888,
+ IMG_FMT_YCBCR420 = 0x0020,
+ IMG_FMT_YCRCB420,
+ IMG_FMT_YCBCR422P,
+ IMG_FMT_YCBYCR422 = 0x0040,
+ IMG_FMT_YCRYCB422,
+ IMG_FMT_CBYCRY422,
+ IMG_FMT_CRYCBY422,
+};
+
+#define img_fmt_is_rgb(x) ((x) & 0x10)
+#define img_fmt_is_ycbcr(x) ((x) & 0x60)
+
+/* Possible values for struct camif_fmt::flags */
+#define FMT_FL_S3C24XX_CODEC (1 << 0)
+#define FMT_FL_S3C24XX_PREVIEW (1 << 1)
+#define FMT_FL_S3C64XX (1 << 2)
+
+/**
+ * struct camif_fmt - pixel format description
+ * @fourcc: fourcc code for this format, 0 if not applicable
+ * @color: a corresponding enum img_fmt
+ * @colplanes: number of physically contiguous data planes
+ * @flags: indicate for which SoCs revisions this format is valid
+ * @depth: bits per pixel (total)
+ * @ybpp: number of luminance bytes per pixel
+ */
+struct camif_fmt {
+ char *name;
+ u32 fourcc;
+ u32 color;
+ u16 colplanes;
+ u16 flags;
+ u8 depth;
+ u8 ybpp;
+};
+
+/**
+ * struct camif_dma_offset - pixel offset information for DMA
+ * @initial: offset (in pixels) to first pixel
+ * @line: offset (in pixels) from end of line to start of next line
+ */
+struct camif_dma_offset {
+ int initial;
+ int line;
+};
+
+/**
+ * struct camif_frame - source/target frame properties
+ * @f_width: full pixel width
+ * @f_height: full pixel height
+ * @rect: crop/composition rectangle
+ * @dma_offset: DMA offset configuration
+ */
+struct camif_frame {
+ u16 f_width;
+ u16 f_height;
+ struct v4l2_rect rect;
+ struct camif_dma_offset dma_offset;
+};
+
+/* CAMIF clocks enumeration */
+enum {
+ CLK_GATE,
+ CLK_CAM,
+ CLK_MAX_NUM,
+};
+
+struct vp_pix_limits {
+ u16 max_out_width;
+ u16 max_sc_out_width;
+ u16 out_width_align;
+ u16 max_height;
+ u8 min_out_width;
+ u16 out_hor_offset_align;
+};
+
+struct camif_pix_limits {
+ u16 win_hor_offset_align;
+};
+
+/**
+ * struct s3c_camif_variant - CAMIF variant structure
+ * @vp_pix_limits: pixel limits for the codec and preview paths
+ * @camif_pix_limits: pixel limits for the camera input interface
+ * @ip_revision: the CAMIF IP revision: 0x20 for s3c244x, 0x32 for s3c6410
+ */
+struct s3c_camif_variant {
+ struct vp_pix_limits vp_pix_limits[2];
+ struct camif_pix_limits pix_limits;
+ u8 ip_revision;
+ u8 has_img_effect;
+ unsigned int vp_offset;
+};
+
+struct s3c_camif_drvdata {
+ const struct s3c_camif_variant *variant;
+ unsigned long bus_clk_freq;
+};
+
+struct camif_scaler {
+ u8 scaleup_h;
+ u8 scaleup_v;
+ u8 copy;
+ u8 enable;
+ u32 h_shift;
+ u32 v_shift;
+ u32 pre_h_ratio;
+ u32 pre_v_ratio;
+ u32 pre_dst_width;
+ u32 pre_dst_height;
+ u32 main_h_ratio;
+ u32 main_v_ratio;
+};
+
+struct camif_dev;
+
+/**
+ * struct camif_vp - CAMIF data processing path structure (codec/preview)
+ * @irq_queue: interrupt handling waitqueue
+ * @irq: interrupt number for this data path
+ * @camif: pointer to the camif structure
+ * @pad: media pad for the video node
+ * @vdev video device
+ * @ctrl_handler: video node controls handler
+ * @owner: file handle that own the streaming
+ * @pending_buf_q: pending (empty) buffers queue head
+ * @active_buf_q: active (being written) buffers queue head
+ * @active_buffers: counter of buffer set up at the DMA engine
+ * @buf_index: identifier of a last empty buffer set up in H/W
+ * @frame_sequence: image frame sequence counter
+ * @reqbufs_count: the number of buffers requested
+ * @scaler: the scaler structure
+ * @out_fmt: pixel format at this video path output
+ * @payload: the output data frame payload size
+ * @out_frame: the output pixel resolution
+ * @state: the video path's state
+ * @fmt_flags: flags determining supported pixel formats
+ * @id: CAMIF id, 0 - codec, 1 - preview
+ * @rotation: current image rotation value
+ * @hflip: apply horizontal flip if set
+ * @vflip: apply vertical flip if set
+ */
+struct camif_vp {
+ wait_queue_head_t irq_queue;
+ int irq;
+ struct camif_dev *camif;
+ struct media_pad pad;
+ struct video_device vdev;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_fh *owner;
+ struct vb2_queue vb_queue;
+ struct list_head pending_buf_q;
+ struct list_head active_buf_q;
+ unsigned int active_buffers;
+ unsigned int buf_index;
+ unsigned int frame_sequence;
+ unsigned int reqbufs_count;
+ struct camif_scaler scaler;
+ const struct camif_fmt *out_fmt;
+ unsigned int payload;
+ struct camif_frame out_frame;
+ unsigned int state;
+ u16 fmt_flags;
+ u8 id;
+ u8 rotation;
+ u8 hflip;
+ u8 vflip;
+ unsigned int offset;
+};
+
+/* Video processing path enumeration */
+#define VP_CODEC 0
+#define VP_PREVIEW 1
+#define CAMIF_VP_NUM 2
+
+/**
+ * struct camif_dev - the CAMIF driver private data structure
+ * @media_dev: top-level media device structure
+ * @v4l2_dev: root v4l2_device
+ * @subdev: camera interface ("catchcam") subdev
+ * @mbus_fmt: camera input media bus format
+ * @camif_crop: camera input interface crop rectangle
+ * @pads: the camif subdev's media pads
+ * @stream_count: the camera interface streaming reference counter
+ * @sensor: image sensor data structure
+ * @m_pipeline: video entity pipeline description
+ * @ctrl_handler: v4l2 control handler (owned by @subdev)
+ * @test_pattern: test pattern controls
+ * @vp: video path (DMA) description (codec/preview)
+ * @alloc_ctx: memory buffer allocator context
+ * @variant: variant information for this device
+ * @dev: pointer to the CAMIF device struct
+ * @pdata: a copy of the driver's platform data
+ * @clock: clocks required for the CAMIF operation
+ * @lock: mutex protecting this data structure
+ * @slock: spinlock protecting CAMIF registers
+ * @io_base: start address of the mmaped CAMIF registers
+ */
+struct camif_dev {
+ struct media_device media_dev;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_subdev subdev;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ struct v4l2_rect camif_crop;
+ struct media_pad pads[CAMIF_SD_PADS_NUM];
+ int stream_count;
+
+ struct cam_sensor {
+ struct v4l2_subdev *sd;
+ short power_count;
+ short stream_count;
+ } sensor;
+ struct media_pipeline *m_pipeline;
+
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *ctrl_test_pattern;
+ struct {
+ struct v4l2_ctrl *ctrl_colorfx;
+ struct v4l2_ctrl *ctrl_colorfx_cbcr;
+ };
+ u8 test_pattern;
+ u8 colorfx;
+ u8 colorfx_cb;
+ u8 colorfx_cr;
+
+ struct camif_vp vp[CAMIF_VP_NUM];
+ struct vb2_alloc_ctx *alloc_ctx;
+
+ const struct s3c_camif_variant *variant;
+ struct device *dev;
+ struct s3c_camif_plat_data pdata;
+ struct clk *clock[CLK_MAX_NUM];
+ struct mutex lock;
+ spinlock_t slock;
+ void __iomem *io_base;
+};
+
+/**
+ * struct camif_addr - Y/Cb/Cr DMA start address structure
+ * @y: luminance plane dma address
+ * @cb: Cb plane dma address
+ * @cr: Cr plane dma address
+ */
+struct camif_addr {
+ dma_addr_t y;
+ dma_addr_t cb;
+ dma_addr_t cr;
+};
+
+/**
+ * struct camif_buffer - the camif video buffer structure
+ * @vb: vb2 buffer
+ * @list: list head for the buffers queue
+ * @paddr: DMA start addresses
+ * @index: an identifier of this buffer at the DMA engine
+ */
+struct camif_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+ struct camif_addr paddr;
+ unsigned int index;
+};
+
+const struct camif_fmt *s3c_camif_find_format(struct camif_vp *vp,
+ const u32 *pixelformat, int index);
+int s3c_camif_register_video_node(struct camif_dev *camif, int idx);
+void s3c_camif_unregister_video_node(struct camif_dev *camif, int idx);
+irqreturn_t s3c_camif_irq_handler(int irq, void *priv);
+int s3c_camif_create_subdev(struct camif_dev *camif);
+void s3c_camif_unregister_subdev(struct camif_dev *camif);
+int s3c_camif_set_defaults(struct camif_dev *camif);
+int s3c_camif_get_scaler_config(struct camif_vp *vp,
+ struct camif_scaler *scaler);
+
+static inline void camif_active_queue_add(struct camif_vp *vp,
+ struct camif_buffer *buf)
+{
+ list_add_tail(&buf->list, &vp->active_buf_q);
+ vp->active_buffers++;
+}
+
+static inline struct camif_buffer *camif_active_queue_pop(
+ struct camif_vp *vp)
+{
+ struct camif_buffer *buf = list_first_entry(&vp->active_buf_q,
+ struct camif_buffer, list);
+ list_del(&buf->list);
+ vp->active_buffers--;
+ return buf;
+}
+
+static inline struct camif_buffer *camif_active_queue_peek(
+ struct camif_vp *vp, int index)
+{
+ struct camif_buffer *tmp, *buf;
+
+ if (WARN_ON(list_empty(&vp->active_buf_q)))
+ return NULL;
+
+ list_for_each_entry_safe(buf, tmp, &vp->active_buf_q, list) {
+ if (buf->index == index) {
+ list_del(&buf->list);
+ vp->active_buffers--;
+ return buf;
+ }
+ }
+
+ return NULL;
+}
+
+static inline void camif_pending_queue_add(struct camif_vp *vp,
+ struct camif_buffer *buf)
+{
+ list_add_tail(&buf->list, &vp->pending_buf_q);
+}
+
+static inline struct camif_buffer *camif_pending_queue_pop(
+ struct camif_vp *vp)
+{
+ struct camif_buffer *buf = list_first_entry(&vp->pending_buf_q,
+ struct camif_buffer, list);
+ list_del(&buf->list);
+ return buf;
+}
+
+#endif /* CAMIF_CORE_H_ */
diff --git a/drivers/media/platform/s3c-camif/camif-regs.c b/drivers/media/platform/s3c-camif/camif-regs.c
new file mode 100644
index 00000000000..1a3b4fc05ec
--- /dev/null
+++ b/drivers/media/platform/s3c-camif/camif-regs.c
@@ -0,0 +1,606 @@
+/*
+ * Samsung s3c24xx/s3c64xx SoC CAMIF driver
+ *
+ * Copyright (C) 2012 Sylwester Nawrocki <sylvester.nawrocki@gmail.com>
+ * Copyright (C) 2012 Tomasz Figa <tomasz.figa@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__
+
+#include <linux/delay.h>
+#include "camif-regs.h"
+
+#define camif_write(_camif, _off, _val) writel(_val, (_camif)->io_base + (_off))
+#define camif_read(_camif, _off) readl((_camif)->io_base + (_off))
+
+void camif_hw_reset(struct camif_dev *camif)
+{
+ u32 cfg;
+
+ cfg = camif_read(camif, S3C_CAMIF_REG_CISRCFMT);
+ cfg |= CISRCFMT_ITU601_8BIT;
+ camif_write(camif, S3C_CAMIF_REG_CISRCFMT, cfg);
+
+ /* S/W reset */
+ cfg = camif_read(camif, S3C_CAMIF_REG_CIGCTRL);
+ cfg |= CIGCTRL_SWRST;
+ if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV)
+ cfg |= CIGCTRL_IRQ_LEVEL;
+ camif_write(camif, S3C_CAMIF_REG_CIGCTRL, cfg);
+ udelay(10);
+
+ cfg = camif_read(camif, S3C_CAMIF_REG_CIGCTRL);
+ cfg &= ~CIGCTRL_SWRST;
+ camif_write(camif, S3C_CAMIF_REG_CIGCTRL, cfg);
+ udelay(10);
+}
+
+void camif_hw_clear_pending_irq(struct camif_vp *vp)
+{
+ u32 cfg = camif_read(vp->camif, S3C_CAMIF_REG_CIGCTRL);
+ cfg |= CIGCTRL_IRQ_CLR(vp->id);
+ camif_write(vp->camif, S3C_CAMIF_REG_CIGCTRL, cfg);
+}
+
+/*
+ * Sets video test pattern (off, color bar, horizontal or vertical gradient).
+ * External sensor pixel clock must be active for the test pattern to work.
+ */
+void camif_hw_set_test_pattern(struct camif_dev *camif, unsigned int pattern)
+{
+ u32 cfg = camif_read(camif, S3C_CAMIF_REG_CIGCTRL);
+ cfg &= ~CIGCTRL_TESTPATTERN_MASK;
+ cfg |= (pattern << 27);
+ camif_write(camif, S3C_CAMIF_REG_CIGCTRL, cfg);
+}
+
+void camif_hw_set_effect(struct camif_dev *camif, unsigned int effect,
+ unsigned int cr, unsigned int cb)
+{
+ static const struct v4l2_control colorfx[] = {
+ { V4L2_COLORFX_NONE, CIIMGEFF_FIN_BYPASS },
+ { V4L2_COLORFX_BW, CIIMGEFF_FIN_ARBITRARY },
+ { V4L2_COLORFX_SEPIA, CIIMGEFF_FIN_ARBITRARY },
+ { V4L2_COLORFX_NEGATIVE, CIIMGEFF_FIN_NEGATIVE },
+ { V4L2_COLORFX_ART_FREEZE, CIIMGEFF_FIN_ARTFREEZE },
+ { V4L2_COLORFX_EMBOSS, CIIMGEFF_FIN_EMBOSSING },
+ { V4L2_COLORFX_SILHOUETTE, CIIMGEFF_FIN_SILHOUETTE },
+ { V4L2_COLORFX_SET_CBCR, CIIMGEFF_FIN_ARBITRARY },
+ };
+ unsigned int i, cfg;
+
+ for (i = 0; i < ARRAY_SIZE(colorfx); i++)
+ if (colorfx[i].id == effect)
+ break;
+
+ if (i == ARRAY_SIZE(colorfx))
+ return;
+
+ cfg = camif_read(camif, S3C_CAMIF_REG_CIIMGEFF(camif->vp->offset));
+ /* Set effect */
+ cfg &= ~CIIMGEFF_FIN_MASK;
+ cfg |= colorfx[i].value;
+ /* Set both paths */
+ if (camif->variant->ip_revision >= S3C6400_CAMIF_IP_REV) {
+ if (effect == V4L2_COLORFX_NONE)
+ cfg &= ~CIIMGEFF_IE_ENABLE_MASK;
+ else
+ cfg |= CIIMGEFF_IE_ENABLE_MASK;
+ }
+ cfg &= ~CIIMGEFF_PAT_CBCR_MASK;
+ cfg |= cr | (cb << 13);
+ camif_write(camif, S3C_CAMIF_REG_CIIMGEFF(camif->vp->offset), cfg);
+}
+
+static const u32 src_pixfmt_map[8][2] = {
+ { V4L2_MBUS_FMT_YUYV8_2X8, CISRCFMT_ORDER422_YCBYCR },
+ { V4L2_MBUS_FMT_YVYU8_2X8, CISRCFMT_ORDER422_YCRYCB },
+ { V4L2_MBUS_FMT_UYVY8_2X8, CISRCFMT_ORDER422_CBYCRY },
+ { V4L2_MBUS_FMT_VYUY8_2X8, CISRCFMT_ORDER422_CRYCBY },
+};
+
+/* Set camera input pixel format and resolution */
+void camif_hw_set_source_format(struct camif_dev *camif)
+{
+ struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt;
+ unsigned int i = ARRAY_SIZE(src_pixfmt_map);
+ u32 cfg;
+
+ while (i-- >= 0) {
+ if (src_pixfmt_map[i][0] == mf->code)
+ break;
+ }
+
+ if (i == 0 && src_pixfmt_map[i][0] != mf->code) {
+ dev_err(camif->dev,
+ "Unsupported pixel code, falling back to %#08x\n",
+ src_pixfmt_map[i][0]);
+ }
+
+ cfg = camif_read(camif, S3C_CAMIF_REG_CISRCFMT);
+ cfg &= ~(CISRCFMT_ORDER422_MASK | CISRCFMT_SIZE_CAM_MASK);
+ cfg |= (mf->width << 16) | mf->height;
+ cfg |= src_pixfmt_map[i][1];
+ camif_write(camif, S3C_CAMIF_REG_CISRCFMT, cfg);
+}
+
+/* Set the camera host input window offsets (cropping) */
+void camif_hw_set_camera_crop(struct camif_dev *camif)
+{
+ struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt;
+ struct v4l2_rect *crop = &camif->camif_crop;
+ u32 hoff2, voff2;
+ u32 cfg;
+
+ /* Note: s3c244x requirement: left = f_width - rect.width / 2 */
+ cfg = camif_read(camif, S3C_CAMIF_REG_CIWDOFST);
+ cfg &= ~(CIWDOFST_OFST_MASK | CIWDOFST_WINOFSEN);
+ cfg |= (crop->left << 16) | crop->top;
+ if (crop->left != 0 || crop->top != 0)
+ cfg |= CIWDOFST_WINOFSEN;
+ camif_write(camif, S3C_CAMIF_REG_CIWDOFST, cfg);
+
+ if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV) {
+ hoff2 = mf->width - crop->width - crop->left;
+ voff2 = mf->height - crop->height - crop->top;
+ cfg = (hoff2 << 16) | voff2;
+ camif_write(camif, S3C_CAMIF_REG_CIWDOFST2, cfg);
+ }
+}
+
+void camif_hw_clear_fifo_overflow(struct camif_vp *vp)
+{
+ struct camif_dev *camif = vp->camif;
+ u32 cfg;
+
+ cfg = camif_read(camif, S3C_CAMIF_REG_CIWDOFST);
+ if (vp->id == 0)
+ cfg |= (CIWDOFST_CLROVCOFIY | CIWDOFST_CLROVCOFICB |
+ CIWDOFST_CLROVCOFICR);
+ else
+ cfg |= (/* CIWDOFST_CLROVPRFIY | */ CIWDOFST_CLROVPRFICB |
+ CIWDOFST_CLROVPRFICR);
+ camif_write(camif, S3C_CAMIF_REG_CIWDOFST, cfg);
+}
+
+/* Set video bus signals polarity */
+void camif_hw_set_camera_bus(struct camif_dev *camif)
+{
+ unsigned int flags = camif->pdata.sensor.flags;
+
+ u32 cfg = camif_read(camif, S3C_CAMIF_REG_CIGCTRL);
+
+ cfg &= ~(CIGCTRL_INVPOLPCLK | CIGCTRL_INVPOLVSYNC |
+ CIGCTRL_INVPOLHREF | CIGCTRL_INVPOLFIELD);
+
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ cfg |= CIGCTRL_INVPOLPCLK;
+
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ cfg |= CIGCTRL_INVPOLVSYNC;
+ /*
+ * HREF is normally high during frame active data
+ * transmission and low during horizontal synchronization
+ * period. Thus HREF active high means HSYNC active low.
+ */
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
+ cfg |= CIGCTRL_INVPOLHREF; /* HREF active low */
+
+ if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV) {
+ if (flags & V4L2_MBUS_FIELD_EVEN_LOW)
+ cfg |= CIGCTRL_INVPOLFIELD;
+ cfg |= CIGCTRL_FIELDMODE;
+ }
+
+ pr_debug("Setting CIGCTRL to: %#x\n", cfg);
+
+ camif_write(camif, S3C_CAMIF_REG_CIGCTRL, cfg);
+}
+
+void camif_hw_set_output_addr(struct camif_vp *vp,
+ struct camif_addr *paddr, int i)
+{
+ struct camif_dev *camif = vp->camif;
+
+ camif_write(camif, S3C_CAMIF_REG_CIYSA(vp->id, i), paddr->y);
+ if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV
+ || vp->id == VP_CODEC) {
+ camif_write(camif, S3C_CAMIF_REG_CICBSA(vp->id, i),
+ paddr->cb);
+ camif_write(camif, S3C_CAMIF_REG_CICRSA(vp->id, i),
+ paddr->cr);
+ }
+
+ pr_debug("dst_buf[%d]: %#X, cb: %#X, cr: %#X\n",
+ i, paddr->y, paddr->cb, paddr->cr);
+}
+
+static void camif_hw_set_out_dma_size(struct camif_vp *vp)
+{
+ struct camif_frame *frame = &vp->out_frame;
+ u32 cfg;
+
+ cfg = camif_read(vp->camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset));
+ cfg &= ~CITRGFMT_TARGETSIZE_MASK;
+ cfg |= (frame->f_width << 16) | frame->f_height;
+ camif_write(vp->camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset), cfg);
+}
+
+static void camif_get_dma_burst(u32 width, u32 ybpp, u32 *mburst, u32 *rburst)
+{
+ unsigned int nwords = width * ybpp / 4;
+ unsigned int div, rem;
+
+ if (WARN_ON(width < 8 || (width * ybpp) & 7))
+ return;
+
+ for (div = 16; div >= 2; div /= 2) {
+ if (nwords < div)
+ continue;
+
+ rem = nwords & (div - 1);
+ if (rem == 0) {
+ *mburst = div;
+ *rburst = div;
+ break;
+ }
+ if (rem == div / 2 || rem == div / 4) {
+ *mburst = div;
+ *rburst = rem;
+ break;
+ }
+ }
+}
+
+void camif_hw_set_output_dma(struct camif_vp *vp)
+{
+ struct camif_dev *camif = vp->camif;
+ struct camif_frame *frame = &vp->out_frame;
+ const struct camif_fmt *fmt = vp->out_fmt;
+ unsigned int ymburst = 0, yrburst = 0;
+ u32 cfg;
+
+ camif_hw_set_out_dma_size(vp);
+
+ if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV) {
+ struct camif_dma_offset *offset = &frame->dma_offset;
+ /* Set the input dma offsets. */
+ cfg = S3C_CISS_OFFS_INITIAL(offset->initial);
+ cfg |= S3C_CISS_OFFS_LINE(offset->line);
+ camif_write(camif, S3C_CAMIF_REG_CISSY(vp->id), cfg);
+ camif_write(camif, S3C_CAMIF_REG_CISSCB(vp->id), cfg);
+ camif_write(camif, S3C_CAMIF_REG_CISSCR(vp->id), cfg);
+ }
+
+ /* Configure DMA burst values */
+ camif_get_dma_burst(frame->rect.width, fmt->ybpp, &ymburst, &yrburst);
+
+ cfg = camif_read(camif, S3C_CAMIF_REG_CICTRL(vp->id, vp->offset));
+ cfg &= ~CICTRL_BURST_MASK;
+
+ cfg |= CICTRL_YBURST1(ymburst) | CICTRL_YBURST2(yrburst);
+ cfg |= CICTRL_CBURST1(ymburst / 2) | CICTRL_CBURST2(yrburst / 2);
+
+ camif_write(camif, S3C_CAMIF_REG_CICTRL(vp->id, vp->offset), cfg);
+
+ pr_debug("ymburst: %u, yrburst: %u\n", ymburst, yrburst);
+}
+
+void camif_hw_set_input_path(struct camif_vp *vp)
+{
+ u32 cfg = camif_read(vp->camif, S3C_CAMIF_REG_MSCTRL(vp->id));
+ cfg &= ~MSCTRL_SEL_DMA_CAM;
+ camif_write(vp->camif, S3C_CAMIF_REG_MSCTRL(vp->id), cfg);
+}
+
+void camif_hw_set_target_format(struct camif_vp *vp)
+{
+ struct camif_dev *camif = vp->camif;
+ struct camif_frame *frame = &vp->out_frame;
+ u32 cfg;
+
+ pr_debug("fw: %d, fh: %d color: %d\n", frame->f_width,
+ frame->f_height, vp->out_fmt->color);
+
+ cfg = camif_read(camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset));
+ cfg &= ~CITRGFMT_TARGETSIZE_MASK;
+
+ if (camif->variant->ip_revision == S3C244X_CAMIF_IP_REV) {
+ /* We currently support only YCbCr 4:2:2 at the camera input */
+ cfg |= CITRGFMT_IN422;
+ cfg &= ~CITRGFMT_OUT422;
+ if (vp->out_fmt->color == IMG_FMT_YCBCR422P)
+ cfg |= CITRGFMT_OUT422;
+ } else {
+ cfg &= ~CITRGFMT_OUTFORMAT_MASK;
+ switch (vp->out_fmt->color) {
+ case IMG_FMT_RGB565...IMG_FMT_XRGB8888:
+ cfg |= CITRGFMT_OUTFORMAT_RGB;
+ break;
+ case IMG_FMT_YCBCR420...IMG_FMT_YCRCB420:
+ cfg |= CITRGFMT_OUTFORMAT_YCBCR420;
+ break;
+ case IMG_FMT_YCBCR422P:
+ cfg |= CITRGFMT_OUTFORMAT_YCBCR422;
+ break;
+ case IMG_FMT_YCBYCR422...IMG_FMT_CRYCBY422:
+ cfg |= CITRGFMT_OUTFORMAT_YCBCR422I;
+ break;
+ }
+ }
+
+ /* Rotation is only supported by s3c64xx */
+ if (vp->rotation == 90 || vp->rotation == 270)
+ cfg |= (frame->f_height << 16) | frame->f_width;
+ else
+ cfg |= (frame->f_width << 16) | frame->f_height;
+ camif_write(camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset), cfg);
+
+ /* Target area, output pixel width * height */
+ cfg = camif_read(camif, S3C_CAMIF_REG_CITAREA(vp->id, vp->offset));
+ cfg &= ~CITAREA_MASK;
+ cfg |= (frame->f_width * frame->f_height);
+ camif_write(camif, S3C_CAMIF_REG_CITAREA(vp->id, vp->offset), cfg);
+}
+
+void camif_hw_set_flip(struct camif_vp *vp)
+{
+ u32 cfg = camif_read(vp->camif,
+ S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset));
+
+ cfg &= ~CITRGFMT_FLIP_MASK;
+
+ if (vp->hflip)
+ cfg |= CITRGFMT_FLIP_Y_MIRROR;
+ if (vp->vflip)
+ cfg |= CITRGFMT_FLIP_X_MIRROR;
+
+ camif_write(vp->camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset), cfg);
+}
+
+static void camif_hw_set_prescaler(struct camif_vp *vp)
+{
+ struct camif_dev *camif = vp->camif;
+ struct camif_scaler *sc = &vp->scaler;
+ u32 cfg, shfactor, addr;
+
+ addr = S3C_CAMIF_REG_CISCPRERATIO(vp->id, vp->offset);
+
+ shfactor = 10 - (sc->h_shift + sc->v_shift);
+ cfg = shfactor << 28;
+
+ cfg |= (sc->pre_h_ratio << 16) | sc->pre_v_ratio;
+ camif_write(camif, addr, cfg);
+
+ cfg = (sc->pre_dst_width << 16) | sc->pre_dst_height;
+ camif_write(camif, S3C_CAMIF_REG_CISCPREDST(vp->id, vp->offset), cfg);
+}
+
+void camif_s3c244x_hw_set_scaler(struct camif_vp *vp)
+{
+ struct camif_dev *camif = vp->camif;
+ struct camif_scaler *scaler = &vp->scaler;
+ unsigned int color = vp->out_fmt->color;
+ u32 cfg;
+
+ camif_hw_set_prescaler(vp);
+
+ cfg = camif_read(camif, S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset));
+
+ cfg &= ~(CISCCTRL_SCALEUP_MASK | CISCCTRL_SCALERBYPASS |
+ CISCCTRL_MAIN_RATIO_MASK | CIPRSCCTRL_RGB_FORMAT_24BIT);
+
+ if (scaler->enable) {
+ if (scaler->scaleup_h) {
+ if (vp->id == VP_CODEC)
+ cfg |= CISCCTRL_SCALEUP_H;
+ else
+ cfg |= CIPRSCCTRL_SCALEUP_H;
+ }
+ if (scaler->scaleup_v) {
+ if (vp->id == VP_CODEC)
+ cfg |= CISCCTRL_SCALEUP_V;
+ else
+ cfg |= CIPRSCCTRL_SCALEUP_V;
+ }
+ } else {
+ if (vp->id == VP_CODEC)
+ cfg |= CISCCTRL_SCALERBYPASS;
+ }
+
+ cfg |= ((scaler->main_h_ratio & 0x1ff) << 16);
+ cfg |= scaler->main_v_ratio & 0x1ff;
+
+ if (vp->id == VP_PREVIEW) {
+ if (color == IMG_FMT_XRGB8888)
+ cfg |= CIPRSCCTRL_RGB_FORMAT_24BIT;
+ cfg |= CIPRSCCTRL_SAMPLE;
+ }
+
+ camif_write(camif, S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset), cfg);
+
+ pr_debug("main: h_ratio: %#x, v_ratio: %#x",
+ scaler->main_h_ratio, scaler->main_v_ratio);
+}
+
+void camif_s3c64xx_hw_set_scaler(struct camif_vp *vp)
+{
+ struct camif_dev *camif = vp->camif;
+ struct camif_scaler *scaler = &vp->scaler;
+ unsigned int color = vp->out_fmt->color;
+ u32 cfg;
+
+ camif_hw_set_prescaler(vp);
+
+ cfg = camif_read(camif, S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset));
+
+ cfg &= ~(CISCCTRL_CSCR2Y_WIDE | CISCCTRL_CSCY2R_WIDE
+ | CISCCTRL_SCALEUP_H | CISCCTRL_SCALEUP_V
+ | CISCCTRL_SCALERBYPASS | CISCCTRL_ONE2ONE
+ | CISCCTRL_INRGB_FMT_MASK | CISCCTRL_OUTRGB_FMT_MASK
+ | CISCCTRL_INTERLACE | CISCCTRL_EXTRGB_EXTENSION
+ | CISCCTRL_MAIN_RATIO_MASK);
+
+ cfg |= (CISCCTRL_CSCR2Y_WIDE | CISCCTRL_CSCY2R_WIDE);
+
+ if (!scaler->enable) {
+ cfg |= CISCCTRL_SCALERBYPASS;
+ } else {
+ if (scaler->scaleup_h)
+ cfg |= CISCCTRL_SCALEUP_H;
+ if (scaler->scaleup_v)
+ cfg |= CISCCTRL_SCALEUP_V;
+ if (scaler->copy)
+ cfg |= CISCCTRL_ONE2ONE;
+ }
+
+ switch (color) {
+ case IMG_FMT_RGB666:
+ cfg |= CISCCTRL_OUTRGB_FMT_RGB666;
+ break;
+ case IMG_FMT_XRGB8888:
+ cfg |= CISCCTRL_OUTRGB_FMT_RGB888;
+ break;
+ }
+
+ cfg |= (scaler->main_h_ratio & 0x1ff) << 16;
+ cfg |= scaler->main_v_ratio & 0x1ff;
+
+ camif_write(camif, S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset), cfg);
+
+ pr_debug("main: h_ratio: %#x, v_ratio: %#x",
+ scaler->main_h_ratio, scaler->main_v_ratio);
+}
+
+void camif_hw_set_scaler(struct camif_vp *vp)
+{
+ unsigned int ip_rev = vp->camif->variant->ip_revision;
+
+ if (ip_rev == S3C244X_CAMIF_IP_REV)
+ camif_s3c244x_hw_set_scaler(vp);
+ else
+ camif_s3c64xx_hw_set_scaler(vp);
+}
+
+void camif_hw_enable_scaler(struct camif_vp *vp, bool on)
+{
+ u32 addr = S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset);
+ u32 cfg;
+
+ cfg = camif_read(vp->camif, addr);
+ if (on)
+ cfg |= CISCCTRL_SCALERSTART;
+ else
+ cfg &= ~CISCCTRL_SCALERSTART;
+ camif_write(vp->camif, addr, cfg);
+}
+
+void camif_hw_set_lastirq(struct camif_vp *vp, int enable)
+{
+ u32 addr = S3C_CAMIF_REG_CICTRL(vp->id, vp->offset);
+ u32 cfg;
+
+ cfg = camif_read(vp->camif, addr);
+ if (enable)
+ cfg |= CICTRL_LASTIRQ_ENABLE;
+ else
+ cfg &= ~CICTRL_LASTIRQ_ENABLE;
+ camif_write(vp->camif, addr, cfg);
+}
+
+void camif_hw_enable_capture(struct camif_vp *vp)
+{
+ struct camif_dev *camif = vp->camif;
+ u32 cfg;
+
+ cfg = camif_read(camif, S3C_CAMIF_REG_CIIMGCPT(vp->offset));
+ camif->stream_count++;
+
+ if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV)
+ cfg |= CIIMGCPT_CPT_FREN_ENABLE(vp->id);
+
+ if (vp->scaler.enable)
+ cfg |= CIIMGCPT_IMGCPTEN_SC(vp->id);
+
+ if (camif->stream_count == 1)
+ cfg |= CIIMGCPT_IMGCPTEN;
+
+ camif_write(camif, S3C_CAMIF_REG_CIIMGCPT(vp->offset), cfg);
+
+ pr_debug("CIIMGCPT: %#x, camif->stream_count: %d\n",
+ cfg, camif->stream_count);
+}
+
+void camif_hw_disable_capture(struct camif_vp *vp)
+{
+ struct camif_dev *camif = vp->camif;
+ u32 cfg;
+
+ cfg = camif_read(camif, S3C_CAMIF_REG_CIIMGCPT(vp->offset));
+ cfg &= ~CIIMGCPT_IMGCPTEN_SC(vp->id);
+
+ if (WARN_ON(--(camif->stream_count) < 0))
+ camif->stream_count = 0;
+
+ if (camif->stream_count == 0)
+ cfg &= ~CIIMGCPT_IMGCPTEN;
+
+ pr_debug("CIIMGCPT: %#x, camif->stream_count: %d\n",
+ cfg, camif->stream_count);
+
+ camif_write(camif, S3C_CAMIF_REG_CIIMGCPT(vp->offset), cfg);
+}
+
+void camif_hw_dump_regs(struct camif_dev *camif, const char *label)
+{
+ struct {
+ u32 offset;
+ const char * const name;
+ } registers[] = {
+ { S3C_CAMIF_REG_CISRCFMT, "CISRCFMT" },
+ { S3C_CAMIF_REG_CIWDOFST, "CIWDOFST" },
+ { S3C_CAMIF_REG_CIGCTRL, "CIGCTRL" },
+ { S3C_CAMIF_REG_CIWDOFST2, "CIWDOFST2" },
+ { S3C_CAMIF_REG_CIYSA(0, 0), "CICOYSA0" },
+ { S3C_CAMIF_REG_CICBSA(0, 0), "CICOCBSA0" },
+ { S3C_CAMIF_REG_CICRSA(0, 0), "CICOCRSA0" },
+ { S3C_CAMIF_REG_CIYSA(0, 1), "CICOYSA1" },
+ { S3C_CAMIF_REG_CICBSA(0, 1), "CICOCBSA1" },
+ { S3C_CAMIF_REG_CICRSA(0, 1), "CICOCRSA1" },
+ { S3C_CAMIF_REG_CIYSA(0, 2), "CICOYSA2" },
+ { S3C_CAMIF_REG_CICBSA(0, 2), "CICOCBSA2" },
+ { S3C_CAMIF_REG_CICRSA(0, 2), "CICOCRSA2" },
+ { S3C_CAMIF_REG_CIYSA(0, 3), "CICOYSA3" },
+ { S3C_CAMIF_REG_CICBSA(0, 3), "CICOCBSA3" },
+ { S3C_CAMIF_REG_CICRSA(0, 3), "CICOCRSA3" },
+ { S3C_CAMIF_REG_CIYSA(1, 0), "CIPRYSA0" },
+ { S3C_CAMIF_REG_CIYSA(1, 1), "CIPRYSA1" },
+ { S3C_CAMIF_REG_CIYSA(1, 2), "CIPRYSA2" },
+ { S3C_CAMIF_REG_CIYSA(1, 3), "CIPRYSA3" },
+ { S3C_CAMIF_REG_CITRGFMT(0, 0), "CICOTRGFMT" },
+ { S3C_CAMIF_REG_CITRGFMT(1, 0), "CIPRTRGFMT" },
+ { S3C_CAMIF_REG_CICTRL(0, 0), "CICOCTRL" },
+ { S3C_CAMIF_REG_CICTRL(1, 0), "CIPRCTRL" },
+ { S3C_CAMIF_REG_CISCPREDST(0, 0), "CICOSCPREDST" },
+ { S3C_CAMIF_REG_CISCPREDST(1, 0), "CIPRSCPREDST" },
+ { S3C_CAMIF_REG_CISCPRERATIO(0, 0), "CICOSCPRERATIO" },
+ { S3C_CAMIF_REG_CISCPRERATIO(1, 0), "CIPRSCPRERATIO" },
+ { S3C_CAMIF_REG_CISCCTRL(0, 0), "CICOSCCTRL" },
+ { S3C_CAMIF_REG_CISCCTRL(1, 0), "CIPRSCCTRL" },
+ { S3C_CAMIF_REG_CITAREA(0, 0), "CICOTAREA" },
+ { S3C_CAMIF_REG_CITAREA(1, 0), "CIPRTAREA" },
+ { S3C_CAMIF_REG_CISTATUS(0, 0), "CICOSTATUS" },
+ { S3C_CAMIF_REG_CISTATUS(1, 0), "CIPRSTATUS" },
+ { S3C_CAMIF_REG_CIIMGCPT(0), "CIIMGCPT" },
+ };
+ u32 i;
+
+ pr_info("--- %s ---\n", label);
+ for (i = 0; i < ARRAY_SIZE(registers); i++) {
+ u32 cfg = readl(camif->io_base + registers[i].offset);
+ printk(KERN_INFO "%s:\t0x%08x\n", registers[i].name, cfg);
+ }
+}
diff --git a/drivers/media/platform/s3c-camif/camif-regs.h b/drivers/media/platform/s3c-camif/camif-regs.h
new file mode 100644
index 00000000000..af2d472ea1d
--- /dev/null
+++ b/drivers/media/platform/s3c-camif/camif-regs.h
@@ -0,0 +1,269 @@
+/*
+ * Register definition file for s3c24xx/s3c64xx SoC CAMIF driver
+ *
+ * Copyright (C) 2012 Sylwester Nawrocki <sylvester.nawrocki@gmail.com>
+ * Copyright (C) 2012 Tomasz Figa <tomasz.figa@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef CAMIF_REGS_H_
+#define CAMIF_REGS_H_
+
+#include "camif-core.h"
+#include <media/s3c_camif.h>
+
+/*
+ * The id argument indicates the processing path:
+ * id = 0 - codec (FIMC C), 1 - preview (FIMC P).
+ */
+
+/* Camera input format */
+#define S3C_CAMIF_REG_CISRCFMT 0x00
+#define CISRCFMT_ITU601_8BIT (1 << 31)
+#define CISRCFMT_ITU656_8BIT (0 << 31)
+#define CISRCFMT_ORDER422_YCBYCR (0 << 14)
+#define CISRCFMT_ORDER422_YCRYCB (1 << 14)
+#define CISRCFMT_ORDER422_CBYCRY (2 << 14)
+#define CISRCFMT_ORDER422_CRYCBY (3 << 14)
+#define CISRCFMT_ORDER422_MASK (3 << 14)
+#define CISRCFMT_SIZE_CAM_MASK (0x1fff << 16 | 0x1fff)
+
+/* Window offset */
+#define S3C_CAMIF_REG_CIWDOFST 0x04
+#define CIWDOFST_WINOFSEN (1 << 31)
+#define CIWDOFST_CLROVCOFIY (1 << 30)
+#define CIWDOFST_CLROVRLB_PR (1 << 28)
+/* #define CIWDOFST_CLROVPRFIY (1 << 27) */
+#define CIWDOFST_CLROVCOFICB (1 << 15)
+#define CIWDOFST_CLROVCOFICR (1 << 14)
+#define CIWDOFST_CLROVPRFICB (1 << 13)
+#define CIWDOFST_CLROVPRFICR (1 << 12)
+#define CIWDOFST_OFST_MASK (0x7ff << 16 | 0x7ff)
+
+/* Window offset 2 */
+#define S3C_CAMIF_REG_CIWDOFST2 0x14
+#define CIWDOFST2_OFST2_MASK (0xfff << 16 | 0xfff)
+
+/* Global control */
+#define S3C_CAMIF_REG_CIGCTRL 0x08
+#define CIGCTRL_SWRST (1 << 31)
+#define CIGCTRL_CAMRST (1 << 30)
+#define CIGCTRL_TESTPATTERN_NORMAL (0 << 27)
+#define CIGCTRL_TESTPATTERN_COLOR_BAR (1 << 27)
+#define CIGCTRL_TESTPATTERN_HOR_INC (2 << 27)
+#define CIGCTRL_TESTPATTERN_VER_INC (3 << 27)
+#define CIGCTRL_TESTPATTERN_MASK (3 << 27)
+#define CIGCTRL_INVPOLPCLK (1 << 26)
+#define CIGCTRL_INVPOLVSYNC (1 << 25)
+#define CIGCTRL_INVPOLHREF (1 << 24)
+#define CIGCTRL_IRQ_OVFEN (1 << 22)
+#define CIGCTRL_HREF_MASK (1 << 21)
+#define CIGCTRL_IRQ_LEVEL (1 << 20)
+/* IRQ_CLR_C, IRQ_CLR_P */
+#define CIGCTRL_IRQ_CLR(id) (1 << (19 - (id)))
+#define CIGCTRL_FIELDMODE (1 << 2)
+#define CIGCTRL_INVPOLFIELD (1 << 1)
+#define CIGCTRL_CAM_INTERLACE (1 << 0)
+
+/* Y DMA output frame start address. n = 0..3. */
+#define S3C_CAMIF_REG_CIYSA(id, n) (0x18 + (id) * 0x54 + (n) * 4)
+/* Cb plane output DMA start address. n = 0..3. Only codec path. */
+#define S3C_CAMIF_REG_CICBSA(id, n) (0x28 + (id) * 0x54 + (n) * 4)
+/* Cr plane output DMA start address. n = 0..3. Only codec path. */
+#define S3C_CAMIF_REG_CICRSA(id, n) (0x38 + (id) * 0x54 + (n) * 4)
+
+/* CICOTRGFMT, CIPRTRGFMT - Target format */
+#define S3C_CAMIF_REG_CITRGFMT(id, _offs) (0x48 + (id) * (0x34 + (_offs)))
+#define CITRGFMT_IN422 (1 << 31) /* only for s3c24xx */
+#define CITRGFMT_OUT422 (1 << 30) /* only for s3c24xx */
+#define CITRGFMT_OUTFORMAT_YCBCR420 (0 << 29) /* only for s3c6410 */
+#define CITRGFMT_OUTFORMAT_YCBCR422 (1 << 29) /* only for s3c6410 */
+#define CITRGFMT_OUTFORMAT_YCBCR422I (2 << 29) /* only for s3c6410 */
+#define CITRGFMT_OUTFORMAT_RGB (3 << 29) /* only for s3c6410 */
+#define CITRGFMT_OUTFORMAT_MASK (3 << 29) /* only for s3c6410 */
+#define CITRGFMT_TARGETHSIZE(x) ((x) << 16)
+#define CITRGFMT_FLIP_NORMAL (0 << 14)
+#define CITRGFMT_FLIP_X_MIRROR (1 << 14)
+#define CITRGFMT_FLIP_Y_MIRROR (2 << 14)
+#define CITRGFMT_FLIP_180 (3 << 14)
+#define CITRGFMT_FLIP_MASK (3 << 14)
+/* Preview path only */
+#define CITRGFMT_ROT90_PR (1 << 13)
+#define CITRGFMT_TARGETVSIZE(x) ((x) << 0)
+#define CITRGFMT_TARGETSIZE_MASK ((0x1fff << 16) | 0x1fff)
+
+/* CICOCTRL, CIPRCTRL. Output DMA control. */
+#define S3C_CAMIF_REG_CICTRL(id, _offs) (0x4c + (id) * (0x34 + (_offs)))
+#define CICTRL_BURST_MASK (0xfffff << 4)
+/* xBURSTn - 5-bits width */
+#define CICTRL_YBURST1(x) ((x) << 19)
+#define CICTRL_YBURST2(x) ((x) << 14)
+#define CICTRL_RGBBURST1(x) ((x) << 19)
+#define CICTRL_RGBBURST2(x) ((x) << 14)
+#define CICTRL_CBURST1(x) ((x) << 9)
+#define CICTRL_CBURST2(x) ((x) << 4)
+#define CICTRL_LASTIRQ_ENABLE (1 << 2)
+#define CICTRL_ORDER422_MASK (3 << 0)
+
+/* CICOSCPRERATIO, CIPRSCPRERATIO. Pre-scaler control 1. */
+#define S3C_CAMIF_REG_CISCPRERATIO(id, _offs) (0x50 + (id) * (0x34 + (_offs)))
+
+/* CICOSCPREDST, CIPRSCPREDST. Pre-scaler control 2. */
+#define S3C_CAMIF_REG_CISCPREDST(id, _offs) (0x54 + (id) * (0x34 + (_offs)))
+
+/* CICOSCCTRL, CIPRSCCTRL. Main scaler control. */
+#define S3C_CAMIF_REG_CISCCTRL(id, _offs) (0x58 + (id) * (0x34 + (_offs)))
+#define CISCCTRL_SCALERBYPASS (1 << 31)
+/* s3c244x preview path only, s3c64xx both */
+#define CIPRSCCTRL_SAMPLE (1 << 31)
+/* 0 - 16-bit RGB, 1 - 24-bit RGB */
+#define CIPRSCCTRL_RGB_FORMAT_24BIT (1 << 30) /* only for s3c244x */
+#define CIPRSCCTRL_SCALEUP_H (1 << 29) /* only for s3c244x */
+#define CIPRSCCTRL_SCALEUP_V (1 << 28) /* only for s3c244x */
+/* s3c64xx */
+#define CISCCTRL_SCALEUP_H (1 << 30)
+#define CISCCTRL_SCALEUP_V (1 << 29)
+#define CISCCTRL_SCALEUP_MASK (0x3 << 29)
+#define CISCCTRL_CSCR2Y_WIDE (1 << 28)
+#define CISCCTRL_CSCY2R_WIDE (1 << 27)
+#define CISCCTRL_LCDPATHEN_FIFO (1 << 26)
+#define CISCCTRL_INTERLACE (1 << 25)
+#define CISCCTRL_SCALERSTART (1 << 15)
+#define CISCCTRL_INRGB_FMT_RGB565 (0 << 13)
+#define CISCCTRL_INRGB_FMT_RGB666 (1 << 13)
+#define CISCCTRL_INRGB_FMT_RGB888 (2 << 13)
+#define CISCCTRL_INRGB_FMT_MASK (3 << 13)
+#define CISCCTRL_OUTRGB_FMT_RGB565 (0 << 11)
+#define CISCCTRL_OUTRGB_FMT_RGB666 (1 << 11)
+#define CISCCTRL_OUTRGB_FMT_RGB888 (2 << 11)
+#define CISCCTRL_OUTRGB_FMT_MASK (3 << 11)
+#define CISCCTRL_EXTRGB_EXTENSION (1 << 10)
+#define CISCCTRL_ONE2ONE (1 << 9)
+#define CISCCTRL_MAIN_RATIO_MASK (0x1ff << 16 | 0x1ff)
+
+/* CICOTAREA, CIPRTAREA. Target area for DMA (Hsize x Vsize). */
+#define S3C_CAMIF_REG_CITAREA(id, _offs) (0x5c + (id) * (0x34 + (_offs)))
+#define CITAREA_MASK 0xfffffff
+
+/* Codec (id = 0) or preview (id = 1) path status. */
+#define S3C_CAMIF_REG_CISTATUS(id, _offs) (0x64 + (id) * (0x34 + (_offs)))
+#define CISTATUS_OVFIY_STATUS (1 << 31)
+#define CISTATUS_OVFICB_STATUS (1 << 30)
+#define CISTATUS_OVFICR_STATUS (1 << 29)
+#define CISTATUS_OVF_MASK (0x7 << 29)
+#define CIPRSTATUS_OVF_MASK (0x3 << 30)
+#define CISTATUS_VSYNC_STATUS (1 << 28)
+#define CISTATUS_FRAMECNT_MASK (3 << 26)
+#define CISTATUS_FRAMECNT(__reg) (((__reg) >> 26) & 0x3)
+#define CISTATUS_WINOFSTEN_STATUS (1 << 25)
+#define CISTATUS_IMGCPTEN_STATUS (1 << 22)
+#define CISTATUS_IMGCPTENSC_STATUS (1 << 21)
+#define CISTATUS_VSYNC_A_STATUS (1 << 20)
+#define CISTATUS_FRAMEEND_STATUS (1 << 19) /* 17 on s3c64xx */
+
+/* Image capture enable */
+#define S3C_CAMIF_REG_CIIMGCPT(_offs) (0xa0 + (_offs))
+#define CIIMGCPT_IMGCPTEN (1 << 31)
+#define CIIMGCPT_IMGCPTEN_SC(id) (1 << (30 - (id)))
+/* Frame control: 1 - one-shot, 0 - free run */
+#define CIIMGCPT_CPT_FREN_ENABLE(id) (1 << (25 - (id)))
+#define CIIMGCPT_CPT_FRMOD_ENABLE (0 << 18)
+#define CIIMGCPT_CPT_FRMOD_CNT (1 << 18)
+
+/* Capture sequence */
+#define S3C_CAMIF_REG_CICPTSEQ 0xc4
+
+/* Image effects */
+#define S3C_CAMIF_REG_CIIMGEFF(_offs) (0xb0 + (_offs))
+#define CIIMGEFF_IE_ENABLE(id) (1 << (30 + (id)))
+#define CIIMGEFF_IE_ENABLE_MASK (3 << 30)
+/* Image effect: 1 - after scaler, 0 - before scaler */
+#define CIIMGEFF_IE_AFTER_SC (1 << 29)
+#define CIIMGEFF_FIN_MASK (7 << 26)
+#define CIIMGEFF_FIN_BYPASS (0 << 26)
+#define CIIMGEFF_FIN_ARBITRARY (1 << 26)
+#define CIIMGEFF_FIN_NEGATIVE (2 << 26)
+#define CIIMGEFF_FIN_ARTFREEZE (3 << 26)
+#define CIIMGEFF_FIN_EMBOSSING (4 << 26)
+#define CIIMGEFF_FIN_SILHOUETTE (5 << 26)
+#define CIIMGEFF_PAT_CBCR_MASK ((0xff << 13) | 0xff)
+#define CIIMGEFF_PAT_CB(x) ((x) << 13)
+#define CIIMGEFF_PAT_CR(x) (x)
+
+/* MSCOY0SA, MSPRY0SA. Y/Cb/Cr frame start address for input DMA. */
+#define S3C_CAMIF_REG_MSY0SA(id) (0xd4 + ((id) * 0x2c))
+#define S3C_CAMIF_REG_MSCB0SA(id) (0xd8 + ((id) * 0x2c))
+#define S3C_CAMIF_REG_MSCR0SA(id) (0xdc + ((id) * 0x2c))
+
+/* MSCOY0END, MSCOY0END. Y/Cb/Cr frame end address for input DMA. */
+#define S3C_CAMIF_REG_MSY0END(id) (0xe0 + ((id) * 0x2c))
+#define S3C_CAMIF_REG_MSCB0END(id) (0xe4 + ((id) * 0x2c))
+#define S3C_CAMIF_REG_MSCR0END(id) (0xe8 + ((id) * 0x2c))
+
+/* MSPRYOFF, MSPRYOFF. Y/Cb/Cr offset. n: 0 - codec, 1 - preview. */
+#define S3C_CAMIF_REG_MSYOFF(id) (0x118 + ((id) * 0x2c))
+#define S3C_CAMIF_REG_MSCBOFF(id) (0x11c + ((id) * 0x2c))
+#define S3C_CAMIF_REG_MSCROFF(id) (0x120 + ((id) * 0x2c))
+
+/* Real input DMA data size. n = 0 - codec, 1 - preview. */
+#define S3C_CAMIF_REG_MSWIDTH(id) (0xf8 + (id) * 0x2c)
+#define AUTOLOAD_ENABLE (1 << 31)
+#define ADDR_CH_DIS (1 << 30)
+#define MSHEIGHT(x) (((x) & 0x3ff) << 16)
+#define MSWIDTH(x) ((x) & 0x3ff)
+
+/* Input DMA control. n = 0 - codec, 1 - preview */
+#define S3C_CAMIF_REG_MSCTRL(id) (0xfc + (id) * 0x2c)
+#define MSCTRL_ORDER422_M_YCBYCR (0 << 4)
+#define MSCTRL_ORDER422_M_YCRYCB (1 << 4)
+#define MSCTRL_ORDER422_M_CBYCRY (2 << 4)
+#define MSCTRL_ORDER422_M_CRYCBY (3 << 4)
+/* 0 - camera, 1 - DMA */
+#define MSCTRL_SEL_DMA_CAM (1 << 3)
+#define MSCTRL_INFORMAT_M_YCBCR420 (0 << 1)
+#define MSCTRL_INFORMAT_M_YCBCR422 (1 << 1)
+#define MSCTRL_INFORMAT_M_YCBCR422I (2 << 1)
+#define MSCTRL_INFORMAT_M_RGB (3 << 1)
+#define MSCTRL_ENVID_M (1 << 0)
+
+/* CICOSCOSY, CIPRSCOSY. Scan line Y/Cb/Cr offset. */
+#define S3C_CAMIF_REG_CISSY(id) (0x12c + (id) * 0x0c)
+#define S3C_CAMIF_REG_CISSCB(id) (0x130 + (id) * 0x0c)
+#define S3C_CAMIF_REG_CISSCR(id) (0x134 + (id) * 0x0c)
+#define S3C_CISS_OFFS_INITIAL(x) ((x) << 16)
+#define S3C_CISS_OFFS_LINE(x) ((x) << 0)
+
+/* ------------------------------------------------------------------ */
+
+void camif_hw_reset(struct camif_dev *camif);
+void camif_hw_clear_pending_irq(struct camif_vp *vp);
+void camif_hw_clear_fifo_overflow(struct camif_vp *vp);
+void camif_hw_set_lastirq(struct camif_vp *vp, int enable);
+void camif_hw_set_input_path(struct camif_vp *vp);
+void camif_hw_enable_scaler(struct camif_vp *vp, bool on);
+void camif_hw_enable_capture(struct camif_vp *vp);
+void camif_hw_disable_capture(struct camif_vp *vp);
+void camif_hw_set_camera_bus(struct camif_dev *camif);
+void camif_hw_set_source_format(struct camif_dev *camif);
+void camif_hw_set_camera_crop(struct camif_dev *camif);
+void camif_hw_set_scaler(struct camif_vp *vp);
+void camif_hw_set_flip(struct camif_vp *vp);
+void camif_hw_set_output_dma(struct camif_vp *vp);
+void camif_hw_set_target_format(struct camif_vp *vp);
+void camif_hw_set_test_pattern(struct camif_dev *camif, unsigned int pattern);
+void camif_hw_set_effect(struct camif_dev *camif, unsigned int effect,
+ unsigned int cr, unsigned int cb);
+void camif_hw_set_output_addr(struct camif_vp *vp, struct camif_addr *paddr,
+ int index);
+void camif_hw_dump_regs(struct camif_dev *camif, const char *label);
+
+static inline u32 camif_hw_get_status(struct camif_vp *vp)
+{
+ return readl(vp->camif->io_base + S3C_CAMIF_REG_CISTATUS(vp->id,
+ vp->offset));
+}
+
+#endif /* CAMIF_REGS_H_ */
diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c
index 891ee873c62..fdb6740248a 100644
--- a/drivers/media/platform/s5p-fimc/fimc-capture.c
+++ b/drivers/media/platform/s5p-fimc/fimc-capture.c
@@ -1230,6 +1230,14 @@ static int fimc_cap_qbuf(struct file *file, void *priv,
return vb2_qbuf(&fimc->vid_cap.vbq, buf);
}
+static int fimc_cap_expbuf(struct file *file, void *priv,
+ struct v4l2_exportbuffer *eb)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ return vb2_expbuf(&fimc->vid_cap.vbq, eb);
+}
+
static int fimc_cap_dqbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
@@ -1354,6 +1362,7 @@ static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {
.vidioc_qbuf = fimc_cap_qbuf,
.vidioc_dqbuf = fimc_cap_dqbuf,
+ .vidioc_expbuf = fimc_cap_expbuf,
.vidioc_prepare_buf = fimc_cap_prepare_buf,
.vidioc_create_bufs = fimc_cap_create_bufs,
@@ -1729,7 +1738,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc,
q = &fimc->vid_cap.vbq;
memset(q, 0, sizeof(*q));
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
- q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
q->drv_priv = fimc->vid_cap.ctx;
q->ops = &fimc_capture_qops;
q->mem_ops = &vb2_dma_contig_memops;
diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/s5p-fimc/fimc-m2m.c
index 62afed3162e..1d21da4bd24 100644
--- a/drivers/media/platform/s5p-fimc/fimc-m2m.c
+++ b/drivers/media/platform/s5p-fimc/fimc-m2m.c
@@ -105,7 +105,7 @@ static void fimc_device_run(void *priv)
struct fimc_frame *sf, *df;
struct fimc_dev *fimc;
unsigned long flags;
- u32 ret;
+ int ret;
if (WARN(!ctx, "Null context\n"))
return;
@@ -439,6 +439,15 @@ static int fimc_m2m_dqbuf(struct file *file, void *fh,
return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
}
+static int fimc_m2m_expbuf(struct file *file, void *fh,
+ struct v4l2_exportbuffer *eb)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb);
+}
+
+
static int fimc_m2m_streamon(struct file *file, void *fh,
enum v4l2_buf_type type)
{
@@ -607,6 +616,7 @@ static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
.vidioc_querybuf = fimc_m2m_querybuf,
.vidioc_qbuf = fimc_m2m_qbuf,
.vidioc_dqbuf = fimc_m2m_dqbuf,
+ .vidioc_expbuf = fimc_m2m_expbuf,
.vidioc_streamon = fimc_m2m_streamon,
.vidioc_streamoff = fimc_m2m_streamoff,
.vidioc_g_crop = fimc_m2m_g_crop,
@@ -622,7 +632,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
int ret;
src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
- src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
src_vq->drv_priv = ctx;
src_vq->ops = &fimc_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
@@ -633,7 +643,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
return ret;
dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
- dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
dst_vq->drv_priv = ctx;
dst_vq->ops = &fimc_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c
index 0531ab70a94..1bd5678cfeb 100644
--- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c
+++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c
@@ -213,7 +213,7 @@ static int fimc_pipeline_close(struct fimc_pipeline *p)
* @pipeline: video pipeline structure
* @on: passed as the s_stream call argument
*/
-int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on)
+static int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on)
{
int i, ret;
@@ -547,7 +547,7 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd,
if (ret)
break;
- v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]",
+ v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]\n",
source->name, flags ? '=' : '-', sink->name);
if (flags == 0 || sensor == NULL)
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
index eb6a70b0f82..6dad9a74f61 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
@@ -636,6 +636,19 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
return -EINVAL;
}
+/* Export DMA buffer */
+static int vidioc_expbuf(struct file *file, void *priv,
+ struct v4l2_exportbuffer *eb)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (eb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_expbuf(&ctx->vq_src, eb);
+ if (eb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_expbuf(&ctx->vq_dst, eb);
+ return -EINVAL;
+}
+
/* Stream on */
static int vidioc_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
@@ -813,6 +826,7 @@ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = {
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_expbuf = vidioc_expbuf,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_g_crop = vidioc_g_crop,
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
index 2af6d522f4a..f92f6ddd739 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
@@ -1165,6 +1165,19 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
return ret;
}
+/* Export DMA buffer */
+static int vidioc_expbuf(struct file *file, void *priv,
+ struct v4l2_exportbuffer *eb)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (eb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_expbuf(&ctx->vq_src, eb);
+ if (eb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_expbuf(&ctx->vq_dst, eb);
+ return -EINVAL;
+}
+
/* Stream on */
static int vidioc_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
@@ -1542,7 +1555,7 @@ int vidioc_encoder_cmd(struct file *file, void *priv,
}
static int vidioc_subscribe_event(struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub)
+ const struct v4l2_event_subscription *sub)
{
switch (sub->type) {
case V4L2_EVENT_EOS:
@@ -1568,6 +1581,7 @@ static const struct v4l2_ioctl_ops s5p_mfc_enc_ioctl_ops = {
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_expbuf = vidioc_expbuf,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_s_parm = vidioc_s_parm,
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
index 367db755228..2895333866f 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
@@ -28,7 +28,7 @@ static struct s5p_mfc_pm *pm;
static struct s5p_mfc_dev *p_dev;
#ifdef CLK_DEBUG
-atomic_t clk_ref;
+static atomic_t clk_ref;
#endif
int s5p_mfc_init_pm(struct s5p_mfc_dev *dev)
diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c
index 0c1cd895ff6..7379e77bf4e 100644
--- a/drivers/media/platform/s5p-tv/mixer_video.c
+++ b/drivers/media/platform/s5p-tv/mixer_video.c
@@ -19,7 +19,6 @@
#include <linux/videodev2.h>
#include <linux/mm.h>
#include <linux/module.h>
-#include <linux/version.h>
#include <linux/timer.h>
#include <media/videobuf2-dma-contig.h>
@@ -698,6 +697,15 @@ static int mxr_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
return vb2_dqbuf(&layer->vb_queue, p, file->f_flags & O_NONBLOCK);
}
+static int mxr_expbuf(struct file *file, void *priv,
+ struct v4l2_exportbuffer *eb)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ return vb2_expbuf(&layer->vb_queue, eb);
+}
+
static int mxr_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct mxr_layer *layer = video_drvdata(file);
@@ -725,6 +733,7 @@ static const struct v4l2_ioctl_ops mxr_ioctl_ops = {
.vidioc_querybuf = mxr_querybuf,
.vidioc_qbuf = mxr_qbuf,
.vidioc_dqbuf = mxr_dqbuf,
+ .vidioc_expbuf = mxr_expbuf,
/* Streaming control */
.vidioc_streamon = mxr_streamon,
.vidioc_streamoff = mxr_streamoff,
@@ -1093,7 +1102,7 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
layer->vb_queue = (struct vb2_queue) {
.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
- .io_modes = VB2_MMAP | VB2_USERPTR,
+ .io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF,
.drv_priv = layer,
.buf_struct_size = sizeof(struct mxr_buffer),
.ops = &mxr_video_qops,
diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig
index 9afe1e7bde7..cb6791e62bd 100644
--- a/drivers/media/platform/soc_camera/Kconfig
+++ b/drivers/media/platform/soc_camera/Kconfig
@@ -19,6 +19,7 @@ config MX1_VIDEO
config VIDEO_MX1
tristate "i.MX1/i.MXL CMOS Sensor Interface driver"
+ depends on BROKEN
depends on VIDEO_DEV && ARCH_MX1 && SOC_CAMERA
select FIQ
select VIDEOBUF_DMA_CONTIG
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index d3f0b84e2d7..4e3735679f1 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -645,11 +645,17 @@ static ssize_t soc_camera_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
struct soc_camera_device *icd = file->private_data;
- int err = -EINVAL;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ dev_dbg(icd->pdev, "read called, buf %p\n", buf);
+
+ if (ici->ops->init_videobuf2 && icd->vb2_vidq.io_modes & VB2_READ)
+ return vb2_read(&icd->vb2_vidq, buf, count, ppos,
+ file->f_flags & O_NONBLOCK);
dev_err(icd->pdev, "camera device read not implemented\n");
- return err;
+ return -EINVAL;
}
static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma)
@@ -1048,10 +1054,8 @@ static void scan_add_host(struct soc_camera_host *ici)
list_for_each_entry(icd, &devices, list) {
if (icd->iface == ici->nr) {
- int ret;
-
icd->parent = ici->v4l2_dev.dev;
- ret = soc_camera_probe(icd);
+ soc_camera_probe(icd);
}
}
diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c
index b366b050a3d..0d59b9db83c 100644
--- a/drivers/media/platform/vivi.c
+++ b/drivers/media/platform/vivi.c
@@ -39,7 +39,6 @@
/* Wake up at about 30 fps */
#define WAKE_NUMERATOR 30
#define WAKE_DENOMINATOR 1001
-#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
#define MAX_WIDTH 1920
#define MAX_HEIGHT 1200
@@ -352,11 +351,6 @@ static void precalculate_bars(struct vivi_dev *dev)
}
}
-#define TSTAMP_MIN_Y 24
-#define TSTAMP_MAX_Y (TSTAMP_MIN_Y + 15)
-#define TSTAMP_INPUT_X 10
-#define TSTAMP_MIN_X (54 + TSTAMP_INPUT_X)
-
/* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */
static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd)
{
@@ -1308,7 +1302,7 @@ static int __init vivi_create_instance(int inst)
/* initialize queue */
q = &dev->vb_vidq;
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
q->drv_priv = dev;
q->buf_struct_size = sizeof(struct vivi_buffer);
q->ops = &vivi_video_qops;
diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c
index 12c70e876f5..a739ad492e7 100644
--- a/drivers/media/radio/radio-aimslab.c
+++ b/drivers/media/radio/radio-aimslab.c
@@ -82,7 +82,7 @@ static struct radio_isa_card *rtrack_alloc(void)
#define AIMS_BIT_VOL_UP (1 << 6) /* active low */
#define AIMS_BIT_VOL_DN (1 << 7) /* active low */
-void rtrack_set_pins(void *handle, u8 pins)
+static void rtrack_set_pins(void *handle, u8 pins)
{
struct radio_isa_card *isa = handle;
struct rtrack *rt = container_of(isa, struct rtrack, isa);
diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c
index 697a421c994..643d80ac28f 100644
--- a/drivers/media/radio/radio-cadet.c
+++ b/drivers/media/radio/radio-cadet.c
@@ -645,7 +645,8 @@ static int __init cadet_init(void)
set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags);
video_set_drvdata(&dev->vdev, dev);
- if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0)
+ res = video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr);
+ if (res < 0)
goto err_hdl;
v4l2_info(v4l2_dev, "ADS Cadet Radio Card at 0x%x\n", dev->io);
return 0;
diff --git a/drivers/media/radio/radio-isa.c b/drivers/media/radio/radio-isa.c
index 3c0067de432..84b7b9f4385 100644
--- a/drivers/media/radio/radio-isa.c
+++ b/drivers/media/radio/radio-isa.c
@@ -191,7 +191,7 @@ static bool radio_isa_valid_io(const struct radio_isa_driver *drv, int io)
return false;
}
-struct radio_isa_card *radio_isa_alloc(struct radio_isa_driver *drv,
+static struct radio_isa_card *radio_isa_alloc(struct radio_isa_driver *drv,
struct device *pdev)
{
struct v4l2_device *v4l2_dev;
@@ -207,8 +207,9 @@ struct radio_isa_card *radio_isa_alloc(struct radio_isa_driver *drv,
return isa;
}
-int radio_isa_common_probe(struct radio_isa_card *isa, struct device *pdev,
- int radio_nr, unsigned region_size)
+static int radio_isa_common_probe(struct radio_isa_card *isa,
+ struct device *pdev,
+ int radio_nr, unsigned region_size)
{
const struct radio_isa_driver *drv = isa->drv;
const struct radio_isa_ops *ops = drv->ops;
@@ -287,7 +288,8 @@ err_dev_reg:
return res;
}
-int radio_isa_common_remove(struct radio_isa_card *isa, unsigned region_size)
+static int radio_isa_common_remove(struct radio_isa_card *isa,
+ unsigned region_size)
{
const struct radio_isa_ops *ops = isa->drv->ops;
diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
index 227dcdb54df..c260a2a354b 100644
--- a/drivers/media/radio/radio-sf16fmi.c
+++ b/drivers/media/radio/radio-sf16fmi.c
@@ -64,7 +64,7 @@ bool pnp_attached;
#define FMI_BIT_VOL_SW (1 << 3)
#define FMI_BIT_TUN_STRQ (1 << 4)
-void fmi_set_pins(void *handle, u8 pins)
+static void fmi_set_pins(void *handle, u8 pins)
{
struct fmi *fmi = handle;
u8 bits = FMI_BIT_TUN_STRQ;
diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c
index d0c90531007..36aec575e0e 100644
--- a/drivers/media/radio/radio-tea5764.c
+++ b/drivers/media/radio/radio-tea5764.c
@@ -145,7 +145,7 @@ struct tea5764_device {
};
/* I2C code related */
-int tea5764_i2c_read(struct tea5764_device *radio)
+static int tea5764_i2c_read(struct tea5764_device *radio)
{
int i;
u16 *p = (u16 *) &radio->regs;
@@ -165,7 +165,7 @@ int tea5764_i2c_read(struct tea5764_device *radio)
return 0;
}
-int tea5764_i2c_write(struct tea5764_device *radio)
+static int tea5764_i2c_write(struct tea5764_device *radio)
{
struct tea5764_write_regs wr;
struct tea5764_regs *r = &radio->regs;
diff --git a/drivers/media/radio/si4713-i2c.c b/drivers/media/radio/si4713-i2c.c
index e3079c142c5..bd61b3bd0ca 100644
--- a/drivers/media/radio/si4713-i2c.c
+++ b/drivers/media/radio/si4713-i2c.c
@@ -1769,7 +1769,7 @@ exit:
}
/* si4713_ioctl - deal with private ioctls (only rnl for now) */
-long si4713_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+static long si4713_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
struct si4713_device *sdev = to_si4713_device(sd);
struct si4713_rnl *rnl = arg;
diff --git a/drivers/media/radio/wl128x/fmdrv.h b/drivers/media/radio/wl128x/fmdrv.h
index d84ad9dad32..aac0f025f76 100644
--- a/drivers/media/radio/wl128x/fmdrv.h
+++ b/drivers/media/radio/wl128x/fmdrv.h
@@ -60,7 +60,7 @@
#define fmdbg(format, ...) \
printk(KERN_DEBUG "fmdrv: " format, ## __VA_ARGS__)
#else /* DEBUG */
-#define fmdbg(format, ...)
+#define fmdbg(format, ...) do {} while(0)
#endif
enum {
FM_MODE_OFF,
diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c
index bf867a6b5ea..602ef7ac8c2 100644
--- a/drivers/media/radio/wl128x/fmdrv_common.c
+++ b/drivers/media/radio/wl128x/fmdrv_common.c
@@ -742,7 +742,7 @@ static void fm_irq_handle_rdsdata_getcmd_resp(struct fmdev *fmdev)
if ((meta_data & FM_RDS_STATUS_ERR_MASK) != 0)
break;
- if (blk_idx < FM_RDS_BLK_IDX_A || blk_idx > FM_RDS_BLK_IDX_D) {
+ if (blk_idx > FM_RDS_BLK_IDX_D) {
fmdbg("Block sequence mismatch\n");
rds->last_blk_idx = -1;
break;
diff --git a/drivers/media/radio/wl128x/fmdrv_rx.c b/drivers/media/radio/wl128x/fmdrv_rx.c
index 3dd9fc097c4..ebf09a3927d 100644
--- a/drivers/media/radio/wl128x/fmdrv_rx.c
+++ b/drivers/media/radio/wl128x/fmdrv_rx.c
@@ -305,7 +305,7 @@ int fm_rx_set_volume(struct fmdev *fmdev, u16 vol_to_set)
if (fmdev->curr_fmmode != FM_MODE_RX)
return -EPERM;
- if (vol_to_set < FM_RX_VOLUME_MIN || vol_to_set > FM_RX_VOLUME_MAX) {
+ if (vol_to_set > FM_RX_VOLUME_MAX) {
fmerr("Volume is not within(%d-%d) range\n",
FM_RX_VOLUME_MIN, FM_RX_VOLUME_MAX);
return -EINVAL;
diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c
index 49bb356ed14..2d6fb26a017 100644
--- a/drivers/media/rc/ati_remote.c
+++ b/drivers/media/rc/ati_remote.c
@@ -784,7 +784,7 @@ static void ati_remote_rc_init(struct ati_remote *ati_remote)
rdev->priv = ati_remote;
rdev->driver_type = RC_DRIVER_SCANCODE;
- rdev->allowed_protos = RC_TYPE_OTHER;
+ rdev->allowed_protos = RC_BIT_OTHER;
rdev->driver_name = "ati_remote";
rdev->open = ati_remote_rc_open;
diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index d05ac15b5de..22231dd4f62 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -329,7 +329,7 @@ static int ene_rx_get_sample_reg(struct ene_device *dev)
}
/* Sense current received carrier */
-void ene_rx_sense_carrier(struct ene_device *dev)
+static void ene_rx_sense_carrier(struct ene_device *dev)
{
DEFINE_IR_RAW_EVENT(ev);
@@ -1003,7 +1003,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL);
rdev = rc_allocate_device();
if (!dev || !rdev)
- goto error1;
+ goto failure;
/* validate resources */
error = -ENODEV;
@@ -1014,10 +1014,10 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
if (!pnp_port_valid(pnp_dev, 0) ||
pnp_port_len(pnp_dev, 0) < ENE_IO_SIZE)
- goto error;
+ goto failure;
if (!pnp_irq_valid(pnp_dev, 0))
- goto error;
+ goto failure;
spin_lock_init(&dev->hw_lock);
@@ -1033,7 +1033,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
/* detect hardware version and features */
error = ene_hw_detect(dev);
if (error)
- goto error;
+ goto failure;
if (!dev->hw_learning_and_tx_capable && txsim) {
dev->hw_learning_and_tx_capable = true;
@@ -1046,7 +1046,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
learning_mode_force = false;
rdev->driver_type = RC_DRIVER_IR_RAW;
- rdev->allowed_protos = RC_TYPE_ALL;
+ rdev->allowed_protos = RC_BIT_ALL;
rdev->priv = dev;
rdev->open = ene_open;
rdev->close = ene_close;
@@ -1078,30 +1078,27 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
/* claim the resources */
error = -EBUSY;
if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) {
- dev->hw_io = -1;
- dev->irq = -1;
- goto error;
+ goto failure;
}
dev->irq = pnp_irq(pnp_dev, 0);
if (request_irq(dev->irq, ene_isr,
IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev)) {
- dev->irq = -1;
- goto error;
+ goto failure2;
}
error = rc_register_device(rdev);
if (error < 0)
- goto error;
+ goto failure3;
pr_notice("driver has been successfully loaded\n");
return 0;
-error:
- if (dev && dev->irq >= 0)
- free_irq(dev->irq, dev);
- if (dev && dev->hw_io >= 0)
- release_region(dev->hw_io, ENE_IO_SIZE);
-error1:
+
+failure3:
+ free_irq(dev->irq, dev);
+failure2:
+ release_region(dev->hw_io, ENE_IO_SIZE);
+failure:
rc_free_device(rdev);
kfree(dev);
return error;
diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
index 52fd7696b1b..936c3f79b62 100644
--- a/drivers/media/rc/fintek-cir.c
+++ b/drivers/media/rc/fintek-cir.c
@@ -541,7 +541,7 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id
/* Set up the rc device */
rdev->priv = fintek;
rdev->driver_type = RC_DRIVER_IR_RAW;
- rdev->allowed_protos = RC_TYPE_ALL;
+ rdev->allowed_protos = RC_BIT_ALL;
rdev->open = fintek_open;
rdev->close = fintek_close;
rdev->input_name = FINTEK_DESCRIPTION;
@@ -684,12 +684,12 @@ static struct pnp_driver fintek_driver = {
.shutdown = fintek_shutdown,
};
-int fintek_init(void)
+static int fintek_init(void)
{
return pnp_register_driver(&fintek_driver);
}
-void fintek_exit(void)
+static void fintek_exit(void)
{
pnp_unregister_driver(&fintek_driver);
}
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
index 04cb272db16..ba1a1eb356c 100644
--- a/drivers/media/rc/gpio-ir-recv.c
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -95,7 +95,7 @@ static int __devinit gpio_ir_recv_probe(struct platform_device *pdev)
if (pdata->allowed_protos)
rcdev->allowed_protos = pdata->allowed_protos;
else
- rcdev->allowed_protos = RC_TYPE_ALL;
+ rcdev->allowed_protos = RC_BIT_ALL;
rcdev->map_name = pdata->map_name ?: RC_MAP_EMPTY;
gpio_dev->rcdev = rcdev;
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
index 51d7057aca0..5a9163da63c 100644
--- a/drivers/media/rc/iguanair.c
+++ b/drivers/media/rc/iguanair.c
@@ -499,7 +499,7 @@ static int __devinit iguanair_probe(struct usb_interface *intf,
usb_to_input_id(ir->udev, &rc->input_id);
rc->dev.parent = &intf->dev;
rc->driver_type = RC_DRIVER_IR_RAW;
- rc->allowed_protos = RC_TYPE_ALL;
+ rc->allowed_protos = RC_BIT_ALL;
rc->priv = ir;
rc->open = iguanair_open;
rc->close = iguanair_close;
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index 5dd0386604f..8f6a28921ed 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -1001,7 +1001,7 @@ static void imon_touch_display_timeout(unsigned long data)
* 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)
+static int imon_ir_change_protocol(struct rc_dev *rc, u64 *rc_type)
{
int retval;
struct imon_context *ictx = rc->priv;
@@ -1010,31 +1010,27 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 rc_type)
unsigned char ir_proto_packet[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86 };
- if (rc_type && !(rc_type & rc->allowed_protos))
+ if (*rc_type && !(*rc_type & rc->allowed_protos))
dev_warn(dev, "Looks like you're trying to use an IR protocol "
"this device does not support\n");
- switch (rc_type) {
- case RC_TYPE_RC6:
+ if (*rc_type & RC_BIT_RC6_MCE) {
dev_dbg(dev, "Configuring IR receiver for MCE protocol\n");
ir_proto_packet[0] = 0x01;
- break;
- case RC_TYPE_UNKNOWN:
- case RC_TYPE_OTHER:
+ *rc_type = RC_BIT_RC6_MCE;
+ } else if (*rc_type & RC_BIT_OTHER) {
dev_dbg(dev, "Configuring IR receiver for iMON protocol\n");
if (!pad_stabilize)
dev_dbg(dev, "PAD stabilize functionality disabled\n");
/* ir_proto_packet[0] = 0x00; // already the default */
- rc_type = RC_TYPE_OTHER;
- break;
- default:
+ *rc_type = RC_BIT_OTHER;
+ } else {
dev_warn(dev, "Unsupported IR protocol specified, overriding "
"to iMON IR protocol\n");
if (!pad_stabilize)
dev_dbg(dev, "PAD stabilize functionality disabled\n");
/* ir_proto_packet[0] = 0x00; // already the default */
- rc_type = RC_TYPE_OTHER;
- break;
+ *rc_type = RC_BIT_OTHER;
}
memcpy(ictx->usb_tx_buf, &ir_proto_packet, sizeof(ir_proto_packet));
@@ -1048,7 +1044,7 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 rc_type)
if (retval)
goto out;
- ictx->rc_type = rc_type;
+ ictx->rc_type = *rc_type;
ictx->pad_mouse = false;
out:
@@ -1323,7 +1319,7 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
rel_x = buf[2];
rel_y = buf[3];
- if (ictx->rc_type == RC_TYPE_OTHER && pad_stabilize) {
+ if (ictx->rc_type == RC_BIT_OTHER && pad_stabilize) {
if ((buf[1] == 0) && ((rel_x != 0) || (rel_y != 0))) {
dir = stabilize((int)rel_x, (int)rel_y,
timeout, threshold);
@@ -1390,7 +1386,7 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
buf[0] = 0x01;
buf[1] = buf[4] = buf[5] = buf[6] = buf[7] = 0;
- if (ictx->rc_type == RC_TYPE_OTHER && pad_stabilize) {
+ if (ictx->rc_type == RC_BIT_OTHER && pad_stabilize) {
dir = stabilize((int)rel_x, (int)rel_y,
timeout, threshold);
if (!dir) {
@@ -1511,7 +1507,7 @@ static void imon_incoming_packet(struct imon_context *ictx,
kc = imon_panel_key_lookup(scancode);
} else {
scancode = be32_to_cpu(*((u32 *)buf));
- if (ictx->rc_type == RC_TYPE_RC6) {
+ if (ictx->rc_type == RC_BIT_RC6_MCE) {
ktype = IMON_KEY_IMON;
if (buf[0] == 0x80)
ktype = IMON_KEY_MCE;
@@ -1744,7 +1740,7 @@ static void imon_get_ffdc_type(struct imon_context *ictx)
{
u8 ffdc_cfg_byte = ictx->usb_rx_buf[6];
u8 detected_display_type = IMON_DISPLAY_TYPE_NONE;
- u64 allowed_protos = RC_TYPE_OTHER;
+ u64 allowed_protos = RC_BIT_OTHER;
switch (ffdc_cfg_byte) {
/* iMON Knob, no display, iMON IR + vol knob */
@@ -1775,13 +1771,13 @@ static void imon_get_ffdc_type(struct imon_context *ictx)
case 0x9e:
dev_info(ictx->dev, "0xffdc iMON VFD, MCE IR");
detected_display_type = IMON_DISPLAY_TYPE_VFD;
- allowed_protos = RC_TYPE_RC6;
+ allowed_protos = RC_BIT_RC6_MCE;
break;
/* iMON LCD, MCE IR */
case 0x9f:
dev_info(ictx->dev, "0xffdc iMON LCD, MCE IR");
detected_display_type = IMON_DISPLAY_TYPE_LCD;
- allowed_protos = RC_TYPE_RC6;
+ allowed_protos = RC_BIT_RC6_MCE;
break;
default:
dev_info(ictx->dev, "Unknown 0xffdc device, "
@@ -1789,7 +1785,7 @@ static void imon_get_ffdc_type(struct imon_context *ictx)
detected_display_type = IMON_DISPLAY_TYPE_VFD;
/* We don't know which one it is, allow user to set the
* RC6 one from userspace if OTHER wasn't correct. */
- allowed_protos |= RC_TYPE_RC6;
+ allowed_protos |= RC_BIT_RC6_MCE;
break;
}
@@ -1875,7 +1871,7 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx)
rdev->priv = ictx;
rdev->driver_type = RC_DRIVER_SCANCODE;
- rdev->allowed_protos = RC_TYPE_OTHER | RC_TYPE_RC6; /* iMON PAD or MCE */
+ rdev->allowed_protos = RC_BIT_OTHER | RC_BIT_RC6_MCE; /* iMON PAD or MCE */
rdev->change_protocol = imon_ir_change_protocol;
rdev->driver_name = MOD_NAME;
@@ -1893,7 +1889,7 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx)
imon_set_display_type(ictx);
- if (ictx->rc_type == RC_TYPE_RC6)
+ if (ictx->rc_type == RC_BIT_RC6_MCE)
rdev->map_name = RC_MAP_IMON_MCE;
else
rdev->map_name = RC_MAP_IMON_PAD;
diff --git a/drivers/media/rc/ir-jvc-decoder.c b/drivers/media/rc/ir-jvc-decoder.c
index 035668e27f6..69edffb9fe9 100644
--- a/drivers/media/rc/ir-jvc-decoder.c
+++ b/drivers/media/rc/ir-jvc-decoder.c
@@ -47,7 +47,7 @@ static int ir_jvc_decode(struct rc_dev *dev, struct ir_raw_event ev)
{
struct jvc_dec *data = &dev->raw->jvc;
- if (!(dev->raw->enabled_protocols & RC_TYPE_JVC))
+ if (!(dev->raw->enabled_protocols & RC_BIT_JVC))
return 0;
if (!is_timing_event(ev)) {
@@ -174,7 +174,7 @@ out:
}
static struct ir_raw_handler jvc_handler = {
- .protocols = RC_TYPE_JVC,
+ .protocols = RC_BIT_JVC,
.decode = ir_jvc_decode,
};
diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c
index 870c93052fd..9945e5e7f61 100644
--- a/drivers/media/rc/ir-lirc-codec.c
+++ b/drivers/media/rc/ir-lirc-codec.c
@@ -35,7 +35,7 @@ static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev)
struct lirc_codec *lirc = &dev->raw->lirc;
int sample;
- if (!(dev->raw->enabled_protocols & RC_TYPE_LIRC))
+ if (!(dev->raw->enabled_protocols & RC_BIT_LIRC))
return 0;
if (!dev->raw->lirc.drv || !dev->raw->lirc.drv->rbuf)
@@ -408,7 +408,7 @@ static int ir_lirc_unregister(struct rc_dev *dev)
}
static struct ir_raw_handler lirc_handler = {
- .protocols = RC_TYPE_LIRC,
+ .protocols = RC_BIT_LIRC,
.decode = ir_lirc_decode,
.raw_register = ir_lirc_register,
.raw_unregister = ir_lirc_unregister,
diff --git a/drivers/media/rc/ir-mce_kbd-decoder.c b/drivers/media/rc/ir-mce_kbd-decoder.c
index 3784ebf80ec..33fafa4cf7c 100644
--- a/drivers/media/rc/ir-mce_kbd-decoder.c
+++ b/drivers/media/rc/ir-mce_kbd-decoder.c
@@ -216,7 +216,7 @@ static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev)
u32 scancode;
unsigned long delay;
- if (!(dev->raw->enabled_protocols & RC_TYPE_MCE_KBD))
+ if (!(dev->raw->enabled_protocols & RC_BIT_MCE_KBD))
return 0;
if (!is_timing_event(ev)) {
@@ -422,7 +422,7 @@ static int ir_mce_kbd_unregister(struct rc_dev *dev)
}
static struct ir_raw_handler mce_kbd_handler = {
- .protocols = RC_TYPE_MCE_KBD,
+ .protocols = RC_BIT_MCE_KBD,
.decode = ir_mce_kbd_decode,
.raw_register = ir_mce_kbd_register,
.raw_unregister = ir_mce_kbd_unregister,
diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c
index 2ca509e6e16..a47ee363496 100644
--- a/drivers/media/rc/ir-nec-decoder.c
+++ b/drivers/media/rc/ir-nec-decoder.c
@@ -52,7 +52,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev)
u8 address, not_address, command, not_command;
bool send_32bits = false;
- if (!(dev->raw->enabled_protocols & RC_TYPE_NEC))
+ if (!(dev->raw->enabled_protocols & RC_BIT_NEC))
return 0;
if (!is_timing_event(ev)) {
@@ -201,7 +201,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev)
}
static struct ir_raw_handler nec_handler = {
- .protocols = RC_TYPE_NEC,
+ .protocols = RC_BIT_NEC,
.decode = ir_nec_decode,
};
diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c
index 9ab663a507a..5b4d1ddeac4 100644
--- a/drivers/media/rc/ir-rc5-decoder.c
+++ b/drivers/media/rc/ir-rc5-decoder.c
@@ -52,8 +52,8 @@ static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev)
u8 toggle;
u32 scancode;
- if (!(dev->raw->enabled_protocols & RC_TYPE_RC5))
- return 0;
+ if (!(dev->raw->enabled_protocols & (RC_BIT_RC5 | RC_BIT_RC5X)))
+ return 0;
if (!is_timing_event(ev)) {
if (ev.reset)
@@ -128,6 +128,10 @@ again:
if (data->wanted_bits == RC5X_NBITS) {
/* RC5X */
u8 xdata, command, system;
+ if (!(dev->raw->enabled_protocols & RC_BIT_RC5X)) {
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
xdata = (data->bits & 0x0003F) >> 0;
command = (data->bits & 0x00FC0) >> 6;
system = (data->bits & 0x1F000) >> 12;
@@ -141,6 +145,10 @@ again:
} else {
/* RC5 */
u8 command, system;
+ if (!(dev->raw->enabled_protocols & RC_BIT_RC5)) {
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
command = (data->bits & 0x0003F) >> 0;
system = (data->bits & 0x007C0) >> 6;
toggle = (data->bits & 0x00800) ? 1 : 0;
@@ -164,7 +172,7 @@ out:
}
static struct ir_raw_handler rc5_handler = {
- .protocols = RC_TYPE_RC5,
+ .protocols = RC_BIT_RC5 | RC_BIT_RC5X,
.decode = ir_rc5_decode,
};
diff --git a/drivers/media/rc/ir-rc5-sz-decoder.c b/drivers/media/rc/ir-rc5-sz-decoder.c
index ec8d4a2e2c5..fd807a8308d 100644
--- a/drivers/media/rc/ir-rc5-sz-decoder.c
+++ b/drivers/media/rc/ir-rc5-sz-decoder.c
@@ -48,8 +48,8 @@ static int ir_rc5_sz_decode(struct rc_dev *dev, struct ir_raw_event ev)
u8 toggle, command, system;
u32 scancode;
- if (!(dev->raw->enabled_protocols & RC_TYPE_RC5_SZ))
- return 0;
+ if (!(dev->raw->enabled_protocols & RC_BIT_RC5_SZ))
+ return 0;
if (!is_timing_event(ev)) {
if (ev.reset)
@@ -128,7 +128,7 @@ out:
}
static struct ir_raw_handler rc5_sz_handler = {
- .protocols = RC_TYPE_RC5_SZ,
+ .protocols = RC_BIT_RC5_SZ,
.decode = ir_rc5_sz_decode,
};
diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c
index 4cfdd7fa4bb..e19072ffb36 100644
--- a/drivers/media/rc/ir-rc6-decoder.c
+++ b/drivers/media/rc/ir-rc6-decoder.c
@@ -89,7 +89,9 @@ static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev)
u32 scancode;
u8 toggle;
- if (!(dev->raw->enabled_protocols & RC_TYPE_RC6))
+ if (!(dev->raw->enabled_protocols &
+ (RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 |
+ RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE)))
return 0;
if (!is_timing_event(ev)) {
@@ -271,7 +273,9 @@ out:
}
static struct ir_raw_handler rc6_handler = {
- .protocols = RC_TYPE_RC6,
+ .protocols = RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 |
+ RC_BIT_RC6_6A_24 | RC_BIT_RC6_6A_32 |
+ RC_BIT_RC6_MCE,
.decode = ir_rc6_decode,
};
diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c
index 82e6c1e282d..9e76c7b40af 100644
--- a/drivers/media/rc/ir-rx51.c
+++ b/drivers/media/rc/ir-rx51.c
@@ -479,18 +479,7 @@ struct platform_driver lirc_rx51_platform_driver = {
.owner = THIS_MODULE,
},
};
-
-static int __init lirc_rx51_init(void)
-{
- return platform_driver_register(&lirc_rx51_platform_driver);
-}
-module_init(lirc_rx51_init);
-
-static void __exit lirc_rx51_exit(void)
-{
- platform_driver_unregister(&lirc_rx51_platform_driver);
-}
-module_exit(lirc_rx51_exit);
+module_platform_driver(lirc_rx51_platform_driver);
MODULE_DESCRIPTION("LIRC TX driver for Nokia RX51");
MODULE_AUTHOR("Nokia Corporation");
diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c
index 7e54ec57bcf..7e69a3b6537 100644
--- a/drivers/media/rc/ir-sanyo-decoder.c
+++ b/drivers/media/rc/ir-sanyo-decoder.c
@@ -58,7 +58,7 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev)
u32 scancode;
u8 address, command, not_command;
- if (!(dev->raw->enabled_protocols & RC_TYPE_SANYO))
+ if (!(dev->raw->enabled_protocols & RC_BIT_SANYO))
return 0;
if (!is_timing_event(ev)) {
@@ -179,7 +179,7 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev)
}
static struct ir_raw_handler sanyo_handler = {
- .protocols = RC_TYPE_SANYO,
+ .protocols = RC_BIT_SANYO,
.decode = ir_sanyo_decode,
};
diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c
index dab98b37621..fb914342cf4 100644
--- a/drivers/media/rc/ir-sony-decoder.c
+++ b/drivers/media/rc/ir-sony-decoder.c
@@ -45,7 +45,8 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev)
u32 scancode;
u8 device, subdevice, function;
- if (!(dev->raw->enabled_protocols & RC_TYPE_SONY))
+ if (!(dev->raw->enabled_protocols &
+ (RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20)))
return 0;
if (!is_timing_event(ev)) {
@@ -123,16 +124,28 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev)
switch (data->count) {
case 12:
+ if (!(dev->raw->enabled_protocols & RC_BIT_SONY12)) {
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
device = bitrev8((data->bits << 3) & 0xF8);
subdevice = 0;
function = bitrev8((data->bits >> 4) & 0xFE);
break;
case 15:
+ if (!(dev->raw->enabled_protocols & RC_BIT_SONY15)) {
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
device = bitrev8((data->bits >> 0) & 0xFF);
subdevice = 0;
function = bitrev8((data->bits >> 7) & 0xFE);
break;
case 20:
+ if (!(dev->raw->enabled_protocols & RC_BIT_SONY20)) {
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
device = bitrev8((data->bits >> 5) & 0xF8);
subdevice = bitrev8((data->bits >> 0) & 0xFF);
function = bitrev8((data->bits >> 12) & 0xFE);
@@ -157,7 +170,7 @@ out:
}
static struct ir_raw_handler sony_handler = {
- .protocols = RC_TYPE_SONY,
+ .protocols = RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20,
.decode = ir_sony_decode,
};
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index 24c77a42fc3..5e5a7f2b818 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -1563,7 +1563,7 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
/* set up ir-core props */
rdev->priv = itdev;
rdev->driver_type = RC_DRIVER_IR_RAW;
- rdev->allowed_protos = RC_TYPE_ALL;
+ rdev->allowed_protos = RC_BIT_ALL;
rdev->open = ite_open;
rdev->close = ite_close;
rdev->s_idle = ite_s_idle;
@@ -1708,12 +1708,12 @@ static struct pnp_driver ite_driver = {
.shutdown = ite_shutdown,
};
-int ite_init(void)
+static int ite_init(void)
{
return pnp_register_driver(&ite_driver);
}
-void ite_exit(void)
+static void ite_exit(void)
{
pnp_unregister_driver(&ite_driver);
}
diff --git a/drivers/media/rc/keymaps/rc-imon-mce.c b/drivers/media/rc/keymaps/rc-imon-mce.c
index 124c7228ba8..f0da960560b 100644
--- a/drivers/media/rc/keymaps/rc-imon-mce.c
+++ b/drivers/media/rc/keymaps/rc-imon-mce.c
@@ -121,7 +121,7 @@ static struct rc_map_list imon_mce_map = {
.scan = imon_mce,
.size = ARRAY_SIZE(imon_mce),
/* its RC6, but w/a hardware decoder */
- .rc_type = RC_TYPE_RC6,
+ .rc_type = RC_TYPE_RC6_MCE,
.name = RC_MAP_IMON_MCE,
}
};
diff --git a/drivers/media/rc/keymaps/rc-rc6-mce.c b/drivers/media/rc/keymaps/rc-rc6-mce.c
index 753e43ec787..ef4006fe4de 100644
--- a/drivers/media/rc/keymaps/rc-rc6-mce.c
+++ b/drivers/media/rc/keymaps/rc-rc6-mce.c
@@ -97,7 +97,7 @@ static struct rc_map_list rc6_mce_map = {
.map = {
.scan = rc6_mce,
.size = ARRAY_SIZE(rc6_mce),
- .rc_type = RC_TYPE_RC6,
+ .rc_type = RC_TYPE_RC6_MCE,
.name = RC_MAP_RC6_MCE,
}
};
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index 850547fe711..b2146cd99fd 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -1205,7 +1205,7 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir)
rc->dev.parent = dev;
rc->priv = ir;
rc->driver_type = RC_DRIVER_IR_RAW;
- rc->allowed_protos = RC_TYPE_ALL;
+ rc->allowed_protos = RC_BIT_ALL;
rc->timeout = MS_TO_NS(100);
if (!ir->flags.no_tx) {
rc->s_tx_mask = mceusb_set_tx_mask;
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
index 2ea913a44ae..e4ea89a11ee 100644
--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -472,6 +472,7 @@ static void nvt_enable_wake(struct nvt_dev *nvt)
nvt_cir_wake_reg_write(nvt, 0, CIR_WAKE_IREN);
}
+#if 0 /* Currently unused */
/* rx carrier detect only works in learning mode, must be called w/nvt_lock */
static u32 nvt_rx_carrier_detect(struct nvt_dev *nvt)
{
@@ -504,7 +505,7 @@ static u32 nvt_rx_carrier_detect(struct nvt_dev *nvt)
return carrier;
}
-
+#endif
/*
* set carrier frequency
*
@@ -620,7 +621,6 @@ static void nvt_dump_rx_buf(struct nvt_dev *nvt)
static void nvt_process_rx_ir_data(struct nvt_dev *nvt)
{
DEFINE_IR_RAW_EVENT(rawir);
- u32 carrier;
u8 sample;
int i;
@@ -629,9 +629,6 @@ static void nvt_process_rx_ir_data(struct nvt_dev *nvt)
if (debug)
nvt_dump_rx_buf(nvt);
- if (nvt->carrier_detect_enabled)
- carrier = nvt_rx_carrier_detect(nvt);
-
nvt_dbg_verbose("Processing buffer of len %d", nvt->pkts);
init_ir_raw_event(&rawir);
@@ -1045,7 +1042,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
/* Set up the rc device */
rdev->priv = nvt;
rdev->driver_type = RC_DRIVER_IR_RAW;
- rdev->allowed_protos = RC_TYPE_ALL;
+ rdev->allowed_protos = RC_BIT_ALL;
rdev->open = nvt_open;
rdev->close = nvt_close;
rdev->tx_ir = nvt_tx_ir;
@@ -1220,12 +1217,12 @@ static struct pnp_driver nvt_driver = {
.shutdown = nvt_shutdown,
};
-int nvt_init(void)
+static int nvt_init(void)
{
return pnp_register_driver(&nvt_driver);
}
-void nvt_exit(void)
+static void nvt_exit(void)
{
pnp_unregister_driver(&nvt_driver);
}
diff --git a/drivers/media/rc/nuvoton-cir.h b/drivers/media/rc/nuvoton-cir.h
index 0d5e0872a2e..7c3674ff5ea 100644
--- a/drivers/media/rc/nuvoton-cir.h
+++ b/drivers/media/rc/nuvoton-cir.h
@@ -103,7 +103,6 @@ struct nvt_dev {
/* rx settings */
bool learning_enabled;
- bool carrier_detect_enabled;
/* track cir wake state */
u8 wake_state;
diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c
index f9be68132c6..53d02827a47 100644
--- a/drivers/media/rc/rc-loopback.c
+++ b/drivers/media/rc/rc-loopback.c
@@ -195,7 +195,7 @@ static int __init loop_init(void)
rc->map_name = RC_MAP_EMPTY;
rc->priv = &loopdev;
rc->driver_type = RC_DRIVER_IR_RAW;
- rc->allowed_protos = RC_TYPE_ALL;
+ rc->allowed_protos = RC_BIT_ALL;
rc->timeout = 100 * 1000 * 1000; /* 100 ms */
rc->min_timeout = 1;
rc->max_timeout = UINT_MAX;
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index cabc19c1051..601d1ac1c68 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -725,25 +725,36 @@ static struct class ir_input_class = {
.devnode = ir_devnode,
};
+/*
+ * These are the protocol textual descriptions that are
+ * used by the sysfs protocols file. Note that the order
+ * of the entries is relevant.
+ */
static struct {
u64 type;
char *name;
} proto_names[] = {
- { RC_TYPE_UNKNOWN, "unknown" },
- { RC_TYPE_RC5, "rc-5" },
- { RC_TYPE_NEC, "nec" },
- { RC_TYPE_RC6, "rc-6" },
- { RC_TYPE_JVC, "jvc" },
- { RC_TYPE_SONY, "sony" },
- { RC_TYPE_RC5_SZ, "rc-5-sz" },
- { RC_TYPE_SANYO, "sanyo" },
- { RC_TYPE_MCE_KBD, "mce_kbd" },
- { RC_TYPE_LIRC, "lirc" },
- { RC_TYPE_OTHER, "other" },
+ { RC_BIT_NONE, "none" },
+ { RC_BIT_OTHER, "other" },
+ { RC_BIT_UNKNOWN, "unknown" },
+ { RC_BIT_RC5 |
+ RC_BIT_RC5X, "rc-5" },
+ { RC_BIT_NEC, "nec" },
+ { RC_BIT_RC6_0 |
+ RC_BIT_RC6_6A_20 |
+ RC_BIT_RC6_6A_24 |
+ RC_BIT_RC6_6A_32 |
+ RC_BIT_RC6_MCE, "rc-6" },
+ { RC_BIT_JVC, "jvc" },
+ { RC_BIT_SONY12 |
+ RC_BIT_SONY15 |
+ RC_BIT_SONY20, "sony" },
+ { RC_BIT_RC5_SZ, "rc-5-sz" },
+ { RC_BIT_SANYO, "sanyo" },
+ { RC_BIT_MCE_KBD, "mce_kbd" },
+ { RC_BIT_LIRC, "lirc" },
};
-#define PROTO_NONE "none"
-
/**
* show_protocols() - shows the current IR protocol(s)
* @device: the device descriptor
@@ -790,6 +801,9 @@ static ssize_t show_protocols(struct device *device,
tmp += sprintf(tmp, "[%s] ", proto_names[i].name);
else if (allowed & proto_names[i].type)
tmp += sprintf(tmp, "%s ", proto_names[i].name);
+
+ if (allowed & proto_names[i].type)
+ allowed &= ~proto_names[i].type;
}
if (tmp != buf)
@@ -867,26 +881,20 @@ static ssize_t store_protocols(struct device *device,
disable = false;
}
- if (!enable && !disable && !strncasecmp(tmp, PROTO_NONE, sizeof(PROTO_NONE))) {
- tmp += sizeof(PROTO_NONE);
- mask = 0;
- count++;
- } else {
- for (i = 0; i < ARRAY_SIZE(proto_names); i++) {
- if (!strcasecmp(tmp, proto_names[i].name)) {
- tmp += strlen(proto_names[i].name);
- mask = proto_names[i].type;
- break;
- }
- }
- if (i == ARRAY_SIZE(proto_names)) {
- IR_dprintk(1, "Unknown protocol: '%s'\n", tmp);
- ret = -EINVAL;
- goto out;
+ for (i = 0; i < ARRAY_SIZE(proto_names); i++) {
+ if (!strcasecmp(tmp, proto_names[i].name)) {
+ mask = proto_names[i].type;
+ break;
}
- count++;
}
+ if (i == ARRAY_SIZE(proto_names)) {
+ IR_dprintk(1, "Unknown protocol: '%s'\n", tmp);
+ return -EINVAL;
+ }
+
+ count++;
+
if (enable)
type |= mask;
else if (disable)
@@ -902,7 +910,7 @@ static ssize_t store_protocols(struct device *device,
}
if (dev->change_protocol) {
- rc = dev->change_protocol(dev, type);
+ rc = dev->change_protocol(dev, &type);
if (rc < 0) {
IR_dprintk(1, "Error setting protocols to 0x%llx\n",
(long long)type);
@@ -1117,7 +1125,8 @@ int rc_register_device(struct rc_dev *dev)
}
if (dev->change_protocol) {
- rc = dev->change_protocol(dev, rc_map->rc_type);
+ u64 rc_type = (1 << rc_map->rc_type);
+ rc = dev->change_protocol(dev, &rc_type);
if (rc < 0)
goto out_raw;
}
diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
index 9f5a17bb5ef..a8887aba9fa 100644
--- a/drivers/media/rc/redrat3.c
+++ b/drivers/media/rc/redrat3.c
@@ -1082,7 +1082,7 @@ static struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3)
rc->dev.parent = dev;
rc->priv = rr3;
rc->driver_type = RC_DRIVER_IR_RAW;
- rc->allowed_protos = RC_TYPE_ALL;
+ rc->allowed_protos = RC_BIT_ALL;
rc->timeout = US_TO_NS(2750);
rc->tx_ir = redrat3_transmit_ir;
rc->s_tx_carrier = redrat3_set_tx_carrier;
diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c
index d6f4bfe0939..c720f12f661 100644
--- a/drivers/media/rc/streamzap.c
+++ b/drivers/media/rc/streamzap.c
@@ -322,7 +322,7 @@ static struct rc_dev *streamzap_init_rc_dev(struct streamzap_ir *sz)
rdev->dev.parent = dev;
rdev->priv = sz;
rdev->driver_type = RC_DRIVER_IR_RAW;
- rdev->allowed_protos = RC_TYPE_ALL;
+ rdev->allowed_protos = RC_BIT_ALL;
rdev->driver_name = DRIVER_NAME;
rdev->map_name = RC_MAP_STREAMZAP;
diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c
index fef05235234..f0921b5483e 100644
--- a/drivers/media/rc/ttusbir.c
+++ b/drivers/media/rc/ttusbir.c
@@ -316,7 +316,7 @@ static int __devinit ttusbir_probe(struct usb_interface *intf,
usb_to_input_id(tt->udev, &rc->input_id);
rc->dev.parent = &intf->dev;
rc->driver_type = RC_DRIVER_IR_RAW;
- rc->allowed_protos = RC_TYPE_ALL;
+ rc->allowed_protos = RC_BIT_ALL;
rc->priv = tt;
rc->driver_name = DRIVER_NAME;
rc->map_name = RC_MAP_TT_1500;
diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c
index 7c9b5f33113..7f3c476dde0 100644
--- a/drivers/media/rc/winbond-cir.c
+++ b/drivers/media/rc/winbond-cir.c
@@ -7,6 +7,7 @@
* with minor modifications.
*
* Original Author: David Härdeman <david@hardeman.nu>
+ * Copyright (C) 2012 Sean Young <sean@mess.org>
* Copyright (C) 2009 - 2011 David Härdeman <david@hardeman.nu>
*
* Dedicated to my daughter Matilda, without whose loving attention this
@@ -22,9 +23,7 @@
* o IR Receive
* o IR Transmit
* o Wake-On-CIR functionality
- *
- * To do:
- * o Learning
+ * o Carrier detection
*
* 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
@@ -149,6 +148,12 @@
#define WBCIR_REGSEL_MASK 0x20
/* Starting address of selected register in WBCIR_REG_WCEIR_INDEX */
#define WBCIR_REG_ADDR0 0x00
+/* Enable carrier counter */
+#define WBCIR_CNTR_EN 0x01
+/* Reset carrier counter */
+#define WBCIR_CNTR_R 0x02
+/* Invert TX */
+#define WBCIR_IRTX_INV 0x04
/* Valid banks for the SP3 UART */
enum wbcir_bank {
@@ -184,7 +189,7 @@ enum wbcir_txstate {
};
/* Misc */
-#define WBCIR_NAME "winbond-cir"
+#define WBCIR_NAME "Winbond CIR"
#define WBCIR_ID_FAMILY 0xF1 /* Family ID for the WPCD376I */
#define WBCIR_ID_CHIP 0x04 /* Chip ID for the WPCD376I */
#define INVALID_SCANCODE 0x7FFFFFFF /* Invalid with all protos */
@@ -207,7 +212,8 @@ struct wbcir_data {
/* RX state */
enum wbcir_rxstate rxstate;
struct led_trigger *rxtrigger;
- struct ir_raw_event rxev;
+ int carrier_report_enabled;
+ u32 pulse_duration;
/* TX state */
enum wbcir_txstate txstate;
@@ -330,6 +336,30 @@ wbcir_to_rc6cells(u8 val)
*****************************************************************************/
static void
+wbcir_carrier_report(struct wbcir_data *data)
+{
+ unsigned counter = inb(data->ebase + WBCIR_REG_ECEIR_CNT_LO) |
+ inb(data->ebase + WBCIR_REG_ECEIR_CNT_HI) << 8;
+
+ if (counter > 0 && counter < 0xffff) {
+ DEFINE_IR_RAW_EVENT(ev);
+
+ ev.carrier_report = 1;
+ ev.carrier = DIV_ROUND_CLOSEST(counter * 1000000u,
+ data->pulse_duration);
+
+ ir_raw_event_store(data->dev, &ev);
+ }
+
+ /* reset and restart the counter */
+ data->pulse_duration = 0;
+ wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL, WBCIR_CNTR_R,
+ WBCIR_CNTR_EN | WBCIR_CNTR_R);
+ wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL, WBCIR_CNTR_EN,
+ WBCIR_CNTR_EN | WBCIR_CNTR_R);
+}
+
+static void
wbcir_idle_rx(struct rc_dev *dev, bool idle)
{
struct wbcir_data *data = dev->priv;
@@ -339,9 +369,16 @@ wbcir_idle_rx(struct rc_dev *dev, bool idle)
led_trigger_event(data->rxtrigger, LED_FULL);
}
- if (idle && data->rxstate != WBCIR_RXSTATE_INACTIVE)
+ if (idle && data->rxstate != WBCIR_RXSTATE_INACTIVE) {
+ data->rxstate = WBCIR_RXSTATE_INACTIVE;
+ led_trigger_event(data->rxtrigger, LED_OFF);
+
+ if (data->carrier_report_enabled)
+ wbcir_carrier_report(data);
+
/* Tell hardware to go idle by setting RXINACTIVE */
outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR);
+ }
}
static void
@@ -349,21 +386,22 @@ wbcir_irq_rx(struct wbcir_data *data, struct pnp_dev *device)
{
u8 irdata;
DEFINE_IR_RAW_EVENT(rawir);
+ unsigned duration;
/* Since RXHDLEV is set, at least 8 bytes are in the FIFO */
while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL) {
irdata = inb(data->sbase + WBCIR_REG_SP3_RXDATA);
if (data->rxstate == WBCIR_RXSTATE_ERROR)
continue;
+
+ duration = ((irdata & 0x7F) + 1) * 2;
rawir.pulse = irdata & 0x80 ? false : true;
- rawir.duration = US_TO_NS(((irdata & 0x7F) + 1) * 10);
- ir_raw_event_store_with_filter(data->dev, &rawir);
- }
+ rawir.duration = US_TO_NS(duration);
- /* Check if we should go idle */
- if (data->dev->idle) {
- led_trigger_event(data->rxtrigger, LED_OFF);
- data->rxstate = WBCIR_RXSTATE_INACTIVE;
+ if (rawir.pulse)
+ data->pulse_duration += duration;
+
+ ir_raw_event_store_with_filter(data->dev, &rawir);
}
ir_raw_event_handle(data->dev);
@@ -492,6 +530,33 @@ wbcir_irq_handler(int irqno, void *cookie)
*****************************************************************************/
static int
+wbcir_set_carrier_report(struct rc_dev *dev, int enable)
+{
+ struct wbcir_data *data = dev->priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&data->spinlock, flags);
+
+ if (data->carrier_report_enabled == enable) {
+ spin_unlock_irqrestore(&data->spinlock, flags);
+ return 0;
+ }
+
+ data->pulse_duration = 0;
+ wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL, WBCIR_CNTR_R,
+ WBCIR_CNTR_EN | WBCIR_CNTR_R);
+
+ if (enable && data->dev->idle)
+ wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL,
+ WBCIR_CNTR_EN, WBCIR_CNTR_EN | WBCIR_CNTR_R);
+
+ data->carrier_report_enabled = enable;
+ spin_unlock_irqrestore(&data->spinlock, flags);
+
+ return 0;
+}
+
+static int
wbcir_txcarrier(struct rc_dev *dev, u32 carrier)
{
struct wbcir_data *data = dev->priv;
@@ -837,7 +902,7 @@ wbcir_init_hw(struct wbcir_data *data)
/* Set IRTX_INV */
if (invert)
- outb(0x04, data->ebase + WBCIR_REG_ECEIR_CCTL);
+ outb(WBCIR_IRTX_INV, data->ebase + WBCIR_REG_ECEIR_CCTL);
else
outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL);
@@ -866,8 +931,8 @@ wbcir_init_hw(struct wbcir_data *data)
/* prescaler 1.0, tx/rx fifo lvl 16 */
outb(0x30, data->sbase + WBCIR_REG_SP3_EXCR2);
- /* Set baud divisor to sample every 10 us */
- outb(0x0F, data->sbase + WBCIR_REG_SP3_BGDL);
+ /* Set baud divisor to sample every 2 ns */
+ outb(0x03, data->sbase + WBCIR_REG_SP3_BGDL);
outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH);
/* Set CEIR mode */
@@ -876,9 +941,12 @@ wbcir_init_hw(struct wbcir_data *data)
inb(data->sbase + WBCIR_REG_SP3_LSR); /* Clear LSR */
inb(data->sbase + WBCIR_REG_SP3_MSR); /* Clear MSR */
- /* Disable RX demod, enable run-length enc/dec, set freq span */
+ /*
+ * Disable RX demod, enable run-length enc/dec, set freq span and
+ * enable over-sampling
+ */
wbcir_select_bank(data, WBCIR_BANK_7);
- outb(0x90, data->sbase + WBCIR_REG_SP3_RCCFG);
+ outb(0xd0, data->sbase + WBCIR_REG_SP3_RCCFG);
/* Disable timer */
wbcir_select_bank(data, WBCIR_BANK_4);
@@ -915,9 +983,8 @@ wbcir_init_hw(struct wbcir_data *data)
/* Clear RX state */
data->rxstate = WBCIR_RXSTATE_INACTIVE;
- data->rxev.duration = 0;
ir_raw_event_reset(data->dev);
- ir_raw_event_handle(data->dev);
+ ir_raw_event_set_idle(data->dev, true);
/* Clear TX state */
if (data->txstate == WBCIR_TXSTATE_ACTIVE) {
@@ -1007,7 +1074,7 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
}
data->dev->driver_type = RC_DRIVER_IR_RAW;
- data->dev->driver_name = WBCIR_NAME;
+ data->dev->driver_name = DRVNAME;
data->dev->input_name = WBCIR_NAME;
data->dev->input_phys = "wbcir/cir0";
data->dev->input_id.bustype = BUS_HOST;
@@ -1016,13 +1083,15 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
data->dev->input_id.version = WBCIR_ID_CHIP;
data->dev->map_name = RC_MAP_RC6_MCE;
data->dev->s_idle = wbcir_idle_rx;
+ data->dev->s_carrier_report = wbcir_set_carrier_report;
data->dev->s_tx_mask = wbcir_txmask;
data->dev->s_tx_carrier = wbcir_txcarrier;
data->dev->tx_ir = wbcir_tx;
data->dev->priv = data;
data->dev->dev.parent = &device->dev;
data->dev->timeout = MS_TO_NS(100);
- data->dev->allowed_protos = RC_TYPE_ALL;
+ data->dev->rx_resolution = US_TO_NS(2);
+ data->dev->allowed_protos = RC_BIT_ALL;
if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) {
dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
diff --git a/drivers/media/tuners/fc2580.c b/drivers/media/tuners/fc2580.c
index aff39ae457a..81f38aae9c6 100644
--- a/drivers/media/tuners/fc2580.c
+++ b/drivers/media/tuners/fc2580.c
@@ -35,8 +35,6 @@
* Currently it blind writes bunch of static registers from the
* fc2580_freq_regs_lut[] when fc2580_set_params() is called. Add some
* logic to reduce unneeded register writes.
- * There is also don't-care registers, initialized with value 0xff, and those
- * are also written to the chip currently (yes, not wise).
*/
/* write multiple registers */
@@ -111,6 +109,17 @@ static int fc2580_rd_reg(struct fc2580_priv *priv, u8 reg, u8 *val)
return fc2580_rd_regs(priv, reg, val, 1);
}
+/* write single register conditionally only when value differs from 0xff
+ * XXX: This is special routine meant only for writing fc2580_freq_regs_lut[]
+ * values. Do not use for the other purposes. */
+static int fc2580_wr_reg_ff(struct fc2580_priv *priv, u8 reg, u8 val)
+{
+ if (val == 0xff)
+ return 0;
+ else
+ return fc2580_wr_regs(priv, reg, &val, 1);
+}
+
static int fc2580_set_params(struct dvb_frontend *fe)
{
struct fc2580_priv *priv = fe->tuner_priv;
@@ -213,99 +222,99 @@ static int fc2580_set_params(struct dvb_frontend *fe)
if (i == ARRAY_SIZE(fc2580_freq_regs_lut))
goto err;
- ret = fc2580_wr_reg(priv, 0x25, fc2580_freq_regs_lut[i].r25_val);
+ ret = fc2580_wr_reg_ff(priv, 0x25, fc2580_freq_regs_lut[i].r25_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x27, fc2580_freq_regs_lut[i].r27_val);
+ ret = fc2580_wr_reg_ff(priv, 0x27, fc2580_freq_regs_lut[i].r27_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x28, fc2580_freq_regs_lut[i].r28_val);
+ ret = fc2580_wr_reg_ff(priv, 0x28, fc2580_freq_regs_lut[i].r28_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x29, fc2580_freq_regs_lut[i].r29_val);
+ ret = fc2580_wr_reg_ff(priv, 0x29, fc2580_freq_regs_lut[i].r29_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x2b, fc2580_freq_regs_lut[i].r2b_val);
+ ret = fc2580_wr_reg_ff(priv, 0x2b, fc2580_freq_regs_lut[i].r2b_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x2c, fc2580_freq_regs_lut[i].r2c_val);
+ ret = fc2580_wr_reg_ff(priv, 0x2c, fc2580_freq_regs_lut[i].r2c_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x2d, fc2580_freq_regs_lut[i].r2d_val);
+ ret = fc2580_wr_reg_ff(priv, 0x2d, fc2580_freq_regs_lut[i].r2d_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x30, fc2580_freq_regs_lut[i].r30_val);
+ ret = fc2580_wr_reg_ff(priv, 0x30, fc2580_freq_regs_lut[i].r30_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x44, fc2580_freq_regs_lut[i].r44_val);
+ ret = fc2580_wr_reg_ff(priv, 0x44, fc2580_freq_regs_lut[i].r44_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x50, fc2580_freq_regs_lut[i].r50_val);
+ ret = fc2580_wr_reg_ff(priv, 0x50, fc2580_freq_regs_lut[i].r50_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x53, fc2580_freq_regs_lut[i].r53_val);
+ ret = fc2580_wr_reg_ff(priv, 0x53, fc2580_freq_regs_lut[i].r53_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x5f, fc2580_freq_regs_lut[i].r5f_val);
+ ret = fc2580_wr_reg_ff(priv, 0x5f, fc2580_freq_regs_lut[i].r5f_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x61, fc2580_freq_regs_lut[i].r61_val);
+ ret = fc2580_wr_reg_ff(priv, 0x61, fc2580_freq_regs_lut[i].r61_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x62, fc2580_freq_regs_lut[i].r62_val);
+ ret = fc2580_wr_reg_ff(priv, 0x62, fc2580_freq_regs_lut[i].r62_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x63, fc2580_freq_regs_lut[i].r63_val);
+ ret = fc2580_wr_reg_ff(priv, 0x63, fc2580_freq_regs_lut[i].r63_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x67, fc2580_freq_regs_lut[i].r67_val);
+ ret = fc2580_wr_reg_ff(priv, 0x67, fc2580_freq_regs_lut[i].r67_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x68, fc2580_freq_regs_lut[i].r68_val);
+ ret = fc2580_wr_reg_ff(priv, 0x68, fc2580_freq_regs_lut[i].r68_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x69, fc2580_freq_regs_lut[i].r69_val);
+ ret = fc2580_wr_reg_ff(priv, 0x69, fc2580_freq_regs_lut[i].r69_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x6a, fc2580_freq_regs_lut[i].r6a_val);
+ ret = fc2580_wr_reg_ff(priv, 0x6a, fc2580_freq_regs_lut[i].r6a_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x6b, fc2580_freq_regs_lut[i].r6b_val);
+ ret = fc2580_wr_reg_ff(priv, 0x6b, fc2580_freq_regs_lut[i].r6b_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x6c, fc2580_freq_regs_lut[i].r6c_val);
+ ret = fc2580_wr_reg_ff(priv, 0x6c, fc2580_freq_regs_lut[i].r6c_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x6d, fc2580_freq_regs_lut[i].r6d_val);
+ ret = fc2580_wr_reg_ff(priv, 0x6d, fc2580_freq_regs_lut[i].r6d_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x6e, fc2580_freq_regs_lut[i].r6e_val);
+ ret = fc2580_wr_reg_ff(priv, 0x6e, fc2580_freq_regs_lut[i].r6e_val);
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x6f, fc2580_freq_regs_lut[i].r6f_val);
+ ret = fc2580_wr_reg_ff(priv, 0x6f, fc2580_freq_regs_lut[i].r6f_val);
if (ret < 0)
goto err;
diff --git a/drivers/media/tuners/max2165.c b/drivers/media/tuners/max2165.c
index ba84936aafd..95ed46f2cd2 100644
--- a/drivers/media/tuners/max2165.c
+++ b/drivers/media/tuners/max2165.c
@@ -161,7 +161,7 @@ static int max2165_set_bandwidth(struct max2165_priv *priv, u32 bw)
return 0;
}
-int fixpt_div32(u32 dividend, u32 divisor, u32 *quotient, u32 *fraction)
+static int fixpt_div32(u32 dividend, u32 divisor, u32 *quotient, u32 *fraction)
{
u32 remainder;
u32 q, f = 0;
diff --git a/drivers/media/tuners/tua9001.c b/drivers/media/tuners/tua9001.c
index 38966847407..83a6240f64d 100644
--- a/drivers/media/tuners/tua9001.c
+++ b/drivers/media/tuners/tua9001.c
@@ -136,7 +136,7 @@ static int tua9001_set_params(struct dvb_frontend *fe)
{
struct tua9001_priv *priv = fe->tuner_priv;
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret, i;
+ int ret = 0, i;
u16 val;
u32 frequency;
struct reg_val data[2];
diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c
index 4937712278f..5c0fd787cc8 100644
--- a/drivers/media/tuners/xc4000.c
+++ b/drivers/media/tuners/xc4000.c
@@ -934,7 +934,7 @@ static int check_firmware(struct dvb_frontend *fe, unsigned int type,
int rc = 0, is_retry = 0;
u16 hwmodel;
v4l2_std_id std0;
- u8 hw_major, hw_minor, fw_major, fw_minor;
+ u8 hw_major = 0, hw_minor = 0, fw_major = 0, fw_minor = 0;
dprintk(1, "%s called\n", __func__);
diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c
index 448361c6a13..0cb7c28dcb1 100644
--- a/drivers/media/usb/au0828/au0828-cards.c
+++ b/drivers/media/usb/au0828/au0828-cards.c
@@ -25,7 +25,7 @@
#include "media/tuner.h"
#include "media/v4l2-common.h"
-void hvr950q_cs5340_audio(void *priv, int enable)
+static void hvr950q_cs5340_audio(void *priv, int enable)
{
/* Because the HVR-950q shares an i2s bus between the cs5340 and the
au8522, we need to hold cs5340 in reset when using the au8522 */
diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c
index b328f6550d0..9a6f15613a3 100644
--- a/drivers/media/usb/au0828/au0828-dvb.c
+++ b/drivers/media/usb/au0828/au0828-dvb.c
@@ -272,7 +272,6 @@ static void au0828_restart_dvb_streaming(struct work_struct *work)
struct au0828_dev *dev = container_of(work, struct au0828_dev,
restart_streaming);
struct au0828_dvb *dvb = &dev->dvb;
- int ret;
if (dev->urb_streaming == 0)
return;
@@ -282,7 +281,7 @@ static void au0828_restart_dvb_streaming(struct work_struct *work)
mutex_lock(&dvb->lock);
/* Stop transport */
- ret = stop_urb_transfer(dev);
+ stop_urb_transfer(dev);
au0828_write(dev, 0x608, 0x00);
au0828_write(dev, 0x609, 0x00);
au0828_write(dev, 0x60a, 0x00);
@@ -293,7 +292,7 @@ static void au0828_restart_dvb_streaming(struct work_struct *work)
au0828_write(dev, 0x609, 0x72);
au0828_write(dev, 0x60a, 0x71);
au0828_write(dev, 0x60b, 0x01);
- ret = start_urb_transfer(dev);
+ start_urb_transfer(dev);
mutex_unlock(&dvb->lock);
}
diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c
index 87058557057..45387aab10c 100644
--- a/drivers/media/usb/au0828/au0828-video.c
+++ b/drivers/media/usb/au0828/au0828-video.c
@@ -158,7 +158,7 @@ static void au0828_irq_callback(struct urb *urb)
/*
* Stop and Deallocate URBs
*/
-void au0828_uninit_isoc(struct au0828_dev *dev)
+static void au0828_uninit_isoc(struct au0828_dev *dev)
{
struct urb *urb;
int i;
@@ -197,9 +197,9 @@ void au0828_uninit_isoc(struct au0828_dev *dev)
/*
* Allocate URBs and start IRQ
*/
-int au0828_init_isoc(struct au0828_dev *dev, int max_packets,
- int num_bufs, int max_pkt_size,
- int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb))
+static int au0828_init_isoc(struct au0828_dev *dev, int max_packets,
+ int num_bufs, int max_pkt_size,
+ int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb))
{
struct au0828_dmaqueue *dma_q = &dev->vidq;
int i;
@@ -783,7 +783,7 @@ static int au0828_i2s_init(struct au0828_dev *dev)
* Auvitek au0828 analog stream enable
* Please set interface0 to AS5 before enable the stream
*/
-int au0828_analog_stream_enable(struct au0828_dev *d)
+static int au0828_analog_stream_enable(struct au0828_dev *d)
{
dprintk(1, "au0828_analog_stream_enable called\n");
au0828_writereg(d, AU0828_SENSORCTRL_VBI_103, 0x00);
@@ -810,7 +810,7 @@ int au0828_analog_stream_disable(struct au0828_dev *d)
return 0;
}
-void au0828_analog_stream_reset(struct au0828_dev *dev)
+static void au0828_analog_stream_reset(struct au0828_dev *dev)
{
dprintk(1, "au0828_analog_stream_reset called\n");
au0828_writereg(dev, AU0828_SENSORCTRL_100, 0x0);
@@ -913,7 +913,7 @@ static int get_ressource(struct au0828_fh *fh)
/* This function ensures that video frames continue to be delivered even if
the ITU-656 input isn't receiving any data (thereby preventing applications
such as tvtime from hanging) */
-void au0828_vid_buffer_timeout(unsigned long data)
+static void au0828_vid_buffer_timeout(unsigned long data)
{
struct au0828_dev *dev = (struct au0828_dev *) data;
struct au0828_dmaqueue *dma_q = &dev->vidq;
@@ -937,7 +937,7 @@ void au0828_vid_buffer_timeout(unsigned long data)
spin_unlock_irqrestore(&dev->slock, flags);
}
-void au0828_vbi_buffer_timeout(unsigned long data)
+static void au0828_vbi_buffer_timeout(unsigned long data)
{
struct au0828_dev *dev = (struct au0828_dev *) data;
struct au0828_dmaqueue *dma_q = &dev->vbiq;
diff --git a/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c
index 447148eff95..72220791374 100644
--- a/drivers/media/usb/cx231xx/cx231xx-avcore.c
+++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c
@@ -1068,12 +1068,12 @@ int cx231xx_unmute_audio(struct cx231xx *dev)
}
EXPORT_SYMBOL_GPL(cx231xx_unmute_audio);
-int stopAudioFirmware(struct cx231xx *dev)
+static int stopAudioFirmware(struct cx231xx *dev)
{
return vid_blk_write_byte(dev, DL_CTL_CONTROL, 0x03);
}
-int restartAudioFirmware(struct cx231xx *dev)
+static int restartAudioFirmware(struct cx231xx *dev)
{
return vid_blk_write_byte(dev, DL_CTL_CONTROL, 0x13);
}
@@ -2631,11 +2631,6 @@ int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type)
rc = cx231xx_stop_stream(dev, ep_mask);
}
- if (dev->mode == CX231XX_ANALOG_MODE)
- ;/* do any in Analog mode */
- else
- ;/* do any in digital mode */
-
return rc;
}
EXPORT_SYMBOL_GPL(cx231xx_capture_start);
diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index b84ebc54d91..bbed1e40eed 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
@@ -686,7 +686,7 @@ int cx231xx_tuner_callback(void *ptr, int component, int command, int arg)
}
EXPORT_SYMBOL_GPL(cx231xx_tuner_callback);
-void cx231xx_reset_out(struct cx231xx *dev)
+static void cx231xx_reset_out(struct cx231xx *dev)
{
cx231xx_set_gpio_value(dev, CX23417_RESET, 1);
msleep(200);
@@ -694,11 +694,13 @@ void cx231xx_reset_out(struct cx231xx *dev)
msleep(200);
cx231xx_set_gpio_value(dev, CX23417_RESET, 1);
}
-void cx231xx_enable_OSC(struct cx231xx *dev)
+
+static void cx231xx_enable_OSC(struct cx231xx *dev)
{
cx231xx_set_gpio_value(dev, CX23417_OSC_EN, 1);
}
-void cx231xx_sleep_s5h1432(struct cx231xx *dev)
+
+static void cx231xx_sleep_s5h1432(struct cx231xx *dev)
{
cx231xx_set_gpio_value(dev, SLEEP_S5H1432, 0);
}
diff --git a/drivers/media/usb/cx231xx/cx231xx-i2c.c b/drivers/media/usb/cx231xx/cx231xx-i2c.c
index 781feed406f..96a5a096539 100644
--- a/drivers/media/usb/cx231xx/cx231xx-i2c.c
+++ b/drivers/media/usb/cx231xx/cx231xx-i2c.c
@@ -72,8 +72,8 @@ static inline bool is_tuner(struct cx231xx *dev, struct cx231xx_i2c *bus,
/*
* cx231xx_i2c_send_bytes()
*/
-int cx231xx_i2c_send_bytes(struct i2c_adapter *i2c_adap,
- const struct i2c_msg *msg)
+static int cx231xx_i2c_send_bytes(struct i2c_adapter *i2c_adap,
+ const struct i2c_msg *msg)
{
struct cx231xx_i2c *bus = i2c_adap->algo_data;
struct cx231xx *dev = bus->dev;
diff --git a/drivers/media/usb/cx231xx/cx231xx-input.c b/drivers/media/usb/cx231xx/cx231xx-input.c
index 96176e9db5a..0f7b4244682 100644
--- a/drivers/media/usb/cx231xx/cx231xx-input.c
+++ b/drivers/media/usb/cx231xx/cx231xx-input.c
@@ -99,7 +99,7 @@ int cx231xx_ir_init(struct cx231xx *dev)
/* The i2c micro-controller only outputs the cmd part of NEC protocol */
dev->init_data.rc_dev->scanmask = 0xff;
dev->init_data.rc_dev->driver_name = "cx231xx";
- dev->init_data.type = RC_TYPE_NEC;
+ dev->init_data.type = RC_BIT_NEC;
info.addr = 0x30;
/* Load and bind ir-kbd-i2c */
diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c
index 3d7526e28d4..943d9342370 100644
--- a/drivers/media/usb/dvb-usb-v2/af9015.c
+++ b/drivers/media/usb/dvb-usb-v2/af9015.c
@@ -1306,7 +1306,7 @@ static int af9015_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
if (!rc->map_name)
rc->map_name = RC_MAP_EMPTY;
- rc->allowed_protos = RC_TYPE_NEC;
+ rc->allowed_protos = RC_BIT_NEC;
rc->query = af9015_rc_query;
rc->interval = 500;
diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c
index ea27eaff4e3..61ae7f9d0b2 100644
--- a/drivers/media/usb/dvb-usb-v2/af9035.c
+++ b/drivers/media/usb/dvb-usb-v2/af9035.c
@@ -1023,10 +1023,10 @@ static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
switch (tmp) {
case 0: /* NEC */
default:
- rc->allowed_protos = RC_TYPE_NEC;
+ rc->allowed_protos = RC_BIT_NEC;
break;
case 1: /* RC6 */
- rc->allowed_protos = RC_TYPE_RC6;
+ rc->allowed_protos = RC_BIT_RC6_MCE;
break;
}
diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c
index ec540140c81..d05c5b563da 100644
--- a/drivers/media/usb/dvb-usb-v2/anysee.c
+++ b/drivers/media/usb/dvb-usb-v2/anysee.c
@@ -1048,7 +1048,7 @@ static int anysee_rc_query(struct dvb_usb_device *d)
static int anysee_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
{
- rc->allowed_protos = RC_TYPE_NEC;
+ rc->allowed_protos = RC_BIT_NEC;
rc->query = anysee_rc_query;
rc->interval = 250; /* windows driver uses 500ms */
@@ -1170,7 +1170,7 @@ static int anysee_ci_poll_slot_status(struct dvb_ca_en50221 *ci, int slot,
struct dvb_usb_device *d = ci->data;
struct anysee_state *state = d_to_priv(d);
int ret;
- u8 tmp;
+ u8 tmp = 0;
ret = anysee_rd_reg_mask(d, REG_IOC, &tmp, 0x40);
if (ret)
diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c
index 54f1221d930..d75dbf27e99 100644
--- a/drivers/media/usb/dvb-usb-v2/az6007.c
+++ b/drivers/media/usb/dvb-usb-v2/az6007.c
@@ -826,7 +826,7 @@ static int az6007_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
{
pr_debug("Getting az6007 Remote Control properties\n");
- rc->allowed_protos = RC_TYPE_NEC;
+ rc->allowed_protos = RC_BIT_NEC;
rc->query = az6007_rc_query;
rc->interval = 400;
diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h
index bae16a1189d..059291b892b 100644
--- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h
@@ -137,7 +137,7 @@ struct dvb_usb_driver_info {
struct dvb_usb_rc {
const char *map_name;
u64 allowed_protos;
- int (*change_protocol)(struct rc_dev *dev, u64 rc_type);
+ int (*change_protocol)(struct rc_dev *dev, u64 *rc_type);
int (*query) (struct dvb_usb_device *d);
unsigned int interval;
const enum rc_driver_type driver_type;
diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
index ba51f65204d..671b4fa232b 100644
--- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
@@ -224,7 +224,7 @@ static void dvb_usb_data_complete_raw(struct usb_data_stream *stream, u8 *buf,
dvb_dmx_swfilter_raw(&adap->demux, buf, len);
}
-int dvb_usbv2_adapter_stream_init(struct dvb_usb_adapter *adap)
+static int dvb_usbv2_adapter_stream_init(struct dvb_usb_adapter *adap)
{
dev_dbg(&adap_to_d(adap)->udev->dev, "%s: adap=%d\n", __func__,
adap->id);
@@ -236,7 +236,7 @@ int dvb_usbv2_adapter_stream_init(struct dvb_usb_adapter *adap)
return usb_urb_initv2(&adap->stream, &adap->props->stream);
}
-int dvb_usbv2_adapter_stream_exit(struct dvb_usb_adapter *adap)
+static int dvb_usbv2_adapter_stream_exit(struct dvb_usb_adapter *adap)
{
dev_dbg(&adap_to_d(adap)->udev->dev, "%s: adap=%d\n", __func__,
adap->id);
@@ -368,7 +368,7 @@ static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
return dvb_usb_ctrl_feed(dvbdmxfeed, -1);
}
-int dvb_usbv2_adapter_dvb_init(struct dvb_usb_adapter *adap)
+static int dvb_usbv2_adapter_dvb_init(struct dvb_usb_adapter *adap)
{
int ret;
struct dvb_usb_device *d = adap_to_d(adap);
@@ -440,7 +440,7 @@ err_dvb_register_adapter:
return ret;
}
-int dvb_usbv2_adapter_dvb_exit(struct dvb_usb_adapter *adap)
+static int dvb_usbv2_adapter_dvb_exit(struct dvb_usb_adapter *adap)
{
dev_dbg(&adap_to_d(adap)->udev->dev, "%s: adap=%d\n", __func__,
adap->id);
@@ -456,7 +456,7 @@ int dvb_usbv2_adapter_dvb_exit(struct dvb_usb_adapter *adap)
return 0;
}
-int dvb_usbv2_device_power_ctrl(struct dvb_usb_device *d, int onoff)
+static int dvb_usbv2_device_power_ctrl(struct dvb_usb_device *d, int onoff)
{
int ret;
@@ -553,7 +553,7 @@ err:
return ret;
}
-int dvb_usbv2_adapter_frontend_init(struct dvb_usb_adapter *adap)
+static int dvb_usbv2_adapter_frontend_init(struct dvb_usb_adapter *adap)
{
int ret, i, count_registered = 0;
struct dvb_usb_device *d = adap_to_d(adap);
@@ -622,7 +622,7 @@ err:
return ret;
}
-int dvb_usbv2_adapter_frontend_exit(struct dvb_usb_adapter *adap)
+static int dvb_usbv2_adapter_frontend_exit(struct dvb_usb_adapter *adap)
{
int i;
dev_dbg(&adap_to_d(adap)->udev->dev, "%s: adap=%d\n", __func__,
diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c
index 695f9106bc5..47204280b8b 100644
--- a/drivers/media/usb/dvb-usb-v2/it913x.c
+++ b/drivers/media/usb/dvb-usb-v2/it913x.c
@@ -659,13 +659,19 @@ static int it913x_frontend_attach(struct dvb_usb_adapter *adap)
it913x_wr_reg(d, DEV_0_DMOD, MP2IF2_SW_RST, 0x1);
it913x_wr_reg(d, DEV_0, EP0_TX_EN, 0x0f);
it913x_wr_reg(d, DEV_0, EP0_TX_NAK, 0x1b);
- it913x_wr_reg(d, DEV_0, EP0_TX_EN, 0x2f);
+ if (st->proprietary_ir == false) /* Enable endpoint 3 */
+ it913x_wr_reg(d, DEV_0, EP0_TX_EN, 0x3f);
+ else
+ it913x_wr_reg(d, DEV_0, EP0_TX_EN, 0x2f);
it913x_wr_reg(d, DEV_0, EP4_TX_LEN_LSB,
ep_size & 0xff);
it913x_wr_reg(d, DEV_0, EP4_TX_LEN_MSB, ep_size >> 8);
ret = it913x_wr_reg(d, DEV_0, EP4_MAX_PKT, pkt_size);
} else if (adap->id == 1 && adap->fe[0]) {
- it913x_wr_reg(d, DEV_0, EP0_TX_EN, 0x6f);
+ if (st->proprietary_ir == false)
+ it913x_wr_reg(d, DEV_0, EP0_TX_EN, 0x7f);
+ else
+ it913x_wr_reg(d, DEV_0, EP0_TX_EN, 0x6f);
it913x_wr_reg(d, DEV_0, EP5_TX_LEN_LSB,
ep_size & 0xff);
it913x_wr_reg(d, DEV_0, EP5_TX_LEN_MSB, ep_size >> 8);
@@ -698,7 +704,7 @@ static int it913x_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
return 0;
}
- rc->allowed_protos = RC_TYPE_NEC;
+ rc->allowed_protos = RC_BIT_NEC;
rc->query = it913x_rc_query;
rc->interval = 250;
diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c
index c41d9d9ec7b..6427ac359f2 100644
--- a/drivers/media/usb/dvb-usb-v2/lmedm04.c
+++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c
@@ -799,7 +799,7 @@ static const char fw_c_rs2000[] = LME2510_C_RS2000;
static const char fw_lg[] = LME2510_LG;
static const char fw_s0194[] = LME2510_S0194;
-const char *lme_firmware_switch(struct dvb_usb_device *d, int cold)
+static const char *lme_firmware_switch(struct dvb_usb_device *d, int cold)
{
struct lme2510_state *st = d->priv;
struct usb_device *udev = d->udev;
@@ -1253,7 +1253,7 @@ static int lme2510_get_stream_config(struct dvb_frontend *fe, u8 *ts_type,
static int lme2510_get_rc_config(struct dvb_usb_device *d,
struct dvb_usb_rc *rc)
{
- rc->allowed_protos = RC_TYPE_NEC;
+ rc->allowed_protos = RC_BIT_NEC;
return 0;
}
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
index 093f1acce40..a4c302d0aa3 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
@@ -1197,7 +1197,7 @@ static int rtl2831u_get_rc_config(struct dvb_usb_device *d,
struct dvb_usb_rc *rc)
{
rc->map_name = RC_MAP_EMPTY;
- rc->allowed_protos = RC_TYPE_NEC;
+ rc->allowed_protos = RC_BIT_NEC;
rc->query = rtl2831u_rc_query;
rc->interval = 400;
@@ -1269,7 +1269,7 @@ static int rtl2832u_get_rc_config(struct dvb_usb_device *d,
struct dvb_usb_rc *rc)
{
rc->map_name = RC_MAP_EMPTY;
- rc->allowed_protos = RC_TYPE_NEC;
+ rc->allowed_protos = RC_BIT_NEC;
rc->query = rtl2832u_rc_query;
rc->interval = 400;
@@ -1338,6 +1338,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = {
&rtl2832u_props, "G-Tek Electronics Group Lifeview LV5TDLX DVB-T", NULL) },
{ DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK,
&rtl2832u_props, "NOXON DAB/DAB+ USB dongle", NULL) },
+ { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK_REV2,
+ &rtl2832u_props, "NOXON DAB/DAB+ USB dongle (rev 2)", NULL) },
{ DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_TREKSTOR_TERRES_2_0,
&rtl2832u_props, "Trekstor DVB-T Stick Terres 2.0", NULL) },
{ DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1101,
diff --git a/drivers/media/usb/dvb-usb-v2/usb_urb.c b/drivers/media/usb/dvb-usb-v2/usb_urb.c
index 5989b659037..7346f85f3f2 100644
--- a/drivers/media/usb/dvb-usb-v2/usb_urb.c
+++ b/drivers/media/usb/dvb-usb-v2/usb_urb.c
@@ -112,7 +112,7 @@ int usb_urb_submitv2(struct usb_data_stream *stream,
return 0;
}
-int usb_urb_free_urbs(struct usb_data_stream *stream)
+static int usb_urb_free_urbs(struct usb_data_stream *stream)
{
int i;
@@ -205,7 +205,7 @@ static int usb_urb_alloc_isoc_urbs(struct usb_data_stream *stream)
return 0;
}
-int usb_free_stream_buffers(struct usb_data_stream *stream)
+static int usb_free_stream_buffers(struct usb_data_stream *stream)
{
if (stream->state & USB_STATE_URB_BUF) {
while (stream->buf_num) {
@@ -223,8 +223,8 @@ int usb_free_stream_buffers(struct usb_data_stream *stream)
return 0;
}
-int usb_alloc_stream_buffers(struct usb_data_stream *stream, int num,
- unsigned long size)
+static int usb_alloc_stream_buffers(struct usb_data_stream *stream, int num,
+ unsigned long size)
{
stream->buf_num = 0;
stream->buf_size = size;
diff --git a/drivers/media/usb/dvb-usb/az6027.c b/drivers/media/usb/dvb-usb/az6027.c
index 5e45ae60542..91e0119e8a8 100644
--- a/drivers/media/usb/dvb-usb/az6027.c
+++ b/drivers/media/usb/dvb-usb/az6027.c
@@ -298,7 +298,8 @@ struct stb6100_config az6027_stb6100_config = {
/* check for mutex FIXME */
-int az6027_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen)
+static int az6027_usb_in_op(struct dvb_usb_device *d, u8 req,
+ u16 value, u16 index, u8 *b, int blen)
{
int ret = -1;
if (mutex_lock_interruptible(&d->usb_mutex))
@@ -1051,10 +1052,10 @@ static struct i2c_algorithm az6027_i2c_algo = {
.functionality = az6027_i2c_func,
};
-int az6027_identify_state(struct usb_device *udev,
- struct dvb_usb_device_properties *props,
- struct dvb_usb_device_description **desc,
- int *cold)
+static int az6027_identify_state(struct usb_device *udev,
+ struct dvb_usb_device_properties *props,
+ struct dvb_usb_device_description **desc,
+ int *cold)
{
u8 *b;
s16 ret;
diff --git a/drivers/media/usb/dvb-usb/dib0700.h b/drivers/media/usb/dvb-usb/dib0700.h
index 7de125c0b36..637b6123f39 100644
--- a/drivers/media/usb/dvb-usb/dib0700.h
+++ b/drivers/media/usb/dvb-usb/dib0700.h
@@ -64,7 +64,7 @@ extern int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff);
extern struct i2c_algorithm dib0700_i2c_algo;
extern int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props,
struct dvb_usb_device_description **desc, int *cold);
-extern int dib0700_change_protocol(struct rc_dev *dev, u64 rc_type);
+extern int dib0700_change_protocol(struct rc_dev *dev, u64 *rc_type);
extern int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz);
extern int dib0700_device_count;
diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c
index ef87229de6a..19b5ed2825d 100644
--- a/drivers/media/usb/dvb-usb/dib0700_core.c
+++ b/drivers/media/usb/dvb-usb/dib0700_core.c
@@ -605,7 +605,7 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
return ret;
}
-int dib0700_change_protocol(struct rc_dev *rc, u64 rc_type)
+int dib0700_change_protocol(struct rc_dev *rc, u64 *rc_type)
{
struct dvb_usb_device *d = rc->priv;
struct dib0700_state *st = d->priv;
@@ -621,17 +621,19 @@ int dib0700_change_protocol(struct rc_dev *rc, u64 rc_type)
st->buf[2] = 0;
/* Set the IR mode */
- if (rc_type == RC_TYPE_RC5)
+ if (*rc_type & RC_BIT_RC5) {
new_proto = 1;
- else if (rc_type == RC_TYPE_NEC)
+ *rc_type = RC_BIT_RC5;
+ } else if (*rc_type & RC_BIT_NEC) {
new_proto = 0;
- else if (rc_type == RC_TYPE_RC6) {
+ *rc_type = RC_BIT_NEC;
+ } else if (*rc_type & RC_BIT_RC6_MCE) {
if (st->fw_version < 0x10200) {
ret = -EINVAL;
goto out;
}
-
new_proto = 2;
+ *rc_type = RC_BIT_RC6_MCE;
} else {
ret = -EINVAL;
goto out;
@@ -645,7 +647,7 @@ int dib0700_change_protocol(struct rc_dev *rc, u64 rc_type)
goto out;
}
- d->props.rc.core.protocol = rc_type;
+ d->props.rc.core.protocol = *rc_type;
out:
mutex_unlock(&d->usb_mutex);
@@ -707,7 +709,7 @@ static void dib0700_rc_urb_completion(struct urb *purb)
purb->actual_length);
switch (d->props.rc.core.protocol) {
- case RC_TYPE_NEC:
+ case RC_BIT_NEC:
toggle = 0;
/* NEC protocol sends repeat code as 0 0 0 FF */
diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c
index 510001da6e8..11798426fa8 100644
--- a/drivers/media/usb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/usb/dvb-usb/dib0700_devices.c
@@ -518,7 +518,7 @@ static int dib0700_rc_query_old_firmware(struct dvb_usb_device *d)
d->last_event = 0;
switch (d->props.rc.core.protocol) {
- case RC_TYPE_NEC:
+ case RC_BIT_NEC:
/* NEC protocol sends repeat code as 0 0 0 FF */
if ((key[3-2] == 0x00) && (key[3-3] == 0x00) &&
(key[3] == 0xff))
@@ -3658,9 +3658,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -3698,9 +3698,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -3763,9 +3763,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -3808,9 +3808,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -3890,9 +3890,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -3936,9 +3936,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -3987,9 +3987,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -4055,9 +4055,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -4106,9 +4106,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_NEC_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -4177,9 +4177,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -4215,9 +4215,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -4295,9 +4295,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -4341,9 +4341,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_NEC_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -4394,9 +4394,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -4433,9 +4433,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -4472,9 +4472,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -4511,9 +4511,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -4550,9 +4550,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -4589,9 +4589,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -4644,9 +4644,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -4681,9 +4681,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -4721,9 +4721,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -4761,9 +4761,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
@@ -4802,9 +4802,9 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
.module_name = "dib0700",
.rc_query = dib0700_rc_query_old_firmware,
- .allowed_protos = RC_TYPE_RC5 |
- RC_TYPE_RC6 |
- RC_TYPE_NEC,
+ .allowed_protos = RC_BIT_RC5 |
+ RC_BIT_RC6_MCE |
+ RC_BIT_NEC,
.change_protocol = dib0700_change_protocol,
},
},
diff --git a/drivers/media/usb/dvb-usb/dvb-usb.h b/drivers/media/usb/dvb-usb/dvb-usb.h
index aab0f99bc89..ce4c4e3b58b 100644
--- a/drivers/media/usb/dvb-usb/dvb-usb.h
+++ b/drivers/media/usb/dvb-usb/dvb-usb.h
@@ -202,7 +202,7 @@ struct dvb_rc {
u64 protocol;
u64 allowed_protos;
enum rc_driver_type driver_type;
- int (*change_protocol)(struct rc_dev *dev, u64 rc_type);
+ int (*change_protocol)(struct rc_dev *dev, u64 *rc_type);
char *module_name;
int (*rc_query) (struct dvb_usb_device *d);
int rc_interval;
diff --git a/drivers/media/usb/dvb-usb/pctv452e.c b/drivers/media/usb/dvb-usb/pctv452e.c
index 02e878577c3..d1ddfa13de8 100644
--- a/drivers/media/usb/dvb-usb/pctv452e.c
+++ b/drivers/media/usb/dvb-usb/pctv452e.c
@@ -927,7 +927,7 @@ static struct dvb_usb_device_properties pctv452e_properties = {
.rc.core = {
.rc_codes = RC_MAP_DIB0700_RC5_TABLE,
- .allowed_protos = RC_TYPE_UNKNOWN,
+ .allowed_protos = RC_BIT_UNKNOWN,
.rc_query = pctv452e_rc_query,
.rc_interval = 100,
},
@@ -980,7 +980,7 @@ static struct dvb_usb_device_properties tt_connect_s2_3600_properties = {
.rc.core = {
.rc_codes = RC_MAP_TT_1500,
- .allowed_protos = RC_TYPE_UNKNOWN,
+ .allowed_protos = RC_BIT_UNKNOWN,
.rc_query = pctv452e_rc_query,
.rc_interval = 100,
},
diff --git a/drivers/media/usb/dvb-usb/technisat-usb2.c b/drivers/media/usb/dvb-usb/technisat-usb2.c
index 7a8c8c18590..40832a1aef6 100644
--- a/drivers/media/usb/dvb-usb/technisat-usb2.c
+++ b/drivers/media/usb/dvb-usb/technisat-usb2.c
@@ -732,7 +732,7 @@ static struct dvb_usb_device_properties technisat_usb2_devices = {
.rc_codes = RC_MAP_TECHNISAT_USB2,
.module_name = "technisat-usb2",
.rc_query = technisat_usb2_rc_query,
- .allowed_protos = RC_TYPE_ALL,
+ .allowed_protos = RC_BIT_ALL,
.driver_type = RC_DRIVER_IR_RAW,
}
};
diff --git a/drivers/media/usb/dvb-usb/ttusb2.c b/drivers/media/usb/dvb-usb/ttusb2.c
index 6a50cdea3bc..bcdac225ebe 100644
--- a/drivers/media/usb/dvb-usb/ttusb2.c
+++ b/drivers/media/usb/dvb-usb/ttusb2.c
@@ -741,7 +741,7 @@ static struct dvb_usb_device_properties ttusb2_properties_ct3650 = {
.rc_interval = 150, /* Less than IR_KEYPRESS_TIMEOUT */
.rc_codes = RC_MAP_TT_1500,
.rc_query = tt3650_rc_query,
- .allowed_protos = RC_TYPE_UNKNOWN,
+ .allowed_protos = RC_BIT_UNKNOWN,
},
.num_adapters = 1,
diff --git a/drivers/media/usb/dvb-usb/vp702x.c b/drivers/media/usb/dvb-usb/vp702x.c
index 07c673a6e76..22cf9f96cb9 100644
--- a/drivers/media/usb/dvb-usb/vp702x.c
+++ b/drivers/media/usb/dvb-usb/vp702x.c
@@ -56,7 +56,7 @@ static int vp702x_usb_in_op_unlocked(struct dvb_usb_device *d, u8 req,
}
int vp702x_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value,
- u16 index, u8 *b, int blen)
+ u16 index, u8 *b, int blen)
{
int ret;
@@ -67,8 +67,8 @@ int vp702x_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value,
return ret;
}
-int vp702x_usb_out_op_unlocked(struct dvb_usb_device *d, u8 req, u16 value,
- u16 index, u8 *b, int blen)
+static int vp702x_usb_out_op_unlocked(struct dvb_usb_device *d, u8 req,
+ u16 value, u16 index, u8 *b, int blen)
{
int ret;
deb_xfer("out: req. %02x, val: %04x, ind: %04x, buffer: ",req,value,index);
@@ -86,7 +86,7 @@ int vp702x_usb_out_op_unlocked(struct dvb_usb_device *d, u8 req, u16 value,
return 0;
}
-int vp702x_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
+static int vp702x_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
u16 index, u8 *b, int blen)
{
int ret;
diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index 16a84f9f46d..619bffbab3b 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -1979,6 +1979,15 @@ struct em28xx_board em28xx_boards[] = {
EM28XX_I2C_CLK_WAIT_ENABLE |
EM28XX_I2C_FREQ_400_KHZ,
},
+ [EM2884_BOARD_TERRATEC_HTC_USB_XS] = {
+ .name = "Terratec Cinergy HTC USB XS",
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS,
+ .tuner_type = TUNER_ABSENT,
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ },
};
const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
@@ -2057,9 +2066,9 @@ struct usb_device_id em28xx_id_table[] = {
{ USB_DEVICE(0x0ccd, 0x0043),
.driver_info = EM2870_BOARD_TERRATEC_XS },
{ USB_DEVICE(0x0ccd, 0x008e), /* Cinergy HTC USB XS Rev. 1 */
- .driver_info = EM2884_BOARD_TERRATEC_H5 },
+ .driver_info = EM2884_BOARD_TERRATEC_HTC_USB_XS },
{ USB_DEVICE(0x0ccd, 0x00ac), /* Cinergy HTC USB XS Rev. 2 */
- .driver_info = EM2884_BOARD_TERRATEC_H5 },
+ .driver_info = EM2884_BOARD_TERRATEC_HTC_USB_XS },
{ USB_DEVICE(0x0ccd, 0x10a2), /* H5 Rev. 1 */
.driver_info = EM2884_BOARD_TERRATEC_H5 },
{ USB_DEVICE(0x0ccd, 0x10ad), /* H5 Rev. 2 */
@@ -3297,7 +3306,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
dev->num_alt = interface->num_altsetting;
- if ((card[nr] >= 0) && (card[nr] < em28xx_bcount))
+ if ((unsigned)card[nr] < em28xx_bcount)
dev->model = card[nr];
/* save our data pointer in this interface device */
diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
index 13ae821949e..63f2e7070c0 100644
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
@@ -331,7 +331,7 @@ static struct drxk_config hauppauge_930c_drxk = {
.load_firmware_sync = true,
};
-struct drxk_config terratec_htc_stick_drxk = {
+static struct drxk_config terratec_htc_stick_drxk = {
.adr = 0x29,
.single_master = 1,
.no_i2c_bridge = 1,
@@ -520,7 +520,10 @@ static void terratec_htc_stick_init(struct em28xx *dev)
{ -1, -1, -1, -1},
};
- /* Init the analog decoder? */
+ /*
+ * Init the analog decoder (not yet supported), but
+ * it's probably still a good idea.
+ */
struct {
unsigned char r[4];
int len;
@@ -547,6 +550,64 @@ static void terratec_htc_stick_init(struct em28xx *dev)
em28xx_gpio_set(dev, terratec_htc_stick_end);
};
+static void terratec_htc_usb_xs_init(struct em28xx *dev)
+{
+ int i;
+
+ struct em28xx_reg_seq terratec_htc_usb_xs_init[] = {
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {EM2874_R80_GPIO, 0xb2, 0xff, 100},
+ {EM2874_R80_GPIO, 0xb2, 0xff, 50},
+ {EM2874_R80_GPIO, 0xb6, 0xff, 100},
+ { -1, -1, -1, -1},
+ };
+ struct em28xx_reg_seq terratec_htc_usb_xs_end[] = {
+ {EM2874_R80_GPIO, 0xa6, 0xff, 100},
+ {EM2874_R80_GPIO, 0xa6, 0xff, 50},
+ {EM2874_R80_GPIO, 0xe6, 0xff, 100},
+ { -1, -1, -1, -1},
+ };
+
+ /*
+ * Init the analog decoder (not yet supported), but
+ * it's probably still a good idea.
+ */
+ struct {
+ unsigned char r[4];
+ int len;
+ } regs[] = {
+ {{ 0x06, 0x02, 0x00, 0x31 }, 4},
+ {{ 0x01, 0x02 }, 2},
+ {{ 0x01, 0x02, 0x00, 0xc6 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0xff, 0xaf }, 4},
+ {{ 0x01, 0x00, 0x03, 0xa0 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0x73, 0xaf }, 4},
+ {{ 0x04, 0x00 }, 2},
+ {{ 0x00, 0x04 }, 2},
+ {{ 0x00, 0x04, 0x00, 0x0a }, 4},
+ {{ 0x04, 0x14 }, 2},
+ {{ 0x04, 0x14, 0x00, 0x00 }, 4},
+ };
+
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
+
+ em28xx_gpio_set(dev, terratec_htc_usb_xs_init);
+
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
+ msleep(10);
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44);
+ msleep(10);
+
+ dev->i2c_client.addr = 0x82 >> 1;
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len);
+
+ em28xx_gpio_set(dev, terratec_htc_usb_xs_end);
+};
+
static void pctv_520e_init(struct em28xx *dev)
{
/*
@@ -1155,6 +1216,25 @@ static int em28xx_dvb_init(struct em28xx *dev)
goto out_free;
}
break;
+ case EM2884_BOARD_TERRATEC_HTC_USB_XS:
+ terratec_htc_usb_xs_init(dev);
+
+ /* attach demodulator */
+ dvb->fe[0] = dvb_attach(drxk_attach, &terratec_htc_stick_drxk,
+ &dev->i2c_adap);
+ if (!dvb->fe[0]) {
+ result = -EINVAL;
+ goto out_free;
+ }
+
+ /* Attach the demodulator. */
+ if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
+ &dev->i2c_adap,
+ &em28xx_cxd2820r_tda18271_config)) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
default:
em28xx_errdev("/2: The frontend of your DVB/ATSC card"
" isn't supported yet\n");
diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c
index 97d36b4f19d..660bf803c9e 100644
--- a/drivers/media/usb/em28xx/em28xx-input.c
+++ b/drivers/media/usb/em28xx/em28xx-input.c
@@ -345,7 +345,7 @@ static void em28xx_ir_stop(struct rc_dev *rc)
cancel_delayed_work_sync(&ir->work);
}
-static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type)
+static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type)
{
int rc = 0;
struct em28xx_IR *ir = rc_dev->priv;
@@ -354,14 +354,16 @@ static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type)
/* Adjust xclk based o IR table for RC5/NEC tables */
- if (rc_type == RC_TYPE_RC5) {
+ if (*rc_type & RC_BIT_RC5) {
dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE;
ir->full_code = 1;
- } else if (rc_type == RC_TYPE_NEC) {
+ *rc_type = RC_BIT_RC5;
+ } else if (*rc_type & RC_BIT_NEC) {
dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE;
ir_config = EM2874_IR_NEC;
ir->full_code = 1;
- } else if (rc_type != RC_TYPE_UNKNOWN)
+ *rc_type = RC_BIT_NEC;
+ } else if (*rc_type != RC_BIT_UNKNOWN)
rc = -EINVAL;
em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk,
@@ -524,6 +526,7 @@ static int em28xx_ir_init(struct em28xx *dev)
struct em28xx_IR *ir;
struct rc_dev *rc;
int err = -ENOMEM;
+ u64 rc_type;
if (dev->board.ir_codes == NULL) {
/* No remote control support */
@@ -546,14 +549,15 @@ static int em28xx_ir_init(struct em28xx *dev)
* em2874 supports more protocols. For now, let's just announce
* the two protocols that were already tested
*/
- rc->allowed_protos = RC_TYPE_RC5 | RC_TYPE_NEC;
+ rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC;
rc->priv = ir;
rc->change_protocol = em28xx_ir_change_protocol;
rc->open = em28xx_ir_start;
rc->close = em28xx_ir_stop;
/* By default, keep protocol field untouched */
- err = em28xx_ir_change_protocol(rc, RC_TYPE_UNKNOWN);
+ rc_type = RC_BIT_UNKNOWN;
+ err = em28xx_ir_change_protocol(rc, &rc_type);
if (err)
goto err_out_free;
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index 8757523e686..86e90d86da6 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -128,6 +128,7 @@
#define EM2874_BOARD_MAXMEDIA_UB425_TC 84
#define EM2884_BOARD_PCTV_510E 85
#define EM2884_BOARD_PCTV_520E 86
+#define EM2884_BOARD_TERRATEC_HTC_USB_XS 87
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c
index a2b934146eb..e0a431bb0d4 100644
--- a/drivers/media/usb/gspca/gspca.c
+++ b/drivers/media/usb/gspca/gspca.c
@@ -1586,8 +1586,7 @@ static int vidioc_querybuf(struct file *file, void *priv,
struct gspca_dev *gspca_dev = video_drvdata(file);
struct gspca_frame *frame;
- if (v4l2_buf->index < 0
- || v4l2_buf->index >= gspca_dev->nframes)
+ if (v4l2_buf->index >= gspca_dev->nframes)
return -EINVAL;
frame = &gspca_dev->frame[v4l2_buf->index];
diff --git a/drivers/media/usb/gspca/gspca.h b/drivers/media/usb/gspca/gspca.h
index e3eab82cd4e..352317d7acd 100644
--- a/drivers/media/usb/gspca/gspca.h
+++ b/drivers/media/usb/gspca/gspca.h
@@ -32,7 +32,7 @@ do { \
#define D_USBO 0x00
#define D_V4L2 0x0100
#else
-#define PDEBUG(level, fmt, ...)
+#define PDEBUG(level, fmt, ...) do {} while(0)
#endif
#define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */
diff --git a/drivers/media/usb/gspca/jeilinj.c b/drivers/media/usb/gspca/jeilinj.c
index b897aa86f31..1ba29fe7fad 100644
--- a/drivers/media/usb/gspca/jeilinj.c
+++ b/drivers/media/usb/gspca/jeilinj.c
@@ -114,7 +114,7 @@ static void jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command)
}
/* Responses are one byte only */
-static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char response)
+static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char *response)
{
int retval;
@@ -123,7 +123,7 @@ static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char response)
retval = usb_bulk_msg(gspca_dev->dev,
usb_rcvbulkpipe(gspca_dev->dev, 0x84),
gspca_dev->usb_buf, 1, NULL, 500);
- response = gspca_dev->usb_buf[0];
+ *response = gspca_dev->usb_buf[0];
if (retval < 0) {
pr_err("read command [%02x] error %d\n",
gspca_dev->usb_buf[0], retval);
@@ -260,7 +260,7 @@ static int jlj_start(struct gspca_dev *gspca_dev)
if (start_commands[i].delay)
msleep(start_commands[i].delay);
if (start_commands[i].ack_wanted)
- jlj_read1(gspca_dev, response);
+ jlj_read1(gspca_dev, &response);
}
setcamquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
msleep(2);
diff --git a/drivers/media/usb/gspca/m5602/m5602_s5k4aa.c b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.c
index cc8ec3f7e8d..c8e1572eb50 100644
--- a/drivers/media/usb/gspca/m5602/m5602_s5k4aa.c
+++ b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.c
@@ -74,6 +74,12 @@ static
DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 2548")
}
}, {
+ .ident = "Fujitsu-Siemens Amilo Pi 2530",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 2530")
+ }
+ }, {
.ident = "MSI GX700",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
diff --git a/drivers/media/usb/gspca/pac7302.c b/drivers/media/usb/gspca/pac7302.c
index 2d5c6d8343a..4f5869a9808 100644
--- a/drivers/media/usb/gspca/pac7302.c
+++ b/drivers/media/usb/gspca/pac7302.c
@@ -29,14 +29,13 @@
* Register page 0:
*
* Address Description
- * 0x02 Red balance control
- * 0x03 Green balance control
- * 0x04 Blue balance control
- * Valus are inverted (0=max, 255=min).
+ * 0x01 Red balance control
+ * 0x02 Green balance control
+ * 0x03 Blue balance control
* The Windows driver uses a quadratic approach to map
* the settable values (0-200) on register values:
- * min=0x80, default=0x40, max=0x20
- * 0x0f-0x20 Colors, saturation and exposure control
+ * min=0x20, default=0x40, max=0x80
+ * 0x0f-0x20 Color and saturation control
* 0xa2-0xab Brightness, contrast and gamma control
* 0xb6 Sharpness control (bits 0-4)
*
@@ -78,12 +77,12 @@
*
* Page | Register | Function
* -----+------------+---------------------------------------------------
+ * 0 | 0x01 | setredbalance()
+ * 0 | 0x03 | setbluebalance()
* 0 | 0x0f..0x20 | setcolors()
* 0 | 0xa2..0xab | setbrightcont()
* 0 | 0xb6 | setsharpness()
- * 0 | 0xc5 | setredbalance()
* 0 | 0xc6 | setwhitebalance()
- * 0 | 0xc7 | setbluebalance()
* 0 | 0xdc | setbrightcont(), setcolors()
* 3 | 0x02 | setexposure()
* 3 | 0x10, 0x12 | setgain()
@@ -99,10 +98,13 @@
/* Include pac common sof detection functions */
#include "pac_common.h"
-#define PAC7302_GAIN_DEFAULT 15
-#define PAC7302_GAIN_KNEE 42
-#define PAC7302_EXPOSURE_DEFAULT 66 /* 33 ms / 30 fps */
-#define PAC7302_EXPOSURE_KNEE 133 /* 66 ms / 15 fps */
+#define PAC7302_RGB_BALANCE_MIN 0
+#define PAC7302_RGB_BALANCE_MAX 200
+#define PAC7302_RGB_BALANCE_DEFAULT 100
+#define PAC7302_GAIN_DEFAULT 15
+#define PAC7302_GAIN_KNEE 42
+#define PAC7302_EXPOSURE_DEFAULT 66 /* 33 ms / 30 fps */
+#define PAC7302_EXPOSURE_KNEE 133 /* 66 ms / 15 fps */
MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
"Thomas Kaiser thomas@kaiser-linux.li");
@@ -439,12 +441,31 @@ static void setwhitebalance(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0xdc, 0x01);
}
+static u8 rgbbalance_ctrl_to_reg_value(s32 rgb_ctrl_val)
+{
+ const unsigned int k = 1000; /* precision factor */
+ unsigned int norm;
+
+ /* Normed value [0...k] */
+ norm = k * (rgb_ctrl_val - PAC7302_RGB_BALANCE_MIN)
+ / (PAC7302_RGB_BALANCE_MAX - PAC7302_RGB_BALANCE_MIN);
+ /* Qudratic apporach improves control at small (register) values: */
+ return 64 * norm * norm / (k*k) + 32 * norm / k + 32;
+ /* Y = 64*X*X + 32*X + 32
+ * => register values 0x20-0x80; Windows driver uses these limits */
+
+ /* NOTE: for full value range (0x00-0xff) use
+ * Y = 254*X*X + X
+ * => 254 * norm * norm / (k*k) + 1 * norm / k */
+}
+
static void setredbalance(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
- reg_w(gspca_dev, 0xc5, sd->red_balance->val);
+ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ reg_w(gspca_dev, 0x01,
+ rgbbalance_ctrl_to_reg_value(sd->red_balance->val));
reg_w(gspca_dev, 0xdc, 0x01);
}
@@ -454,7 +475,8 @@ static void setbluebalance(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
- reg_w(gspca_dev, 0xc7, sd->blue_balance->val);
+ reg_w(gspca_dev, 0x03,
+ rgbbalance_ctrl_to_reg_value(sd->blue_balance->val));
reg_w(gspca_dev, 0xdc, 0x01);
}
@@ -643,9 +665,15 @@ static int sd_init_controls(struct gspca_dev *gspca_dev)
V4L2_CID_WHITE_BALANCE_TEMPERATURE,
0, 255, 1, 55);
sd->red_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_RED_BALANCE, 0, 3, 1, 1);
+ V4L2_CID_RED_BALANCE,
+ PAC7302_RGB_BALANCE_MIN,
+ PAC7302_RGB_BALANCE_MAX,
+ 1, PAC7302_RGB_BALANCE_DEFAULT);
sd->blue_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_BLUE_BALANCE, 0, 3, 1, 1);
+ V4L2_CID_BLUE_BALANCE,
+ PAC7302_RGB_BALANCE_MIN,
+ PAC7302_RGB_BALANCE_MAX,
+ 1, PAC7302_RGB_BALANCE_DEFAULT);
gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
diff --git a/drivers/media/usb/gspca/sonixb.c b/drivers/media/usb/gspca/sonixb.c
index fd1f8d2d3b0..70511d5f953 100644
--- a/drivers/media/usb/gspca/sonixb.c
+++ b/drivers/media/usb/gspca/sonixb.c
@@ -1449,6 +1449,7 @@ static const struct usb_device_id device_table[] = {
{USB_DEVICE(0x0c45, 0x6024), SB(TAS5130CXX, 102)},
{USB_DEVICE(0x0c45, 0x6025), SB(TAS5130CXX, 102)},
#endif
+ {USB_DEVICE(0x0c45, 0x6027), SB(OV7630, 101)}, /* Genius Eye 310 */
{USB_DEVICE(0x0c45, 0x6028), SB(PAS202, 102)},
{USB_DEVICE(0x0c45, 0x6029), SB(PAS106, 102)},
{USB_DEVICE(0x0c45, 0x602a), SB(HV7131D, 102)},
diff --git a/drivers/media/usb/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c
index 304f43ef59e..84dc26fe80e 100644
--- a/drivers/media/usb/hdpvr/hdpvr-core.c
+++ b/drivers/media/usb/hdpvr/hdpvr-core.c
@@ -401,12 +401,14 @@ static int hdpvr_probe(struct usb_interface *interface,
client = hdpvr_register_ir_rx_i2c(dev);
if (!client) {
v4l2_err(&dev->v4l2_dev, "i2c IR RX device register failed\n");
+ retval = -ENODEV;
goto reg_fail;
}
client = hdpvr_register_ir_tx_i2c(dev);
if (!client) {
v4l2_err(&dev->v4l2_dev, "i2c IR TX device register failed\n");
+ retval = -ENODEV;
goto reg_fail;
}
#endif
diff --git a/drivers/media/usb/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c
index 82e819fa91c..031cf024304 100644
--- a/drivers/media/usb/hdpvr/hdpvr-i2c.c
+++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c
@@ -55,7 +55,7 @@ struct i2c_client *hdpvr_register_ir_rx_i2c(struct hdpvr_device *dev)
/* Our default information for ir-kbd-i2c.c to use */
init_data->ir_codes = RC_MAP_HAUPPAUGE;
init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
- init_data->type = RC_TYPE_RC5;
+ init_data->type = RC_BIT_RC5;
init_data->name = "HD-PVR";
init_data->polling_interval = 405; /* ms, duplicated from Windows */
hdpvr_ir_rx_i2c_board_info.platform_data = init_data;
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
index fb828ba1dbb..299751a8b06 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
@@ -3563,9 +3563,9 @@ void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *hdw,
enum pvr2_v4l_type index,int v)
{
switch (index) {
- case pvr2_v4l_type_video: hdw->v4l_minor_number_video = v;
- case pvr2_v4l_type_vbi: hdw->v4l_minor_number_vbi = v;
- case pvr2_v4l_type_radio: hdw->v4l_minor_number_radio = v;
+ case pvr2_v4l_type_video: hdw->v4l_minor_number_video = v;break;
+ case pvr2_v4l_type_vbi: hdw->v4l_minor_number_vbi = v;break;
+ case pvr2_v4l_type_radio: hdw->v4l_minor_number_radio = v;break;
default: break;
}
}
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c
index 885ce11f222..9ab596c78a4 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c
@@ -581,7 +581,7 @@ static void pvr2_i2c_register_ir(struct pvr2_hdw *hdw)
case PVR2_IR_SCHEME_29XXX: /* Original 29xxx device */
init_data->ir_codes = RC_MAP_HAUPPAUGE;
init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP;
- init_data->type = RC_TYPE_RC5;
+ init_data->type = RC_BIT_RC5;
init_data->name = hdw->hdw_desc->description;
init_data->polling_interval = 100; /* ms From ir-kbd-i2c */
/* IR Receiver */
@@ -596,7 +596,7 @@ static void pvr2_i2c_register_ir(struct pvr2_hdw *hdw)
case PVR2_IR_SCHEME_24XXX_MCE: /* 24xxx MCE device */
init_data->ir_codes = RC_MAP_HAUPPAUGE;
init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
- init_data->type = RC_TYPE_RC5;
+ init_data->type = RC_BIT_RC5;
init_data->name = hdw->hdw_desc->description;
/* IR Receiver */
info.addr = 0x71;
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
index db249cad3cd..6930676051e 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
@@ -196,7 +196,7 @@ static int pvr2_g_std(struct file *file, void *priv, v4l2_std_id *std)
return ret;
}
-int pvr2_s_std(struct file *file, void *priv, v4l2_std_id *std)
+static int pvr2_s_std(struct file *file, void *priv, v4l2_std_id *std)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
@@ -365,7 +365,7 @@ static int pvr2_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
vt->audmode);
}
-int pvr2_s_frequency(struct file *file, void *priv, struct v4l2_frequency *vf)
+static int pvr2_s_frequency(struct file *file, void *priv, struct v4l2_frequency *vf)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
diff --git a/drivers/media/usb/pwc/pwc-ctrl.c b/drivers/media/usb/pwc/pwc-ctrl.c
index 1f506fde97d..3a1618580ed 100644
--- a/drivers/media/usb/pwc/pwc-ctrl.c
+++ b/drivers/media/usb/pwc/pwc-ctrl.c
@@ -179,6 +179,8 @@ static int set_video_mode_Nala(struct pwc_device *pdev, int size, int pixfmt,
return -EINVAL;
if (frames < 4)
frames = 4;
+ else if (size > PSZ_QCIF && frames > 15)
+ frames = 15;
else if (frames > 25)
frames = 25;
frames = frames2frames[frames];
diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c
index 42e36bac4d7..5210239cbae 100644
--- a/drivers/media/usb/pwc/pwc-if.c
+++ b/drivers/media/usb/pwc/pwc-if.c
@@ -155,7 +155,7 @@ static struct video_device pwc_template = {
/***************************************************************************/
/* Private functions */
-struct pwc_frame_buf *pwc_get_next_fill_buf(struct pwc_device *pdev)
+static struct pwc_frame_buf *pwc_get_next_fill_buf(struct pwc_device *pdev)
{
unsigned long flags = 0;
struct pwc_frame_buf *buf = NULL;
@@ -1000,7 +1000,11 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
pdev->vb_queue.buf_struct_size = sizeof(struct pwc_frame_buf);
pdev->vb_queue.ops = &pwc_vb_queue_ops;
pdev->vb_queue.mem_ops = &vb2_vmalloc_memops;
- vb2_queue_init(&pdev->vb_queue);
+ rc = vb2_queue_init(&pdev->vb_queue);
+ if (rc < 0) {
+ PWC_ERROR("Oops, could not initialize vb2 queue.\n");
+ goto err_free_mem;
+ }
/* Init video_device structure */
memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template));
diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c
index 2191f6ddf9e..8ebec0d7bf5 100644
--- a/drivers/media/usb/s2255/s2255drv.c
+++ b/drivers/media/usb/s2255/s2255drv.c
@@ -1651,7 +1651,7 @@ static int vidioc_enum_frameintervals(struct file *file, void *priv,
int is_ntsc = 0;
#define NUM_FRAME_ENUMS 4
int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5};
- if (fe->index < 0 || fe->index >= NUM_FRAME_ENUMS)
+ if (fe->index >= NUM_FRAME_ENUMS)
return -EINVAL;
switch (fe->width) {
case 640:
diff --git a/drivers/media/usb/siano/Kconfig b/drivers/media/usb/siano/Kconfig
index 3c76e62d820..5afbd9a4b55 100644
--- a/drivers/media/usb/siano/Kconfig
+++ b/drivers/media/usb/siano/Kconfig
@@ -4,7 +4,8 @@
config SMS_USB_DRV
tristate "Siano SMS1xxx based MDTV receiver"
- depends on DVB_CORE && RC_CORE && HAS_DMA
+ depends on DVB_CORE && HAS_DMA
+ select MEDIA_COMMON_OPTIONS
---help---
Choose if you would like to have Siano's support for USB interface
diff --git a/drivers/media/usb/sn9c102/sn9c102_core.c b/drivers/media/usb/sn9c102/sn9c102_core.c
index 5bfc8e2f018..73605864fff 100644
--- a/drivers/media/usb/sn9c102/sn9c102_core.c
+++ b/drivers/media/usb/sn9c102/sn9c102_core.c
@@ -2481,11 +2481,13 @@ sn9c102_vidioc_enum_framesizes(struct sn9c102_device* cam, void __user * arg)
if (frmsize.pixel_format != V4L2_PIX_FMT_SN9C10X &&
frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8)
return -EINVAL;
+ break;
case BRIDGE_SN9C105:
case BRIDGE_SN9C120:
if (frmsize.pixel_format != V4L2_PIX_FMT_JPEG &&
frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8)
return -EINVAL;
+ break;
}
frmsize.type = V4L2_FRMSIZE_TYPE_STEPWISE;
diff --git a/drivers/media/usb/stk1160/stk1160-i2c.c b/drivers/media/usb/stk1160/stk1160-i2c.c
index 176ac937306..850cf285ada 100644
--- a/drivers/media/usb/stk1160/stk1160-i2c.c
+++ b/drivers/media/usb/stk1160/stk1160-i2c.c
@@ -116,7 +116,7 @@ static int stk1160_i2c_read_reg(struct stk1160 *dev, u8 addr,
if (rc < 0)
return rc;
- stk1160_read_reg(dev, STK1160_SBUSR_RD, value);
+ rc = stk1160_read_reg(dev, STK1160_SBUSR_RD, value);
if (rc < 0)
return rc;
diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c
index 8bdfb027531..fa3671de02a 100644
--- a/drivers/media/usb/stk1160/stk1160-video.c
+++ b/drivers/media/usb/stk1160/stk1160-video.c
@@ -475,7 +475,11 @@ int stk1160_alloc_isoc(struct stk1160 *dev)
if (!dev->isoc_ctl.transfer_buffer[i]) {
stk1160_err("cannot alloc %d bytes for tx[%d] buffer\n",
sb_size, i);
- goto free_i_bufs;
+
+ /* Not enough transfer buffers, so just give up */
+ if (i < STK1160_MIN_BUFS)
+ goto free_i_bufs;
+ goto nomore_tx_bufs;
}
memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
@@ -506,13 +510,28 @@ int stk1160_alloc_isoc(struct stk1160 *dev)
}
}
- stk1160_dbg("urbs allocated\n");
+ stk1160_dbg("%d urbs allocated\n", num_bufs);
/* At last we can say we have some buffers */
dev->isoc_ctl.num_bufs = num_bufs;
return 0;
+nomore_tx_bufs:
+ /*
+ * Failed to allocate desired buffer count. However, we may have
+ * enough to work fine, so we just free the extra urb,
+ * store the allocated count and keep going, fingers crossed!
+ */
+ usb_free_urb(dev->isoc_ctl.urb[i]);
+ dev->isoc_ctl.urb[i] = NULL;
+
+ stk1160_warn("%d urbs allocated. Trying to continue...\n", i - 1);
+
+ dev->isoc_ctl.num_bufs = i - 1;
+
+ return 0;
+
free_i_bufs:
/* Save the allocated buffers so far, so we can properly free them */
dev->isoc_ctl.num_bufs = i+1;
diff --git a/drivers/media/usb/stk1160/stk1160.h b/drivers/media/usb/stk1160/stk1160.h
index 68c8707d36a..05b05b160e1 100644
--- a/drivers/media/usb/stk1160/stk1160.h
+++ b/drivers/media/usb/stk1160/stk1160.h
@@ -30,11 +30,12 @@
#define STK1160_VERSION "0.9.5"
#define STK1160_VERSION_NUM 0x000905
-/* TODO: Decide on number of packets for each buffer */
+/* Decide on number of packets for each buffer */
#define STK1160_NUM_PACKETS 64
/* Number of buffers for isoc transfers */
-#define STK1160_NUM_BUFS 16 /* TODO */
+#define STK1160_NUM_BUFS 16
+#define STK1160_MIN_BUFS 1
/* TODO: This endpoint address should be retrieved */
#define STK1160_EP_VIDEO 0x82
diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c
index 86a0fc56c33..5d3c032d733 100644
--- a/drivers/media/usb/stkwebcam/stk-webcam.c
+++ b/drivers/media/usb/stkwebcam/stk-webcam.c
@@ -54,10 +54,6 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jaime Velasco Juan <jsagarribay@gmail.com> and Nicolas VIVIEN");
MODULE_DESCRIPTION("Syntek DC1125 webcam driver");
-
-/* bool for webcam LED management */
-int first_init = 1;
-
/* Some cameras have audio interfaces, we aren't interested in those */
static struct usb_device_id stkwebcam_table[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(0x174f, 0xa311, 0xff, 0xff, 0xff) },
@@ -554,6 +550,7 @@ static void stk_free_buffers(struct stk_camera *dev)
static int v4l_stk_open(struct file *fp)
{
+ static int first_init = 1; /* webcam LED management */
struct stk_camera *dev;
struct video_device *vdev;
diff --git a/drivers/media/usb/tlg2300/pd-dvb.c b/drivers/media/usb/tlg2300/pd-dvb.c
index 30fcb117e89..ca4994a5190 100644
--- a/drivers/media/usb/tlg2300/pd-dvb.c
+++ b/drivers/media/usb/tlg2300/pd-dvb.c
@@ -1,6 +1,7 @@
#include "pd-common.h"
#include <linux/kernel.h>
#include <linux/usb.h>
+#include <linux/time.h>
#include <linux/dvb/dmx.h>
#include <linux/delay.h>
#include <linux/gfp.h>
diff --git a/drivers/media/usb/tlg2300/pd-video.c b/drivers/media/usb/tlg2300/pd-video.c
index 1f448ac7a49..3082bfa9b2c 100644
--- a/drivers/media/usb/tlg2300/pd-video.c
+++ b/drivers/media/usb/tlg2300/pd-video.c
@@ -888,7 +888,7 @@ static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *in)
{
struct front_face *front = fh;
- if (in->index < 0 || in->index >= POSEIDON_INPUTS)
+ if (in->index >= POSEIDON_INPUTS)
return -EINVAL;
strcpy(in->name, pd_inputs[in->index].name);
in->type = V4L2_INPUT_TYPE_TUNER;
@@ -923,7 +923,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
struct poseidon *pd = front->pd;
s32 ret, cmd_status;
- if (i < 0 || i >= POSEIDON_INPUTS)
+ if (i >= POSEIDON_INPUTS)
return -EINVAL;
ret = send_set_req(pd, SGNL_SRC_SEL,
pd_inputs[i].tlg_src, &cmd_status);
diff --git a/drivers/media/usb/tm6000/tm6000-input.c b/drivers/media/usb/tm6000/tm6000-input.c
index dffbd4bd47b..8a6bbf1d80e 100644
--- a/drivers/media/usb/tm6000/tm6000-input.c
+++ b/drivers/media/usb/tm6000/tm6000-input.c
@@ -109,12 +109,12 @@ static int tm6000_ir_config(struct tm6000_IR *ir)
*/
switch (ir->rc_type) {
- case RC_TYPE_NEC:
+ case RC_BIT_NEC:
leader = 900; /* ms */
pulse = 700; /* ms - the actual value would be 562 */
break;
default:
- case RC_TYPE_RC5:
+ case RC_BIT_RC5:
leader = 900; /* ms - from the NEC decoding */
pulse = 1780; /* ms - The actual value would be 1776 */
break;
@@ -122,12 +122,12 @@ static int tm6000_ir_config(struct tm6000_IR *ir)
pulse = ir_clock_mhz * pulse;
leader = ir_clock_mhz * leader;
- if (ir->rc_type == RC_TYPE_NEC)
+ if (ir->rc_type == RC_BIT_NEC)
leader = leader | 0x8000;
dprintk(2, "%s: %s, %d MHz, leader = 0x%04x, pulse = 0x%06x \n",
__func__,
- (ir->rc_type == RC_TYPE_NEC) ? "NEC" : "RC-5",
+ (ir->rc_type == RC_BIT_NEC) ? "NEC" : "RC-5",
ir_clock_mhz, leader, pulse);
/* Remote WAKEUP = enable, normal mode, from IR decoder output */
@@ -297,7 +297,7 @@ static void tm6000_ir_stop(struct rc_dev *rc)
cancel_delayed_work_sync(&ir->work);
}
-static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 rc_type)
+static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 *rc_type)
{
struct tm6000_IR *ir = rc->priv;
@@ -306,10 +306,10 @@ static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 rc_type)
dprintk(2, "%s\n",__func__);
- if ((rc->rc_map.scan) && (rc_type == RC_TYPE_NEC))
+ if ((rc->rc_map.scan) && (*rc_type == RC_BIT_NEC))
ir->key_addr = ((rc->rc_map.scan[0].scancode >> 8) & 0xffff);
- ir->rc_type = rc_type;
+ ir->rc_type = *rc_type;
tm6000_ir_config(ir);
/* TODO */
@@ -398,6 +398,7 @@ int tm6000_ir_init(struct tm6000_core *dev)
struct tm6000_IR *ir;
struct rc_dev *rc;
int err = -ENOMEM;
+ u64 rc_type;
if (!enable_ir)
return -ENODEV;
@@ -421,7 +422,7 @@ int tm6000_ir_init(struct tm6000_core *dev)
ir->rc = rc;
/* input setup */
- rc->allowed_protos = RC_TYPE_RC5 | RC_TYPE_NEC;
+ rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC;
/* Neded, in order to support NEC remotes with 24 or 32 bits */
rc->scanmask = 0xffff;
rc->priv = ir;
@@ -444,7 +445,8 @@ int tm6000_ir_init(struct tm6000_core *dev)
usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
strlcat(ir->phys, "/input0", sizeof(ir->phys));
- tm6000_ir_change_protocol(rc, RC_TYPE_UNKNOWN);
+ rc_type = RC_BIT_UNKNOWN;
+ tm6000_ir_change_protocol(rc, &rc_type);
rc->input_name = ir->name;
rc->input_phys = ir->phys;
diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c
index 4342cd4f5c8..f656fd7a39a 100644
--- a/drivers/media/usb/tm6000/tm6000-video.c
+++ b/drivers/media/usb/tm6000/tm6000-video.c
@@ -1802,6 +1802,7 @@ int tm6000_v4l2_register(struct tm6000_core *dev)
if (!dev->radio_dev) {
printk(KERN_INFO "%s: can't register radio device\n",
dev->name);
+ ret = -ENXIO;
return ret; /* FIXME release resource */
}
diff --git a/drivers/media/usb/usbvision/usbvision.h b/drivers/media/usb/usbvision/usbvision.h
index 43cf61fe494..8a25876d72c 100644
--- a/drivers/media/usb/usbvision/usbvision.h
+++ b/drivers/media/usb/usbvision/usbvision.h
@@ -167,7 +167,7 @@ enum {
/* This macro restricts an int variable to an inclusive range */
#define RESTRICT_TO_RANGE(v, mi, ma) \
- { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); }
+ { if (((int)v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); }
/*
* We use macros to do YUV -> RGB conversion because this is
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index f7061a5ef1d..516a5b188ea 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -927,7 +927,7 @@ static int __uvc_ctrl_get(struct uvc_video_chain *chain,
int ret;
if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
- return -EINVAL;
+ return -EACCES;
if (!ctrl->loaded) {
ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id,
@@ -1061,7 +1061,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping);
if (ctrl == NULL) {
- ret = -EINVAL;
+ ret = -ENOENT;
goto done;
}
@@ -1099,12 +1099,13 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
return -ERESTARTSYS;
ctrl = uvc_find_control(chain, query_menu->id, &mapping);
- if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU) {
- ret = -EINVAL;
+ if (ctrl == NULL) {
+ ret = -ENOENT;
goto done;
}
- if (query_menu->index >= mapping->menu_count) {
+ if (mapping->v4l2_type != V4L2_CTRL_TYPE_MENU ||
+ query_menu->index >= mapping->menu_count) {
ret = -EINVAL;
goto done;
}
@@ -1263,7 +1264,7 @@ static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
ctrl = uvc_find_control(handle->chain, sev->id, &mapping);
if (ctrl == NULL) {
- ret = -EINVAL;
+ ret = -ENOENT;
goto done;
}
@@ -1414,7 +1415,7 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
ctrl = uvc_find_control(chain, xctrl->id, &mapping);
if (ctrl == NULL)
- return -EINVAL;
+ return -ENOENT;
return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value);
}
@@ -1431,8 +1432,10 @@ int uvc_ctrl_set(struct uvc_video_chain *chain,
int ret;
ctrl = uvc_find_control(chain, xctrl->id, &mapping);
- if (ctrl == NULL || (ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) == 0)
- return -EINVAL;
+ if (ctrl == NULL)
+ return -ENOENT;
+ if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR))
+ return -EACCES;
/* Clamp out of range values. */
switch (mapping->v4l2_type) {
@@ -1452,8 +1455,12 @@ int uvc_ctrl_set(struct uvc_video_chain *chain,
if (step == 0)
step = 1;
- xctrl->value = min + (xctrl->value - min + step/2) / step * step;
- xctrl->value = clamp(xctrl->value, min, max);
+ xctrl->value = min + ((u32)(xctrl->value - min) + step / 2)
+ / step * step;
+ if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED)
+ xctrl->value = clamp(xctrl->value, min, max);
+ else
+ xctrl->value = clamp_t(u32, xctrl->value, min, max);
value = xctrl->value;
break;
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index 5967081747c..5dbefa68b1d 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1562,6 +1562,9 @@ static int uvc_scan_device(struct uvc_device *dev)
INIT_LIST_HEAD(&chain->entities);
mutex_init(&chain->ctrl_mutex);
chain->dev = dev;
+ v4l2_prio_init(&chain->prio);
+
+ term->flags |= UVC_ENTITY_FLAG_DEFAULT;
if (uvc_scan_chain(chain, term) < 0) {
kfree(chain);
@@ -1722,6 +1725,8 @@ static int uvc_register_video(struct uvc_device *dev,
vdev->v4l2_dev = &dev->vdev;
vdev->fops = &uvc_fops;
vdev->release = uvc_release;
+ vdev->prio = &stream->chain->prio;
+ set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags);
if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
vdev->vfl_dir = VFL_DIR_TX;
strlcpy(vdev->name, dev->name, sizeof vdev->name);
@@ -1741,6 +1746,11 @@ static int uvc_register_video(struct uvc_device *dev,
return ret;
}
+ if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ stream->chain->caps |= V4L2_CAP_VIDEO_CAPTURE;
+ else
+ stream->chain->caps |= V4L2_CAP_VIDEO_OUTPUT;
+
atomic_inc(&dev->nstreams);
return 0;
}
diff --git a/drivers/media/usb/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c
index 29e239911d0..dc56a59ecad 100644
--- a/drivers/media/usb/uvc/uvc_entity.c
+++ b/drivers/media/usb/uvc/uvc_entity.c
@@ -93,6 +93,8 @@ static int uvc_mc_init_entity(struct uvc_entity *entity)
} else if (entity->vdev != NULL) {
ret = media_entity_init(&entity->vdev->entity,
entity->num_pads, entity->pads, 0);
+ if (entity->flags & UVC_ENTITY_FLAG_DEFAULT)
+ entity->vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT;
} else
ret = 0;
diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index 18a91fae6bc..778addc5caf 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -128,7 +128,7 @@ int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
int ret;
queue->queue.type = type;
- queue->queue.io_modes = VB2_MMAP | VB2_USERPTR;
+ queue->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
queue->queue.drv_priv = queue;
queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
queue->queue.ops = &uvc_queue_qops;
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index f00db3060e0..8e056046bc2 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -165,17 +165,18 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream,
fcc[0], fcc[1], fcc[2], fcc[3],
fmt->fmt.pix.width, fmt->fmt.pix.height);
- /* Check if the hardware supports the requested format. */
+ /* Check if the hardware supports the requested format, use the default
+ * format otherwise.
+ */
for (i = 0; i < stream->nformats; ++i) {
format = &stream->format[i];
if (format->fcc == fmt->fmt.pix.pixelformat)
break;
}
- if (format == NULL || format->fcc != fmt->fmt.pix.pixelformat) {
- uvc_trace(UVC_TRACE_FORMAT, "Unsupported format 0x%08x.\n",
- fmt->fmt.pix.pixelformat);
- return -EINVAL;
+ if (i == stream->nformats) {
+ format = stream->def_format;
+ fmt->fmt.pix.pixelformat = format->fcc;
}
/* Find the closest image size. The distance between image sizes is
@@ -564,15 +565,30 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
usb_make_path(stream->dev->udev,
cap->bus_info, sizeof(cap->bus_info));
cap->version = LINUX_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
+ | chain->caps;
if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
- | V4L2_CAP_STREAMING;
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE
+ | V4L2_CAP_STREAMING;
else
- cap->capabilities = V4L2_CAP_VIDEO_OUTPUT
- | V4L2_CAP_STREAMING;
+ cap->device_caps = V4L2_CAP_VIDEO_OUTPUT
+ | V4L2_CAP_STREAMING;
break;
}
+ /* Priority */
+ case VIDIOC_G_PRIORITY:
+ *(u32 *)arg = v4l2_prio_max(vdev->prio);
+ break;
+
+ case VIDIOC_S_PRIORITY:
+ ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
+ if (ret < 0)
+ return ret;
+
+ return v4l2_prio_change(vdev->prio, &handle->vfh.prio,
+ *(u32 *)arg);
+
/* Get, Set & Query control */
case VIDIOC_QUERYCTRL:
return uvc_query_v4l2_ctrl(chain, arg);
@@ -591,8 +607,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
ret = uvc_ctrl_get(chain, &xctrl);
uvc_ctrl_rollback(handle);
- if (ret >= 0)
- ctrl->value = xctrl.value;
+ if (ret < 0)
+ return ret == -ENOENT ? -EINVAL : ret;
+
+ ctrl->value = xctrl.value;
break;
}
@@ -601,6 +619,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
struct v4l2_control *ctrl = arg;
struct v4l2_ext_control xctrl;
+ ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
+ if (ret < 0)
+ return ret;
+
memset(&xctrl, 0, sizeof xctrl);
xctrl.id = ctrl->id;
xctrl.value = ctrl->value;
@@ -612,7 +634,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
ret = uvc_ctrl_set(chain, &xctrl);
if (ret < 0) {
uvc_ctrl_rollback(handle);
- return ret;
+ return ret == -ENOENT ? -EINVAL : ret;
}
ret = uvc_ctrl_commit(handle, &xctrl, 1);
if (ret == 0)
@@ -637,8 +659,9 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
ret = uvc_ctrl_get(chain, ctrl);
if (ret < 0) {
uvc_ctrl_rollback(handle);
- ctrls->error_idx = i;
- return ret;
+ ctrls->error_idx = ret == -ENOENT
+ ? ctrls->count : i;
+ return ret == -ENOENT ? -EINVAL : ret;
}
}
ctrls->error_idx = 0;
@@ -647,6 +670,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
}
case VIDIOC_S_EXT_CTRLS:
+ ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
+ if (ret < 0)
+ return ret;
+ /* Fall through */
case VIDIOC_TRY_EXT_CTRLS:
{
struct v4l2_ext_controls *ctrls = arg;
@@ -661,8 +688,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
ret = uvc_ctrl_set(chain, ctrl);
if (ret < 0) {
uvc_ctrl_rollback(handle);
- ctrls->error_idx = i;
- return ret;
+ ctrls->error_idx = (ret == -ENOENT &&
+ cmd == VIDIOC_S_EXT_CTRLS)
+ ? ctrls->count : i;
+ return ret == -ENOENT ? -EINVAL : ret;
}
}
@@ -739,6 +768,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
u32 input = *(u32 *)arg + 1;
+ ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
+ if (ret < 0)
+ return ret;
+
if ((ret = uvc_acquire_privileges(handle)) < 0)
return ret;
@@ -792,6 +825,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
}
case VIDIOC_S_FMT:
+ ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
+ if (ret < 0)
+ return ret;
+
if ((ret = uvc_acquire_privileges(handle)) < 0)
return ret;
@@ -894,6 +931,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
return uvc_v4l2_get_streamparm(stream, arg);
case VIDIOC_S_PARM:
+ ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
+ if (ret < 0)
+ return ret;
+
if ((ret = uvc_acquire_privileges(handle)) < 0)
return ret;
@@ -924,10 +965,14 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
case VIDIOC_G_CROP:
case VIDIOC_S_CROP:
- return -EINVAL;
+ return -ENOTTY;
/* Buffers & streaming */
case VIDIOC_REQBUFS:
+ ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
+ if (ret < 0)
+ return ret;
+
if ((ret = uvc_acquire_privileges(handle)) < 0)
return ret;
@@ -973,6 +1018,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (*type != stream->type)
return -EINVAL;
+ ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
+ if (ret < 0)
+ return ret;
+
if (!uvc_has_privileges(handle))
return -EBUSY;
@@ -991,6 +1040,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (*type != stream->type)
return -EINVAL;
+ ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
+ if (ret < 0)
+ return ret;
+
if (!uvc_has_privileges(handle))
return -EBUSY;
@@ -1030,7 +1083,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
case VIDIOC_ENUMOUTPUT:
uvc_trace(UVC_TRACE_IOCTL, "Unsupported ioctl 0x%08x\n", cmd);
- return -EINVAL;
+ return -ENOTTY;
case UVCIOC_CTRL_MAP:
return uvc_ioctl_ctrl_map(chain, arg);
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 57c3076a462..3394c343201 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -1812,6 +1812,7 @@ int uvc_video_init(struct uvc_streaming *stream)
probe->bFormatIndex = format->index;
probe->bFrameIndex = frame->bFrameIndex;
+ stream->def_format = format;
stream->cur_format = format;
stream->cur_frame = frame;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index af216ec45e3..af505fdd9b3 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -225,10 +225,14 @@ struct uvc_format_desc {
* always be accessed with the UVC_ENTITY_* macros and never directly.
*/
+#define UVC_ENTITY_FLAG_DEFAULT (1 << 0)
+
struct uvc_entity {
struct list_head list; /* Entity as part of a UVC device. */
struct list_head chain; /* Entity as part of a video device
* chain. */
+ unsigned int flags;
+
__u8 id;
__u16 type;
char name[64];
@@ -371,6 +375,9 @@ struct uvc_video_chain {
struct uvc_entity *selector; /* Selector unit */
struct mutex ctrl_mutex; /* Protects ctrl.info */
+
+ struct v4l2_prio_state prio; /* V4L2 priority state */
+ u32 caps; /* V4L2 chain-wide caps */
};
struct uvc_stats_frame {
@@ -436,6 +443,7 @@ struct uvc_streaming {
struct uvc_format *format;
struct uvc_streaming_control ctrl;
+ struct uvc_format *def_format;
struct uvc_format *cur_format;
struct uvc_frame *cur_frame;
/* Protect access to ctrl, cur_format, cur_frame and hardware video
diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c
index 9afab35878b..39edd444293 100644
--- a/drivers/media/usb/zr364xx/zr364xx.c
+++ b/drivers/media/usb/zr364xx/zr364xx.c
@@ -1007,8 +1007,7 @@ static void read_pipe_completion(struct urb *purb)
return;
}
- if (purb->actual_length < 0 ||
- purb->actual_length > pipe_info->transfer_size) {
+ if (purb->actual_length > pipe_info->transfer_size) {
dev_err(&cam->udev->dev, "wrong number of bytes\n");
return;
}
diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
index 0c54e19d994..65875c3aba1 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -59,6 +59,7 @@ config VIDEOBUF_DVB
# Used by drivers that need Videobuf2 modules
config VIDEOBUF2_CORE
+ select DMA_SHARED_BUFFER
tristate
config VIDEOBUF2_MEMOPS
@@ -68,11 +69,13 @@ config VIDEOBUF2_DMA_CONTIG
tristate
select VIDEOBUF2_CORE
select VIDEOBUF2_MEMOPS
+ select DMA_SHARED_BUFFER
config VIDEOBUF2_VMALLOC
tristate
select VIDEOBUF2_CORE
select VIDEOBUF2_MEMOPS
+ select DMA_SHARED_BUFFER
config VIDEOBUF2_DMA_SG
tristate
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index f995dd31151..380ddd89fa4 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -837,7 +837,7 @@ bool v4l2_detect_gtf(unsigned frame_height,
struct v4l2_dv_timings *fmt)
{
int pix_clk;
- int v_fp, v_bp, h_fp, h_bp, hsync;
+ int v_fp, v_bp, h_fp, hsync;
int frame_width, image_height, image_width;
bool default_gtf;
int h_blank;
@@ -885,7 +885,6 @@ bool v4l2_detect_gtf(unsigned frame_height,
hsync = hsync - hsync % GTF_CELL_GRAN;
h_fp = h_blank / 2 - hsync;
- h_bp = h_blank / 2;
fmt->bt.polarities = polarities;
fmt->bt.width = image_width;
diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index 83ffb6436ba..7157af301b1 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -297,6 +297,7 @@ struct v4l2_plane32 {
union {
__u32 mem_offset;
compat_long_t userptr;
+ __s32 fd;
} m;
__u32 data_offset;
__u32 reserved[11];
@@ -318,6 +319,7 @@ struct v4l2_buffer32 {
__u32 offset;
compat_long_t userptr;
compat_caddr_t planes;
+ __s32 fd;
} m;
__u32 length;
__u32 reserved2;
@@ -341,6 +343,9 @@ static int get_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
up_pln = compat_ptr(p);
if (put_user((unsigned long)up_pln, &up->m.userptr))
return -EFAULT;
+ } else if (memory == V4L2_MEMORY_DMABUF) {
+ if (copy_in_user(&up->m.fd, &up32->m.fd, sizeof(int)))
+ return -EFAULT;
} else {
if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset,
sizeof(__u32)))
@@ -364,6 +369,11 @@ static int put_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
if (copy_in_user(&up32->m.mem_offset, &up->m.mem_offset,
sizeof(__u32)))
return -EFAULT;
+ /* For DMABUF, driver might've set up the fd, so copy it back. */
+ if (memory == V4L2_MEMORY_DMABUF)
+ if (copy_in_user(&up32->m.fd, &up->m.fd,
+ sizeof(int)))
+ return -EFAULT;
return 0;
}
@@ -446,6 +456,10 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
if (get_user(kp->m.offset, &up->m.offset))
return -EFAULT;
break;
+ case V4L2_MEMORY_DMABUF:
+ if (get_user(kp->m.fd, &up->m.fd))
+ return -EFAULT;
+ break;
}
}
@@ -510,6 +524,10 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
if (put_user(kp->m.offset, &up->m.offset))
return -EFAULT;
break;
+ case V4L2_MEMORY_DMABUF:
+ if (put_user(kp->m.fd, &up->m.fd))
+ return -EFAULT;
+ break;
}
}
@@ -1000,6 +1018,7 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
case VIDIOC_S_FBUF32:
case VIDIOC_OVERLAY32:
case VIDIOC_QBUF32:
+ case VIDIOC_EXPBUF:
case VIDIOC_DQBUF32:
case VIDIOC_STREAMON32:
case VIDIOC_STREAMOFF32:
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index a2df842e510..98dcad9c8a3 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -571,6 +571,7 @@ static void determine_valid_ioctls(struct video_device *vdev)
SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs);
SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf);
SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf);
+ SET_VALID_IOCTL(ops, VIDIOC_EXPBUF, vidioc_expbuf);
SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf);
SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon);
SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff);
diff --git a/drivers/media/v4l2-core/v4l2-event.c b/drivers/media/v4l2-core/v4l2-event.c
index 18a040b935a..c7200921815 100644
--- a/drivers/media/v4l2-core/v4l2-event.c
+++ b/drivers/media/v4l2-core/v4l2-event.c
@@ -5,7 +5,7 @@
*
* Copyright (C) 2009--2010 Nokia Corporation.
*
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/drivers/media/v4l2-core/v4l2-fh.c b/drivers/media/v4l2-core/v4l2-fh.c
index 9e3fc040ea2..e57c002b415 100644
--- a/drivers/media/v4l2-core/v4l2-fh.c
+++ b/drivers/media/v4l2-core/v4l2-fh.c
@@ -5,7 +5,7 @@
*
* Copyright (C) 2009--2010 Nokia Corporation.
*
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ * Contact: Sakari Ailus <sakari.ailus@iki.fi>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 8f388ff31eb..aa6e7c788db 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -155,6 +155,7 @@ static const char *v4l2_memory_names[] = {
[V4L2_MEMORY_MMAP] = "mmap",
[V4L2_MEMORY_USERPTR] = "userptr",
[V4L2_MEMORY_OVERLAY] = "overlay",
+ [V4L2_MEMORY_DMABUF] = "dmabuf",
};
#define prt_names(a, arr) (((unsigned)(a)) < ARRAY_SIZE(arr) ? arr[a] : "unknown")
@@ -453,6 +454,15 @@ static void v4l_print_buffer(const void *arg, bool write_only)
tc->type, tc->flags, tc->frames, *(__u32 *)tc->userbits);
}
+static void v4l_print_exportbuffer(const void *arg, bool write_only)
+{
+ const struct v4l2_exportbuffer *p = arg;
+
+ pr_cont("fd=%d, type=%s, index=%u, plane=%u, flags=0x%08x\n",
+ p->fd, prt_names(p->type, v4l2_type_names),
+ p->index, p->plane, p->flags);
+}
+
static void v4l_print_create_buffers(const void *arg, bool write_only)
{
const struct v4l2_create_buffers *p = arg;
@@ -1960,6 +1970,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = {
IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO),
IOCTL_INFO_FNC(VIDIOC_OVERLAY, v4l_overlay, v4l_print_u32, INFO_FL_PRIO),
IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE),
+ IOCTL_INFO_STD(VIDIOC_EXPBUF, vidioc_expbuf, v4l_print_exportbuffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_exportbuffer, flags)),
IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE),
IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
index 3ac83583ad7..438ea45d107 100644
--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
+++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
@@ -369,6 +369,19 @@ int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf);
/**
+ * v4l2_m2m_expbuf() - export a source or destination buffer, depending on
+ * the type
+ */
+int v4l2_m2m_expbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+ struct v4l2_exportbuffer *eb)
+{
+ struct vb2_queue *vq;
+
+ vq = v4l2_m2m_get_vq(m2m_ctx, eb->type);
+ return vb2_expbuf(vq, eb);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_expbuf);
+/**
* v4l2_m2m_streamon() - turn on streaming for a video queue
*/
int v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
@@ -510,12 +523,10 @@ struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops)
{
struct v4l2_m2m_dev *m2m_dev;
- if (!m2m_ops)
+ if (!m2m_ops || WARN_ON(!m2m_ops->device_run) ||
+ WARN_ON(!m2m_ops->job_abort))
return ERR_PTR(-EINVAL);
- BUG_ON(!m2m_ops->device_run);
- BUG_ON(!m2m_ops->job_abort);
-
m2m_dev = kzalloc(sizeof *m2m_dev, GFP_KERNEL);
if (!m2m_dev)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index dced41c1d99..996c248dea4 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -412,20 +412,20 @@ static int
v4l2_subdev_link_validate_get_format(struct media_pad *pad,
struct v4l2_subdev_format *fmt)
{
- switch (media_entity_type(pad->entity)) {
- case MEDIA_ENT_T_V4L2_SUBDEV:
+ if (media_entity_type(pad->entity) == MEDIA_ENT_T_V4L2_SUBDEV) {
+ struct v4l2_subdev *sd =
+ media_entity_to_v4l2_subdev(pad->entity);
+
fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
fmt->pad = pad->index;
- return v4l2_subdev_call(media_entity_to_v4l2_subdev(
- pad->entity),
- pad, get_fmt, NULL, fmt);
- default:
- WARN(1, "Driver bug! Wrong media entity type %d, entity %s\n",
- media_entity_type(pad->entity), pad->entity->name);
- /* Fall through */
- case MEDIA_ENT_T_DEVNODE_V4L:
- return -EINVAL;
+ return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
}
+
+ WARN(pad->entity->type != MEDIA_ENT_T_DEVNODE_V4L,
+ "Driver bug! Wrong media entity type 0x%08x, entity %s\n",
+ pad->entity->type, pad->entity->name);
+
+ return -EINVAL;
}
int v4l2_subdev_link_validate(struct media_link *link)
diff --git a/drivers/media/v4l2-core/videobuf-core.c b/drivers/media/v4l2-core/videobuf-core.c
index bf7a326b1cd..5449e8aa984 100644
--- a/drivers/media/v4l2-core/videobuf-core.c
+++ b/drivers/media/v4l2-core/videobuf-core.c
@@ -335,6 +335,9 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b,
case V4L2_MEMORY_OVERLAY:
b->m.offset = vb->boff;
break;
+ case V4L2_MEMORY_DMABUF:
+ /* DMABUF is not handled in videobuf framework */
+ break;
}
b->flags = 0;
@@ -405,6 +408,7 @@ int __videobuf_mmap_setup(struct videobuf_queue *q,
break;
case V4L2_MEMORY_USERPTR:
case V4L2_MEMORY_OVERLAY:
+ case V4L2_MEMORY_DMABUF:
/* nothing */
break;
}
diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index 432df119af2..9f81be23a81 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -109,6 +109,36 @@ static void __vb2_buf_userptr_put(struct vb2_buffer *vb)
}
/**
+ * __vb2_plane_dmabuf_put() - release memory associated with
+ * a DMABUF shared plane
+ */
+static void __vb2_plane_dmabuf_put(struct vb2_queue *q, struct vb2_plane *p)
+{
+ if (!p->mem_priv)
+ return;
+
+ if (p->dbuf_mapped)
+ call_memop(q, unmap_dmabuf, p->mem_priv);
+
+ call_memop(q, detach_dmabuf, p->mem_priv);
+ dma_buf_put(p->dbuf);
+ memset(p, 0, sizeof(*p));
+}
+
+/**
+ * __vb2_buf_dmabuf_put() - release memory associated with
+ * a DMABUF shared buffer
+ */
+static void __vb2_buf_dmabuf_put(struct vb2_buffer *vb)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ unsigned int plane;
+
+ for (plane = 0; plane < vb->num_planes; ++plane)
+ __vb2_plane_dmabuf_put(q, &vb->planes[plane]);
+}
+
+/**
* __setup_offsets() - setup unique offsets ("cookies") for every plane in
* every buffer on the queue
*/
@@ -230,6 +260,8 @@ static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers)
/* Free MMAP buffers or release USERPTR buffers */
if (q->memory == V4L2_MEMORY_MMAP)
__vb2_buf_mem_free(vb);
+ else if (q->memory == V4L2_MEMORY_DMABUF)
+ __vb2_buf_dmabuf_put(vb);
else
__vb2_buf_userptr_put(vb);
}
@@ -362,6 +394,8 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
b->m.offset = vb->v4l2_planes[0].m.mem_offset;
else if (q->memory == V4L2_MEMORY_USERPTR)
b->m.userptr = vb->v4l2_planes[0].m.userptr;
+ else if (q->memory == V4L2_MEMORY_DMABUF)
+ b->m.fd = vb->v4l2_planes[0].m.fd;
}
/*
@@ -454,13 +488,28 @@ static int __verify_mmap_ops(struct vb2_queue *q)
}
/**
+ * __verify_dmabuf_ops() - verify that all memory operations required for
+ * DMABUF queue type have been provided
+ */
+static int __verify_dmabuf_ops(struct vb2_queue *q)
+{
+ if (!(q->io_modes & VB2_DMABUF) || !q->mem_ops->attach_dmabuf ||
+ !q->mem_ops->detach_dmabuf || !q->mem_ops->map_dmabuf ||
+ !q->mem_ops->unmap_dmabuf)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
* __verify_memory_type() - Check whether the memory type and buffer type
* passed to a buffer operation are compatible with the queue.
*/
static int __verify_memory_type(struct vb2_queue *q,
enum v4l2_memory memory, enum v4l2_buf_type type)
{
- if (memory != V4L2_MEMORY_MMAP && memory != V4L2_MEMORY_USERPTR) {
+ if (memory != V4L2_MEMORY_MMAP && memory != V4L2_MEMORY_USERPTR &&
+ memory != V4L2_MEMORY_DMABUF) {
dprintk(1, "reqbufs: unsupported memory type\n");
return -EINVAL;
}
@@ -484,6 +533,11 @@ static int __verify_memory_type(struct vb2_queue *q,
return -EINVAL;
}
+ if (memory == V4L2_MEMORY_DMABUF && __verify_dmabuf_ops(q)) {
+ dprintk(1, "reqbufs: DMABUF for current setup unsupported\n");
+ return -EINVAL;
+ }
+
/*
* Place the busy tests at the end: -EBUSY can be ignored when
* create_bufs is called with count == 0, but count == 0 should still
@@ -790,6 +844,7 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
{
struct vb2_queue *q = vb->vb2_queue;
unsigned long flags;
+ unsigned int plane;
if (vb->state != VB2_BUF_STATE_ACTIVE)
return;
@@ -800,6 +855,10 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
dprintk(4, "Done processing on buffer %d, state: %d\n",
vb->v4l2_buf.index, vb->state);
+ /* sync buffers */
+ for (plane = 0; plane < vb->num_planes; ++plane)
+ call_memop(q, finish, vb->planes[plane].mem_priv);
+
/* Add the buffer to the done buffers list */
spin_lock_irqsave(&q->done_lock, flags);
vb->state = state;
@@ -845,6 +904,16 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b
b->m.planes[plane].length;
}
}
+ if (b->memory == V4L2_MEMORY_DMABUF) {
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ v4l2_planes[plane].m.fd =
+ b->m.planes[plane].m.fd;
+ v4l2_planes[plane].length =
+ b->m.planes[plane].length;
+ v4l2_planes[plane].data_offset =
+ b->m.planes[plane].data_offset;
+ }
+ }
} else {
/*
* Single-planar buffers do not use planes array,
@@ -859,6 +928,13 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b
v4l2_planes[0].m.userptr = b->m.userptr;
v4l2_planes[0].length = b->length;
}
+
+ if (b->memory == V4L2_MEMORY_DMABUF) {
+ v4l2_planes[0].m.fd = b->m.fd;
+ v4l2_planes[0].length = b->length;
+ v4l2_planes[0].data_offset = 0;
+ }
+
}
vb->v4l2_buf.field = b->field;
@@ -959,14 +1035,121 @@ static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b)
}
/**
+ * __qbuf_dmabuf() - handle qbuf of a DMABUF buffer
+ */
+static int __qbuf_dmabuf(struct vb2_buffer *vb, const struct v4l2_buffer *b)
+{
+ struct v4l2_plane planes[VIDEO_MAX_PLANES];
+ struct vb2_queue *q = vb->vb2_queue;
+ void *mem_priv;
+ unsigned int plane;
+ int ret;
+ int write = !V4L2_TYPE_IS_OUTPUT(q->type);
+
+ /* Verify and copy relevant information provided by the userspace */
+ __fill_vb2_buffer(vb, b, planes);
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ struct dma_buf *dbuf = dma_buf_get(planes[plane].m.fd);
+
+ if (IS_ERR_OR_NULL(dbuf)) {
+ dprintk(1, "qbuf: invalid dmabuf fd for plane %d\n",
+ plane);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* use DMABUF size if length is not provided */
+ if (planes[plane].length == 0)
+ planes[plane].length = dbuf->size;
+
+ if (planes[plane].length < planes[plane].data_offset +
+ q->plane_sizes[plane]) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Skip the plane if already verified */
+ if (dbuf == vb->planes[plane].dbuf &&
+ vb->v4l2_planes[plane].length == planes[plane].length) {
+ dma_buf_put(dbuf);
+ continue;
+ }
+
+ dprintk(1, "qbuf: buffer for plane %d changed\n", plane);
+
+ /* Release previously acquired memory if present */
+ __vb2_plane_dmabuf_put(q, &vb->planes[plane]);
+ memset(&vb->v4l2_planes[plane], 0, sizeof(struct v4l2_plane));
+
+ /* Acquire each plane's memory */
+ mem_priv = call_memop(q, attach_dmabuf, q->alloc_ctx[plane],
+ dbuf, planes[plane].length, write);
+ if (IS_ERR(mem_priv)) {
+ dprintk(1, "qbuf: failed to attach dmabuf\n");
+ ret = PTR_ERR(mem_priv);
+ dma_buf_put(dbuf);
+ goto err;
+ }
+
+ vb->planes[plane].dbuf = dbuf;
+ vb->planes[plane].mem_priv = mem_priv;
+ }
+
+ /* TODO: This pins the buffer(s) with dma_buf_map_attachment()).. but
+ * really we want to do this just before the DMA, not while queueing
+ * the buffer(s)..
+ */
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ ret = call_memop(q, map_dmabuf, vb->planes[plane].mem_priv);
+ if (ret) {
+ dprintk(1, "qbuf: failed to map dmabuf for plane %d\n",
+ plane);
+ goto err;
+ }
+ vb->planes[plane].dbuf_mapped = 1;
+ }
+
+ /*
+ * Call driver-specific initialization on the newly acquired buffer,
+ * if provided.
+ */
+ ret = call_qop(q, buf_init, vb);
+ if (ret) {
+ dprintk(1, "qbuf: buffer initialization failed\n");
+ goto err;
+ }
+
+ /*
+ * Now that everything is in order, copy relevant information
+ * provided by userspace.
+ */
+ for (plane = 0; plane < vb->num_planes; ++plane)
+ vb->v4l2_planes[plane] = planes[plane];
+
+ return 0;
+err:
+ /* In case of errors, release planes that were already acquired */
+ __vb2_buf_dmabuf_put(vb);
+
+ return ret;
+}
+
+/**
* __enqueue_in_driver() - enqueue a vb2_buffer in driver for processing
*/
static void __enqueue_in_driver(struct vb2_buffer *vb)
{
struct vb2_queue *q = vb->vb2_queue;
+ unsigned int plane;
vb->state = VB2_BUF_STATE_ACTIVE;
atomic_inc(&q->queued_count);
+
+ /* sync buffers */
+ for (plane = 0; plane < vb->num_planes; ++plane)
+ call_memop(q, prepare, vb->planes[plane].mem_priv);
+
q->ops->buf_queue(vb);
}
@@ -982,6 +1165,9 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
case V4L2_MEMORY_USERPTR:
ret = __qbuf_userptr(vb, b);
break;
+ case V4L2_MEMORY_DMABUF:
+ ret = __qbuf_dmabuf(vb, b);
+ break;
default:
WARN(1, "Invalid queue type\n");
ret = -EINVAL;
@@ -1303,6 +1489,30 @@ int vb2_wait_for_all_buffers(struct vb2_queue *q)
EXPORT_SYMBOL_GPL(vb2_wait_for_all_buffers);
/**
+ * __vb2_dqbuf() - bring back the buffer to the DEQUEUED state
+ */
+static void __vb2_dqbuf(struct vb2_buffer *vb)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ unsigned int i;
+
+ /* nothing to do if the buffer is already dequeued */
+ if (vb->state == VB2_BUF_STATE_DEQUEUED)
+ return;
+
+ vb->state = VB2_BUF_STATE_DEQUEUED;
+
+ /* unmap DMABUF buffer */
+ if (q->memory == V4L2_MEMORY_DMABUF)
+ for (i = 0; i < vb->num_planes; ++i) {
+ if (!vb->planes[i].dbuf_mapped)
+ continue;
+ call_memop(q, unmap_dmabuf, vb->planes[i].mem_priv);
+ vb->planes[i].dbuf_mapped = 0;
+ }
+}
+
+/**
* vb2_dqbuf() - Dequeue a buffer to the userspace
* @q: videobuf2 queue
* @b: buffer structure passed from userspace to vidioc_dqbuf handler
@@ -1363,11 +1573,12 @@ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
__fill_v4l2_buffer(vb, b);
/* Remove from videobuf queue */
list_del(&vb->queued_entry);
+ /* go back to dequeued state */
+ __vb2_dqbuf(vb);
dprintk(1, "dqbuf of buffer %d, with state %d\n",
vb->v4l2_buf.index, vb->state);
- vb->state = VB2_BUF_STATE_DEQUEUED;
return 0;
}
EXPORT_SYMBOL_GPL(vb2_dqbuf);
@@ -1406,7 +1617,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
* Reinitialize all buffers for next use.
*/
for (i = 0; i < q->num_buffers; ++i)
- q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED;
+ __vb2_dqbuf(q->bufs[i]);
}
/**
@@ -1540,6 +1751,79 @@ static int __find_plane_by_offset(struct vb2_queue *q, unsigned long off,
}
/**
+ * vb2_expbuf() - Export a buffer as a file descriptor
+ * @q: videobuf2 queue
+ * @eb: export buffer structure passed from userspace to vidioc_expbuf
+ * handler in driver
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_expbuf handler in driver.
+ */
+int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb)
+{
+ struct vb2_buffer *vb = NULL;
+ struct vb2_plane *vb_plane;
+ int ret;
+ struct dma_buf *dbuf;
+
+ if (q->memory != V4L2_MEMORY_MMAP) {
+ dprintk(1, "Queue is not currently set up for mmap\n");
+ return -EINVAL;
+ }
+
+ if (!q->mem_ops->get_dmabuf) {
+ dprintk(1, "Queue does not support DMA buffer exporting\n");
+ return -EINVAL;
+ }
+
+ if (eb->flags & ~O_CLOEXEC) {
+ dprintk(1, "Queue does support only O_CLOEXEC flag\n");
+ return -EINVAL;
+ }
+
+ if (eb->type != q->type) {
+ dprintk(1, "qbuf: invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ if (eb->index >= q->num_buffers) {
+ dprintk(1, "buffer index out of range\n");
+ return -EINVAL;
+ }
+
+ vb = q->bufs[eb->index];
+
+ if (eb->plane >= vb->num_planes) {
+ dprintk(1, "buffer plane out of range\n");
+ return -EINVAL;
+ }
+
+ vb_plane = &vb->planes[eb->plane];
+
+ dbuf = call_memop(q, get_dmabuf, vb_plane->mem_priv);
+ if (IS_ERR_OR_NULL(dbuf)) {
+ dprintk(1, "Failed to export buffer %d, plane %d\n",
+ eb->index, eb->plane);
+ return -EINVAL;
+ }
+
+ ret = dma_buf_fd(dbuf, eb->flags);
+ if (ret < 0) {
+ dprintk(3, "buffer %d, plane %d failed to export (%d)\n",
+ eb->index, eb->plane, ret);
+ dma_buf_put(dbuf);
+ return ret;
+ }
+
+ dprintk(3, "buffer %d, plane %d exported as %d descriptor\n",
+ eb->index, eb->plane, ret);
+ eb->fd = ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_expbuf);
+
+/**
* vb2_mmap() - map video buffers into application address space
* @q: videobuf2 queue
* @vma: vma passed to the mmap file operation handler in the driver
@@ -2245,6 +2529,16 @@ int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
}
EXPORT_SYMBOL_GPL(vb2_ioctl_streamoff);
+int vb2_ioctl_expbuf(struct file *file, void *priv, struct v4l2_exportbuffer *p)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vb2_queue_is_busy(vdev, file))
+ return -EBUSY;
+ return vb2_expbuf(vdev->queue, p);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_expbuf);
+
/* v4l2_file_operations helpers */
int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma)
diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c
index 4b7132660a9..10beaee7f0a 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c
@@ -10,7 +10,10 @@
* the Free Software Foundation.
*/
+#include <linux/dma-buf.h>
#include <linux/module.h>
+#include <linux/scatterlist.h>
+#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
@@ -23,40 +26,158 @@ struct vb2_dc_conf {
};
struct vb2_dc_buf {
- struct vb2_dc_conf *conf;
+ struct device *dev;
void *vaddr;
- dma_addr_t dma_addr;
unsigned long size;
- struct vm_area_struct *vma;
- atomic_t refcount;
+ dma_addr_t dma_addr;
+ enum dma_data_direction dma_dir;
+ struct sg_table *dma_sgt;
+
+ /* MMAP related */
struct vb2_vmarea_handler handler;
+ atomic_t refcount;
+ struct sg_table *sgt_base;
+
+ /* USERPTR related */
+ struct vm_area_struct *vma;
+
+ /* DMABUF related */
+ struct dma_buf_attachment *db_attach;
};
-static void vb2_dma_contig_put(void *buf_priv);
+/*********************************************/
+/* scatterlist table functions */
+/*********************************************/
+
+
+static void vb2_dc_sgt_foreach_page(struct sg_table *sgt,
+ void (*cb)(struct page *pg))
+{
+ struct scatterlist *s;
+ unsigned int i;
+
+ for_each_sg(sgt->sgl, s, sgt->orig_nents, i) {
+ struct page *page = sg_page(s);
+ unsigned int n_pages = PAGE_ALIGN(s->offset + s->length)
+ >> PAGE_SHIFT;
+ unsigned int j;
+
+ for (j = 0; j < n_pages; ++j, ++page)
+ cb(page);
+ }
+}
+
+static unsigned long vb2_dc_get_contiguous_size(struct sg_table *sgt)
+{
+ struct scatterlist *s;
+ dma_addr_t expected = sg_dma_address(sgt->sgl);
+ unsigned int i;
+ unsigned long size = 0;
+
+ for_each_sg(sgt->sgl, s, sgt->nents, i) {
+ if (sg_dma_address(s) != expected)
+ break;
+ expected = sg_dma_address(s) + sg_dma_len(s);
+ size += sg_dma_len(s);
+ }
+ return size;
+}
+
+/*********************************************/
+/* callbacks for all buffers */
+/*********************************************/
+
+static void *vb2_dc_cookie(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+
+ return &buf->dma_addr;
+}
+
+static void *vb2_dc_vaddr(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+
+ return buf->vaddr;
+}
+
+static unsigned int vb2_dc_num_users(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+
+ return atomic_read(&buf->refcount);
+}
+
+static void vb2_dc_prepare(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+ struct sg_table *sgt = buf->dma_sgt;
+
+ /* DMABUF exporter will flush the cache for us */
+ if (!sgt || buf->db_attach)
+ return;
+
+ dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir);
+}
+
+static void vb2_dc_finish(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+ struct sg_table *sgt = buf->dma_sgt;
+
+ /* DMABUF exporter will flush the cache for us */
+ if (!sgt || buf->db_attach)
+ return;
+
+ dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir);
+}
+
+/*********************************************/
+/* callbacks for MMAP buffers */
+/*********************************************/
+
+static void vb2_dc_put(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+
+ if (!atomic_dec_and_test(&buf->refcount))
+ return;
+
+ if (buf->sgt_base) {
+ sg_free_table(buf->sgt_base);
+ kfree(buf->sgt_base);
+ }
+ dma_free_coherent(buf->dev, buf->size, buf->vaddr, buf->dma_addr);
+ put_device(buf->dev);
+ kfree(buf);
+}
-static void *vb2_dma_contig_alloc(void *alloc_ctx, unsigned long size)
+static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size)
{
struct vb2_dc_conf *conf = alloc_ctx;
+ struct device *dev = conf->dev;
struct vb2_dc_buf *buf;
buf = kzalloc(sizeof *buf, GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
- buf->vaddr = dma_alloc_coherent(conf->dev, size, &buf->dma_addr,
- GFP_KERNEL);
+ /* align image size to PAGE_SIZE */
+ size = PAGE_ALIGN(size);
+
+ buf->vaddr = dma_alloc_coherent(dev, size, &buf->dma_addr, GFP_KERNEL);
if (!buf->vaddr) {
- dev_err(conf->dev, "dma_alloc_coherent of size %ld failed\n",
- size);
+ dev_err(dev, "dma_alloc_coherent of size %ld failed\n", size);
kfree(buf);
return ERR_PTR(-ENOMEM);
}
- buf->conf = conf;
+ /* Prevent the device from being released while the buffer is used */
+ buf->dev = get_device(dev);
buf->size = size;
buf->handler.refcount = &buf->refcount;
- buf->handler.put = vb2_dma_contig_put;
+ buf->handler.put = vb2_dc_put;
buf->handler.arg = buf;
atomic_inc(&buf->refcount);
@@ -64,100 +185,569 @@ static void *vb2_dma_contig_alloc(void *alloc_ctx, unsigned long size)
return buf;
}
-static void vb2_dma_contig_put(void *buf_priv)
+static int vb2_dc_mmap(void *buf_priv, struct vm_area_struct *vma)
{
struct vb2_dc_buf *buf = buf_priv;
+ int ret;
- if (atomic_dec_and_test(&buf->refcount)) {
- dma_free_coherent(buf->conf->dev, buf->size, buf->vaddr,
- buf->dma_addr);
- kfree(buf);
+ if (!buf) {
+ printk(KERN_ERR "No buffer to map\n");
+ return -EINVAL;
+ }
+
+ /*
+ * dma_mmap_* uses vm_pgoff as in-buffer offset, but we want to
+ * map whole buffer
+ */
+ vma->vm_pgoff = 0;
+
+ ret = dma_mmap_coherent(buf->dev, vma, buf->vaddr,
+ buf->dma_addr, buf->size);
+
+ if (ret) {
+ pr_err("Remapping memory failed, error: %d\n", ret);
+ return ret;
}
+
+ vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+ vma->vm_private_data = &buf->handler;
+ vma->vm_ops = &vb2_common_vm_ops;
+
+ vma->vm_ops->open(vma);
+
+ pr_debug("%s: mapped dma addr 0x%08lx at 0x%08lx, size %ld\n",
+ __func__, (unsigned long)buf->dma_addr, vma->vm_start,
+ buf->size);
+
+ return 0;
}
-static void *vb2_dma_contig_cookie(void *buf_priv)
+/*********************************************/
+/* DMABUF ops for exporters */
+/*********************************************/
+
+struct vb2_dc_attachment {
+ struct sg_table sgt;
+ enum dma_data_direction dir;
+};
+
+static int vb2_dc_dmabuf_ops_attach(struct dma_buf *dbuf, struct device *dev,
+ struct dma_buf_attachment *dbuf_attach)
{
- struct vb2_dc_buf *buf = buf_priv;
+ struct vb2_dc_attachment *attach;
+ unsigned int i;
+ struct scatterlist *rd, *wr;
+ struct sg_table *sgt;
+ struct vb2_dc_buf *buf = dbuf->priv;
+ int ret;
- return &buf->dma_addr;
+ attach = kzalloc(sizeof(*attach), GFP_KERNEL);
+ if (!attach)
+ return -ENOMEM;
+
+ sgt = &attach->sgt;
+ /* Copy the buf->base_sgt scatter list to the attachment, as we can't
+ * map the same scatter list to multiple attachments at the same time.
+ */
+ ret = sg_alloc_table(sgt, buf->sgt_base->orig_nents, GFP_KERNEL);
+ if (ret) {
+ kfree(attach);
+ return -ENOMEM;
+ }
+
+ rd = buf->sgt_base->sgl;
+ wr = sgt->sgl;
+ for (i = 0; i < sgt->orig_nents; ++i) {
+ sg_set_page(wr, sg_page(rd), rd->length, rd->offset);
+ rd = sg_next(rd);
+ wr = sg_next(wr);
+ }
+
+ attach->dir = DMA_NONE;
+ dbuf_attach->priv = attach;
+
+ return 0;
}
-static void *vb2_dma_contig_vaddr(void *buf_priv)
+static void vb2_dc_dmabuf_ops_detach(struct dma_buf *dbuf,
+ struct dma_buf_attachment *db_attach)
{
- struct vb2_dc_buf *buf = buf_priv;
- if (!buf)
- return NULL;
+ struct vb2_dc_attachment *attach = db_attach->priv;
+ struct sg_table *sgt;
+
+ if (!attach)
+ return;
+
+ sgt = &attach->sgt;
+
+ /* release the scatterlist cache */
+ if (attach->dir != DMA_NONE)
+ dma_unmap_sg(db_attach->dev, sgt->sgl, sgt->orig_nents,
+ attach->dir);
+ sg_free_table(sgt);
+ kfree(attach);
+ db_attach->priv = NULL;
+}
+
+static struct sg_table *vb2_dc_dmabuf_ops_map(
+ struct dma_buf_attachment *db_attach, enum dma_data_direction dir)
+{
+ struct vb2_dc_attachment *attach = db_attach->priv;
+ /* stealing dmabuf mutex to serialize map/unmap operations */
+ struct mutex *lock = &db_attach->dmabuf->lock;
+ struct sg_table *sgt;
+ int ret;
+
+ mutex_lock(lock);
+
+ sgt = &attach->sgt;
+ /* return previously mapped sg table */
+ if (attach->dir == dir) {
+ mutex_unlock(lock);
+ return sgt;
+ }
+
+ /* release any previous cache */
+ if (attach->dir != DMA_NONE) {
+ dma_unmap_sg(db_attach->dev, sgt->sgl, sgt->orig_nents,
+ attach->dir);
+ attach->dir = DMA_NONE;
+ }
+
+ /* mapping to the client with new direction */
+ ret = dma_map_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, dir);
+ if (ret <= 0) {
+ pr_err("failed to map scatterlist\n");
+ mutex_unlock(lock);
+ return ERR_PTR(-EIO);
+ }
+
+ attach->dir = dir;
+
+ mutex_unlock(lock);
+
+ return sgt;
+}
+
+static void vb2_dc_dmabuf_ops_unmap(struct dma_buf_attachment *db_attach,
+ struct sg_table *sgt, enum dma_data_direction dir)
+{
+ /* nothing to be done here */
+}
+
+static void vb2_dc_dmabuf_ops_release(struct dma_buf *dbuf)
+{
+ /* drop reference obtained in vb2_dc_get_dmabuf */
+ vb2_dc_put(dbuf->priv);
+}
+
+static void *vb2_dc_dmabuf_ops_kmap(struct dma_buf *dbuf, unsigned long pgnum)
+{
+ struct vb2_dc_buf *buf = dbuf->priv;
+
+ return buf->vaddr + pgnum * PAGE_SIZE;
+}
+
+static void *vb2_dc_dmabuf_ops_vmap(struct dma_buf *dbuf)
+{
+ struct vb2_dc_buf *buf = dbuf->priv;
return buf->vaddr;
}
-static unsigned int vb2_dma_contig_num_users(void *buf_priv)
+static int vb2_dc_dmabuf_ops_mmap(struct dma_buf *dbuf,
+ struct vm_area_struct *vma)
{
- struct vb2_dc_buf *buf = buf_priv;
+ return vb2_dc_mmap(dbuf->priv, vma);
+}
- return atomic_read(&buf->refcount);
+static struct dma_buf_ops vb2_dc_dmabuf_ops = {
+ .attach = vb2_dc_dmabuf_ops_attach,
+ .detach = vb2_dc_dmabuf_ops_detach,
+ .map_dma_buf = vb2_dc_dmabuf_ops_map,
+ .unmap_dma_buf = vb2_dc_dmabuf_ops_unmap,
+ .kmap = vb2_dc_dmabuf_ops_kmap,
+ .kmap_atomic = vb2_dc_dmabuf_ops_kmap,
+ .vmap = vb2_dc_dmabuf_ops_vmap,
+ .mmap = vb2_dc_dmabuf_ops_mmap,
+ .release = vb2_dc_dmabuf_ops_release,
+};
+
+static struct sg_table *vb2_dc_get_base_sgt(struct vb2_dc_buf *buf)
+{
+ int ret;
+ struct sg_table *sgt;
+
+ sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt) {
+ dev_err(buf->dev, "failed to alloc sg table\n");
+ return NULL;
+ }
+
+ ret = dma_get_sgtable(buf->dev, sgt, buf->vaddr, buf->dma_addr,
+ buf->size);
+ if (ret < 0) {
+ dev_err(buf->dev, "failed to get scatterlist from DMA API\n");
+ kfree(sgt);
+ return NULL;
+ }
+
+ return sgt;
}
-static int vb2_dma_contig_mmap(void *buf_priv, struct vm_area_struct *vma)
+static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv)
{
struct vb2_dc_buf *buf = buf_priv;
+ struct dma_buf *dbuf;
- if (!buf) {
- printk(KERN_ERR "No buffer to map\n");
- return -EINVAL;
+ if (!buf->sgt_base)
+ buf->sgt_base = vb2_dc_get_base_sgt(buf);
+
+ if (WARN_ON(!buf->sgt_base))
+ return NULL;
+
+ dbuf = dma_buf_export(buf, &vb2_dc_dmabuf_ops, buf->size, 0);
+ if (IS_ERR(dbuf))
+ return NULL;
+
+ /* dmabuf keeps reference to vb2 buffer */
+ atomic_inc(&buf->refcount);
+
+ return dbuf;
+}
+
+/*********************************************/
+/* callbacks for USERPTR buffers */
+/*********************************************/
+
+static inline int vma_is_io(struct vm_area_struct *vma)
+{
+ return !!(vma->vm_flags & (VM_IO | VM_PFNMAP));
+}
+
+static int vb2_dc_get_user_pages(unsigned long start, struct page **pages,
+ int n_pages, struct vm_area_struct *vma, int write)
+{
+ if (vma_is_io(vma)) {
+ unsigned int i;
+
+ for (i = 0; i < n_pages; ++i, start += PAGE_SIZE) {
+ unsigned long pfn;
+ int ret = follow_pfn(vma, start, &pfn);
+
+ if (ret) {
+ pr_err("no page for address %lu\n", start);
+ return ret;
+ }
+ pages[i] = pfn_to_page(pfn);
+ }
+ } else {
+ int n;
+
+ n = get_user_pages(current, current->mm, start & PAGE_MASK,
+ n_pages, write, 1, pages, NULL);
+ /* negative error means that no page was pinned */
+ n = max(n, 0);
+ if (n != n_pages) {
+ pr_err("got only %d of %d user pages\n", n, n_pages);
+ while (n)
+ put_page(pages[--n]);
+ return -EFAULT;
+ }
}
- return vb2_mmap_pfn_range(vma, buf->dma_addr, buf->size,
- &vb2_common_vm_ops, &buf->handler);
+ return 0;
}
-static void *vb2_dma_contig_get_userptr(void *alloc_ctx, unsigned long vaddr,
- unsigned long size, int write)
+static void vb2_dc_put_dirty_page(struct page *page)
{
+ set_page_dirty_lock(page);
+ put_page(page);
+}
+
+static void vb2_dc_put_userptr(void *buf_priv)
+{
+ struct vb2_dc_buf *buf = buf_priv;
+ struct sg_table *sgt = buf->dma_sgt;
+
+ dma_unmap_sg(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir);
+ if (!vma_is_io(buf->vma))
+ vb2_dc_sgt_foreach_page(sgt, vb2_dc_put_dirty_page);
+
+ sg_free_table(sgt);
+ kfree(sgt);
+ vb2_put_vma(buf->vma);
+ kfree(buf);
+}
+
+static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
+ unsigned long size, int write)
+{
+ struct vb2_dc_conf *conf = alloc_ctx;
struct vb2_dc_buf *buf;
+ unsigned long start;
+ unsigned long end;
+ unsigned long offset;
+ struct page **pages;
+ int n_pages;
+ int ret = 0;
struct vm_area_struct *vma;
- dma_addr_t dma_addr = 0;
- int ret;
+ struct sg_table *sgt;
+ unsigned long contig_size;
+ unsigned long dma_align = dma_get_cache_alignment();
+
+ /* Only cache aligned DMA transfers are reliable */
+ if (!IS_ALIGNED(vaddr | size, dma_align)) {
+ pr_debug("user data must be aligned to %lu bytes\n", dma_align);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!size) {
+ pr_debug("size is zero\n");
+ return ERR_PTR(-EINVAL);
+ }
buf = kzalloc(sizeof *buf, GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
- ret = vb2_get_contig_userptr(vaddr, size, &vma, &dma_addr);
+ buf->dev = conf->dev;
+ buf->dma_dir = write ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ start = vaddr & PAGE_MASK;
+ offset = vaddr & ~PAGE_MASK;
+ end = PAGE_ALIGN(vaddr + size);
+ n_pages = (end - start) >> PAGE_SHIFT;
+
+ pages = kmalloc(n_pages * sizeof(pages[0]), GFP_KERNEL);
+ if (!pages) {
+ ret = -ENOMEM;
+ pr_err("failed to allocate pages table\n");
+ goto fail_buf;
+ }
+
+ /* current->mm->mmap_sem is taken by videobuf2 core */
+ vma = find_vma(current->mm, vaddr);
+ if (!vma) {
+ pr_err("no vma for address %lu\n", vaddr);
+ ret = -EFAULT;
+ goto fail_pages;
+ }
+
+ if (vma->vm_end < vaddr + size) {
+ pr_err("vma at %lu is too small for %lu bytes\n", vaddr, size);
+ ret = -EFAULT;
+ goto fail_pages;
+ }
+
+ buf->vma = vb2_get_vma(vma);
+ if (!buf->vma) {
+ pr_err("failed to copy vma\n");
+ ret = -ENOMEM;
+ goto fail_pages;
+ }
+
+ /* extract page list from userspace mapping */
+ ret = vb2_dc_get_user_pages(start, pages, n_pages, vma, write);
if (ret) {
- printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n",
- vaddr);
- kfree(buf);
- return ERR_PTR(ret);
+ pr_err("failed to get user pages\n");
+ goto fail_vma;
+ }
+
+ sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt) {
+ pr_err("failed to allocate sg table\n");
+ ret = -ENOMEM;
+ goto fail_get_user_pages;
+ }
+
+ ret = sg_alloc_table_from_pages(sgt, pages, n_pages,
+ offset, size, GFP_KERNEL);
+ if (ret) {
+ pr_err("failed to initialize sg table\n");
+ goto fail_sgt;
}
+ /* pages are no longer needed */
+ kfree(pages);
+ pages = NULL;
+
+ sgt->nents = dma_map_sg(buf->dev, sgt->sgl, sgt->orig_nents,
+ buf->dma_dir);
+ if (sgt->nents <= 0) {
+ pr_err("failed to map scatterlist\n");
+ ret = -EIO;
+ goto fail_sgt_init;
+ }
+
+ contig_size = vb2_dc_get_contiguous_size(sgt);
+ if (contig_size < size) {
+ pr_err("contiguous mapping is too small %lu/%lu\n",
+ contig_size, size);
+ ret = -EFAULT;
+ goto fail_map_sg;
+ }
+
+ buf->dma_addr = sg_dma_address(sgt->sgl);
buf->size = size;
- buf->dma_addr = dma_addr;
- buf->vma = vma;
+ buf->dma_sgt = sgt;
return buf;
+
+fail_map_sg:
+ dma_unmap_sg(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir);
+
+fail_sgt_init:
+ if (!vma_is_io(buf->vma))
+ vb2_dc_sgt_foreach_page(sgt, put_page);
+ sg_free_table(sgt);
+
+fail_sgt:
+ kfree(sgt);
+
+fail_get_user_pages:
+ if (pages && !vma_is_io(buf->vma))
+ while (n_pages)
+ put_page(pages[--n_pages]);
+
+fail_vma:
+ vb2_put_vma(buf->vma);
+
+fail_pages:
+ kfree(pages); /* kfree is NULL-proof */
+
+fail_buf:
+ kfree(buf);
+
+ return ERR_PTR(ret);
}
-static void vb2_dma_contig_put_userptr(void *mem_priv)
+/*********************************************/
+/* callbacks for DMABUF buffers */
+/*********************************************/
+
+static int vb2_dc_map_dmabuf(void *mem_priv)
{
struct vb2_dc_buf *buf = mem_priv;
+ struct sg_table *sgt;
+ unsigned long contig_size;
- if (!buf)
+ if (WARN_ON(!buf->db_attach)) {
+ pr_err("trying to pin a non attached buffer\n");
+ return -EINVAL;
+ }
+
+ if (WARN_ON(buf->dma_sgt)) {
+ pr_err("dmabuf buffer is already pinned\n");
+ return 0;
+ }
+
+ /* get the associated scatterlist for this buffer */
+ sgt = dma_buf_map_attachment(buf->db_attach, buf->dma_dir);
+ if (IS_ERR_OR_NULL(sgt)) {
+ pr_err("Error getting dmabuf scatterlist\n");
+ return -EINVAL;
+ }
+
+ /* checking if dmabuf is big enough to store contiguous chunk */
+ contig_size = vb2_dc_get_contiguous_size(sgt);
+ if (contig_size < buf->size) {
+ pr_err("contiguous chunk is too small %lu/%lu b\n",
+ contig_size, buf->size);
+ dma_buf_unmap_attachment(buf->db_attach, sgt, buf->dma_dir);
+ return -EFAULT;
+ }
+
+ buf->dma_addr = sg_dma_address(sgt->sgl);
+ buf->dma_sgt = sgt;
+
+ return 0;
+}
+
+static void vb2_dc_unmap_dmabuf(void *mem_priv)
+{
+ struct vb2_dc_buf *buf = mem_priv;
+ struct sg_table *sgt = buf->dma_sgt;
+
+ if (WARN_ON(!buf->db_attach)) {
+ pr_err("trying to unpin a not attached buffer\n");
return;
+ }
- vb2_put_vma(buf->vma);
+ if (WARN_ON(!sgt)) {
+ pr_err("dmabuf buffer is already unpinned\n");
+ return;
+ }
+
+ dma_buf_unmap_attachment(buf->db_attach, sgt, buf->dma_dir);
+
+ buf->dma_addr = 0;
+ buf->dma_sgt = NULL;
+}
+
+static void vb2_dc_detach_dmabuf(void *mem_priv)
+{
+ struct vb2_dc_buf *buf = mem_priv;
+
+ /* if vb2 works correctly you should never detach mapped buffer */
+ if (WARN_ON(buf->dma_addr))
+ vb2_dc_unmap_dmabuf(buf);
+
+ /* detach this attachment */
+ dma_buf_detach(buf->db_attach->dmabuf, buf->db_attach);
kfree(buf);
}
+static void *vb2_dc_attach_dmabuf(void *alloc_ctx, struct dma_buf *dbuf,
+ unsigned long size, int write)
+{
+ struct vb2_dc_conf *conf = alloc_ctx;
+ struct vb2_dc_buf *buf;
+ struct dma_buf_attachment *dba;
+
+ if (dbuf->size < size)
+ return ERR_PTR(-EFAULT);
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ buf->dev = conf->dev;
+ /* create attachment for the dmabuf with the user device */
+ dba = dma_buf_attach(dbuf, buf->dev);
+ if (IS_ERR(dba)) {
+ pr_err("failed to attach dmabuf\n");
+ kfree(buf);
+ return dba;
+ }
+
+ buf->dma_dir = write ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ buf->size = size;
+ buf->db_attach = dba;
+
+ return buf;
+}
+
+/*********************************************/
+/* DMA CONTIG exported functions */
+/*********************************************/
+
const struct vb2_mem_ops vb2_dma_contig_memops = {
- .alloc = vb2_dma_contig_alloc,
- .put = vb2_dma_contig_put,
- .cookie = vb2_dma_contig_cookie,
- .vaddr = vb2_dma_contig_vaddr,
- .mmap = vb2_dma_contig_mmap,
- .get_userptr = vb2_dma_contig_get_userptr,
- .put_userptr = vb2_dma_contig_put_userptr,
- .num_users = vb2_dma_contig_num_users,
+ .alloc = vb2_dc_alloc,
+ .put = vb2_dc_put,
+ .get_dmabuf = vb2_dc_get_dmabuf,
+ .cookie = vb2_dc_cookie,
+ .vaddr = vb2_dc_vaddr,
+ .mmap = vb2_dc_mmap,
+ .get_userptr = vb2_dc_get_userptr,
+ .put_userptr = vb2_dc_put_userptr,
+ .prepare = vb2_dc_prepare,
+ .finish = vb2_dc_finish,
+ .map_dmabuf = vb2_dc_map_dmabuf,
+ .unmap_dmabuf = vb2_dc_unmap_dmabuf,
+ .attach_dmabuf = vb2_dc_attach_dmabuf,
+ .detach_dmabuf = vb2_dc_detach_dmabuf,
+ .num_users = vb2_dc_num_users,
};
EXPORT_SYMBOL_GPL(vb2_dma_contig_memops);
diff --git a/drivers/media/v4l2-core/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c
index 051ea3571b2..81c1ad8b2cf 100644
--- a/drivers/media/v4l2-core/videobuf2-memops.c
+++ b/drivers/media/v4l2-core/videobuf2-memops.c
@@ -137,46 +137,6 @@ int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size,
EXPORT_SYMBOL_GPL(vb2_get_contig_userptr);
/**
- * vb2_mmap_pfn_range() - map physical pages to userspace
- * @vma: virtual memory region for the mapping
- * @paddr: starting physical address of the memory to be mapped
- * @size: size of the memory to be mapped
- * @vm_ops: vm operations to be assigned to the created area
- * @priv: private data to be associated with the area
- *
- * Returns 0 on success.
- */
-int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr,
- unsigned long size,
- const struct vm_operations_struct *vm_ops,
- void *priv)
-{
- int ret;
-
- size = min_t(unsigned long, vma->vm_end - vma->vm_start, size);
-
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- ret = remap_pfn_range(vma, vma->vm_start, paddr >> PAGE_SHIFT,
- size, vma->vm_page_prot);
- if (ret) {
- printk(KERN_ERR "Remapping memory failed, error: %d\n", ret);
- return ret;
- }
-
- vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
- vma->vm_private_data = priv;
- vma->vm_ops = vm_ops;
-
- vma->vm_ops->open(vma);
-
- pr_debug("%s: mapped paddr 0x%08lx at 0x%08lx, size %ld\n",
- __func__, paddr, vma->vm_start, size);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(vb2_mmap_pfn_range);
-
-/**
* vb2_common_vm_open() - increase refcount of the vma
* @vma: virtual memory region for the mapping
*
diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c
index 94efa04d8d5..a47fd4f589a 100644
--- a/drivers/media/v4l2-core/videobuf2-vmalloc.c
+++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c
@@ -30,6 +30,7 @@ struct vb2_vmalloc_buf {
unsigned int n_pages;
atomic_t refcount;
struct vb2_vmarea_handler handler;
+ struct dma_buf *dbuf;
};
static void vb2_vmalloc_put(void *buf_priv);
@@ -207,11 +208,66 @@ static int vb2_vmalloc_mmap(void *buf_priv, struct vm_area_struct *vma)
return 0;
}
+/*********************************************/
+/* callbacks for DMABUF buffers */
+/*********************************************/
+
+static int vb2_vmalloc_map_dmabuf(void *mem_priv)
+{
+ struct vb2_vmalloc_buf *buf = mem_priv;
+
+ buf->vaddr = dma_buf_vmap(buf->dbuf);
+
+ return buf->vaddr ? 0 : -EFAULT;
+}
+
+static void vb2_vmalloc_unmap_dmabuf(void *mem_priv)
+{
+ struct vb2_vmalloc_buf *buf = mem_priv;
+
+ dma_buf_vunmap(buf->dbuf, buf->vaddr);
+ buf->vaddr = NULL;
+}
+
+static void vb2_vmalloc_detach_dmabuf(void *mem_priv)
+{
+ struct vb2_vmalloc_buf *buf = mem_priv;
+
+ if (buf->vaddr)
+ dma_buf_vunmap(buf->dbuf, buf->vaddr);
+
+ kfree(buf);
+}
+
+static void *vb2_vmalloc_attach_dmabuf(void *alloc_ctx, struct dma_buf *dbuf,
+ unsigned long size, int write)
+{
+ struct vb2_vmalloc_buf *buf;
+
+ if (dbuf->size < size)
+ return ERR_PTR(-EFAULT);
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ buf->dbuf = dbuf;
+ buf->write = write;
+ buf->size = size;
+
+ return buf;
+}
+
+
const struct vb2_mem_ops vb2_vmalloc_memops = {
.alloc = vb2_vmalloc_alloc,
.put = vb2_vmalloc_put,
.get_userptr = vb2_vmalloc_get_userptr,
.put_userptr = vb2_vmalloc_put_userptr,
+ .map_dmabuf = vb2_vmalloc_map_dmabuf,
+ .unmap_dmabuf = vb2_vmalloc_unmap_dmabuf,
+ .attach_dmabuf = vb2_vmalloc_attach_dmabuf,
+ .detach_dmabuf = vb2_vmalloc_detach_dmabuf,
.vaddr = vb2_vmalloc_vaddr,
.mmap = vb2_vmalloc_mmap,
.num_users = vb2_vmalloc_num_users,
diff --git a/drivers/message/i2o/README.ioctl b/drivers/message/i2o/README.ioctl
index 5fb195af43e..4a7d2ebdfc9 100644
--- a/drivers/message/i2o/README.ioctl
+++ b/drivers/message/i2o/README.ioctl
@@ -138,7 +138,7 @@ VI. Setting Parameters
The return value is the size in bytes of the data written into
ops->resbuf if no errors occur. If an error occurs, -1 is returned
- and errno is set appropriatly:
+ and errno is set appropriately:
EFAULT Invalid user space pointer was passed
ENXIO Invalid IOP number
@@ -222,7 +222,7 @@ VIII. Downloading Software
RETURNS
This function returns 0 no errors occur. If an error occurs, -1
- is returned and errno is set appropriatly:
+ is returned and errno is set appropriately:
EFAULT Invalid user space pointer was passed
ENXIO Invalid IOP number
@@ -264,7 +264,7 @@ IX. Uploading Software
RETURNS
This function returns 0 if no errors occur. If an error occurs, -1
- is returned and errno is set appropriatly:
+ is returned and errno is set appropriately:
EFAULT Invalid user space pointer was passed
ENXIO Invalid IOP number
@@ -301,7 +301,7 @@ X. Removing Software
RETURNS
This function returns 0 if no errors occur. If an error occurs, -1
- is returned and errno is set appropriatly:
+ is returned and errno is set appropriately:
EFAULT Invalid user space pointer was passed
ENXIO Invalid IOP number
@@ -325,7 +325,7 @@ X. Validating Configuration
RETURNS
This function returns 0 if no erro occur. If an error occurs, -1 is
- returned and errno is set appropriatly:
+ returned and errno is set appropriately:
ETIMEDOUT Timeout waiting for reply message
ENXIO Invalid IOP number
@@ -360,7 +360,7 @@ XI. Configuration Dialog
RETURNS
This function returns 0 if no error occur. If an error occurs, -1
- is returned and errno is set appropriatly:
+ is returned and errno is set appropriately:
EFAULT Invalid user space pointer was passed
ENXIO Invalid IOP number
diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c
index 4796bbf0ae4..49e86aed2bc 100644
--- a/drivers/message/i2o/i2o_block.c
+++ b/drivers/message/i2o/i2o_block.c
@@ -609,7 +609,7 @@ static int i2o_block_release(struct gendisk *disk, fmode_t mode)
u8 operation;
/*
- * This is to deail with the case of an application
+ * This is to deal with the case of an application
* opening a device and then the device disappears while
* it's in use, and then the application tries to release
* it. ex: Unmounting a deleted RAID volume at reboot.
diff --git a/drivers/message/i2o/i2o_config.c b/drivers/message/i2o/i2o_config.c
index 9a49c243a6a..5451beff183 100644
--- a/drivers/message/i2o/i2o_config.c
+++ b/drivers/message/i2o/i2o_config.c
@@ -189,7 +189,7 @@ static int i2o_cfg_parms(unsigned long arg, unsigned int type)
return -ENXIO;
/*
- * Stop users being able to try and allocate arbitary amounts
+ * Stop users being able to try and allocate arbitrary amounts
* of DMA space. 64K is way more than sufficient for this.
*/
if (kcmd.oplen > 65536)
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 94bdf83b4bc..b63987c6ed2 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -211,7 +211,6 @@ config MFD_TPS6586X
depends on I2C=y && GENERIC_HARDIRQS
select MFD_CORE
select REGMAP_I2C
- depends on REGULATOR
help
If you say yes here you get support for the TPS6586X series of
Power Management chips.
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index 3e27c031aea..59da1650fb8 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -1036,23 +1036,43 @@ static struct mfd_cell abx500_common_devs[] = {
static struct mfd_cell ab8500_bm_devs[] = {
{
.name = "ab8500-charger",
+ .of_compatible = "stericsson,ab8500-charger",
.num_resources = ARRAY_SIZE(ab8500_charger_resources),
.resources = ab8500_charger_resources,
+#ifndef CONFIG_OF
+ .platform_data = &ab8500_bm_data,
+ .pdata_size = sizeof(ab8500_bm_data),
+#endif
},
{
.name = "ab8500-btemp",
+ .of_compatible = "stericsson,ab8500-btemp",
.num_resources = ARRAY_SIZE(ab8500_btemp_resources),
.resources = ab8500_btemp_resources,
+#ifndef CONFIG_OF
+ .platform_data = &ab8500_bm_data,
+ .pdata_size = sizeof(ab8500_bm_data),
+#endif
},
{
.name = "ab8500-fg",
+ .of_compatible = "stericsson,ab8500-fg",
.num_resources = ARRAY_SIZE(ab8500_fg_resources),
.resources = ab8500_fg_resources,
+#ifndef CONFIG_OF
+ .platform_data = &ab8500_bm_data,
+ .pdata_size = sizeof(ab8500_bm_data),
+#endif
},
{
.name = "ab8500-chargalg",
+ .of_compatible = "stericsson,ab8500-chargalg",
.num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
.resources = ab8500_chargalg_resources,
+#ifndef CONFIG_OF
+ .platform_data = &ab8500_bm_data,
+ .pdata_size = sizeof(ab8500_bm_data),
+#endif
},
};
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
index 1a6f943f733..c784f4602a7 100644
--- a/drivers/mfd/arizona-core.c
+++ b/drivers/mfd/arizona-core.c
@@ -272,6 +272,7 @@ static struct mfd_cell early_devs[] = {
static struct mfd_cell wm5102_devs[] = {
{ .name = "arizona-extcon" },
{ .name = "arizona-gpio" },
+ { .name = "arizona-haptics" },
{ .name = "arizona-micsupp" },
{ .name = "arizona-pwm" },
{ .name = "wm5102-codec" },
@@ -280,6 +281,7 @@ static struct mfd_cell wm5102_devs[] = {
static struct mfd_cell wm5110_devs[] = {
{ .name = "arizona-extcon" },
{ .name = "arizona-gpio" },
+ { .name = "arizona-haptics" },
{ .name = "arizona-micsupp" },
{ .name = "arizona-pwm" },
{ .name = "wm5110-codec" },
diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c
index f123517065e..abd5c80c7cf 100644
--- a/drivers/mfd/max8997.c
+++ b/drivers/mfd/max8997.c
@@ -21,8 +21,10 @@
* This driver is based on max8998.c
*/
+#include <linux/err.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/pm_runtime.h>
#include <linux/module.h>
@@ -47,6 +49,13 @@ static struct mfd_cell max8997_devs[] = {
{ .name = "max8997-led", .id = 2 },
};
+#ifdef CONFIG_OF
+static struct of_device_id __devinitdata max8997_pmic_dt_match[] = {
+ { .compatible = "maxim,max8997-pmic", .data = TYPE_MAX8997 },
+ {},
+};
+#endif
+
int max8997_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest)
{
struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
@@ -123,6 +132,58 @@ int max8997_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask)
}
EXPORT_SYMBOL_GPL(max8997_update_reg);
+#ifdef CONFIG_OF
+/*
+ * Only the common platform data elements for max8997 are parsed here from the
+ * device tree. Other sub-modules of max8997 such as pmic, rtc and others have
+ * to parse their own platform data elements from device tree.
+ *
+ * The max8997 platform data structure is instantiated here and the drivers for
+ * the sub-modules need not instantiate another instance while parsing their
+ * platform data.
+ */
+static struct max8997_platform_data *max8997_i2c_parse_dt_pdata(
+ struct device *dev)
+{
+ struct max8997_platform_data *pd;
+
+ pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+ if (!pd) {
+ dev_err(dev, "could not allocate memory for pdata\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ pd->ono = irq_of_parse_and_map(dev->of_node, 1);
+
+ /*
+ * ToDo: the 'wakeup' member in the platform data is more of a linux
+ * specfic information. Hence, there is no binding for that yet and
+ * not parsed here.
+ */
+
+ return pd;
+}
+#else
+static struct max8997_platform_data *max8997_i2c_parse_dt_pdata(
+ struct device *dev)
+{
+ return 0;
+}
+#endif
+
+static inline int max8997_i2c_get_driver_data(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+#ifdef CONFIG_OF
+ if (i2c->dev.of_node) {
+ const struct of_device_id *match;
+ match = of_match_node(max8997_pmic_dt_match, i2c->dev.of_node);
+ return (int)match->data;
+ }
+#endif
+ return (int)id->driver_data;
+}
+
static int max8997_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -137,12 +198,21 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, max8997);
max8997->dev = &i2c->dev;
max8997->i2c = i2c;
- max8997->type = id->driver_data;
+ max8997->type = max8997_i2c_get_driver_data(i2c, id);
max8997->irq = i2c->irq;
+ if (max8997->dev->of_node) {
+ pdata = max8997_i2c_parse_dt_pdata(max8997->dev);
+ if (IS_ERR(pdata)) {
+ ret = PTR_ERR(pdata);
+ goto err;
+ }
+ }
+
if (!pdata)
goto err;
+ max8997->pdata = pdata;
max8997->ono = pdata->ono;
mutex_init(&max8997->iolock);
@@ -434,6 +504,7 @@ static struct i2c_driver max8997_i2c_driver = {
.name = "max8997",
.owner = THIS_MODULE,
.pm = &max8997_pm,
+ .of_match_table = of_match_ptr(max8997_pmic_dt_match),
},
.probe = max8997_i2c_probe,
.remove = max8997_i2c_remove,
diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c
index 9f92c3b2209..87ba7ada3bb 100644
--- a/drivers/mfd/tps6586x.c
+++ b/drivers/mfd/tps6586x.c
@@ -24,8 +24,6 @@
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
-#include <linux/regulator/of_regulator.h>
-#include <linux/regulator/machine.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tps6586x.h>
@@ -99,6 +97,9 @@ static struct mfd_cell tps6586x_cell[] = {
.name = "tps6586x-gpio",
},
{
+ .name = "tps6586x-pmic",
+ },
+ {
.name = "tps6586x-rtc",
},
{
@@ -350,80 +351,19 @@ failed:
}
#ifdef CONFIG_OF
-static struct of_regulator_match tps6586x_matches[] = {
- { .name = "sys", .driver_data = (void *)TPS6586X_ID_SYS },
- { .name = "sm0", .driver_data = (void *)TPS6586X_ID_SM_0 },
- { .name = "sm1", .driver_data = (void *)TPS6586X_ID_SM_1 },
- { .name = "sm2", .driver_data = (void *)TPS6586X_ID_SM_2 },
- { .name = "ldo0", .driver_data = (void *)TPS6586X_ID_LDO_0 },
- { .name = "ldo1", .driver_data = (void *)TPS6586X_ID_LDO_1 },
- { .name = "ldo2", .driver_data = (void *)TPS6586X_ID_LDO_2 },
- { .name = "ldo3", .driver_data = (void *)TPS6586X_ID_LDO_3 },
- { .name = "ldo4", .driver_data = (void *)TPS6586X_ID_LDO_4 },
- { .name = "ldo5", .driver_data = (void *)TPS6586X_ID_LDO_5 },
- { .name = "ldo6", .driver_data = (void *)TPS6586X_ID_LDO_6 },
- { .name = "ldo7", .driver_data = (void *)TPS6586X_ID_LDO_7 },
- { .name = "ldo8", .driver_data = (void *)TPS6586X_ID_LDO_8 },
- { .name = "ldo9", .driver_data = (void *)TPS6586X_ID_LDO_9 },
- { .name = "ldo_rtc", .driver_data = (void *)TPS6586X_ID_LDO_RTC },
-};
-
static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *client)
{
- const unsigned int num = ARRAY_SIZE(tps6586x_matches);
struct device_node *np = client->dev.of_node;
struct tps6586x_platform_data *pdata;
- struct tps6586x_subdev_info *devs;
- struct device_node *regs;
- const char *sys_rail_name = NULL;
- unsigned int count;
- unsigned int i, j;
- int err;
-
- regs = of_find_node_by_name(np, "regulators");
- if (!regs)
- return NULL;
-
- err = of_regulator_match(&client->dev, regs, tps6586x_matches, num);
- if (err < 0) {
- of_node_put(regs);
- return NULL;
- }
-
- of_node_put(regs);
- count = err;
-
- devs = devm_kzalloc(&client->dev, count * sizeof(*devs), GFP_KERNEL);
- if (!devs)
- return NULL;
-
- for (i = 0, j = 0; i < num && j < count; i++) {
- struct regulator_init_data *reg_idata;
-
- if (!tps6586x_matches[i].init_data)
- continue;
-
- reg_idata = tps6586x_matches[i].init_data;
- devs[j].name = "tps6586x-regulator";
- devs[j].platform_data = tps6586x_matches[i].init_data;
- devs[j].id = (int)tps6586x_matches[i].driver_data;
- if (devs[j].id == TPS6586X_ID_SYS)
- sys_rail_name = reg_idata->constraints.name;
-
- if ((devs[j].id == TPS6586X_ID_LDO_5) ||
- (devs[j].id == TPS6586X_ID_LDO_RTC))
- reg_idata->supply_regulator = sys_rail_name;
-
- devs[j].of_node = tps6586x_matches[i].of_node;
- j++;
- }
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
+ if (!pdata) {
+ dev_err(&client->dev, "Memory allocation failed\n");
return NULL;
+ }
- pdata->num_subdevs = count;
- pdata->subdevs = devs;
+ pdata->num_subdevs = 0;
+ pdata->subdevs = NULL;
pdata->gpio_base = -1;
pdata->irq_base = -1;
pdata->pm_off = of_property_read_bool(np, "ti,system-power-controller");
diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c
index 14490cc785d..3141c4a173a 100644
--- a/drivers/mfd/wm5102-tables.c
+++ b/drivers/mfd/wm5102-tables.c
@@ -258,6 +258,7 @@ static const struct reg_default wm5102_reg_default[] = {
{ 0x00000154, 0x0000 }, /* R340 - Rate Estimator 3 */
{ 0x00000155, 0x0000 }, /* R341 - Rate Estimator 4 */
{ 0x00000156, 0x0000 }, /* R342 - Rate Estimator 5 */
+ { 0x00000161, 0x0000 }, /* R353 - Dynamic Frequency Scaling 1 */
{ 0x00000171, 0x0000 }, /* R369 - FLL1 Control 1 */
{ 0x00000172, 0x0008 }, /* R370 - FLL1 Control 2 */
{ 0x00000173, 0x0018 }, /* R371 - FLL1 Control 3 */
@@ -1047,6 +1048,7 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_RATE_ESTIMATOR_3:
case ARIZONA_RATE_ESTIMATOR_4:
case ARIZONA_RATE_ESTIMATOR_5:
+ case ARIZONA_DYNAMIC_FREQUENCY_SCALING_1:
case ARIZONA_FLL1_CONTROL_1:
case ARIZONA_FLL1_CONTROL_2:
case ARIZONA_FLL1_CONTROL_3:
@@ -1079,6 +1081,7 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg)
case ARIZONA_FLL2_GPIO_CLOCK:
case ARIZONA_MIC_CHARGE_PUMP_1:
case ARIZONA_LDO1_CONTROL_1:
+ case ARIZONA_LDO1_CONTROL_2:
case ARIZONA_LDO2_CONTROL_1:
case ARIZONA_MIC_BIAS_CTRL_1:
case ARIZONA_MIC_BIAS_CTRL_2:
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c
index c7f62ac544a..bcb226ff9d2 100644
--- a/drivers/mfd/wm8994-core.c
+++ b/drivers/mfd/wm8994-core.c
@@ -401,13 +401,19 @@ static const struct reg_default wm1811_reva_patch[] = {
*/
static int wm8994_device_init(struct wm8994 *wm8994, int irq)
{
- struct wm8994_pdata *pdata = wm8994->dev->platform_data;
+ struct wm8994_pdata *pdata;
struct regmap_config *regmap_config;
const struct reg_default *regmap_patch = NULL;
const char *devname;
int ret, i, patch_regs;
int pulls = 0;
+ if (dev_get_platdata(wm8994->dev)) {
+ pdata = dev_get_platdata(wm8994->dev);
+ wm8994->pdata = *pdata;
+ }
+ pdata = &wm8994->pdata;
+
dev_set_drvdata(wm8994->dev, wm8994);
/* Add the on-chip regulators first for bootstrapping */
@@ -604,24 +610,21 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
}
}
- if (pdata) {
- wm8994->irq_base = pdata->irq_base;
- wm8994->gpio_base = pdata->gpio_base;
-
- /* GPIO configuration is only applied if it's non-zero */
- for (i = 0; i < ARRAY_SIZE(pdata->gpio_defaults); i++) {
- if (pdata->gpio_defaults[i]) {
- wm8994_set_bits(wm8994, WM8994_GPIO_1 + i,
- 0xffff,
- pdata->gpio_defaults[i]);
- }
+ wm8994->irq_base = pdata->irq_base;
+ wm8994->gpio_base = pdata->gpio_base;
+
+ /* GPIO configuration is only applied if it's non-zero */
+ for (i = 0; i < ARRAY_SIZE(pdata->gpio_defaults); i++) {
+ if (pdata->gpio_defaults[i]) {
+ wm8994_set_bits(wm8994, WM8994_GPIO_1 + i,
+ 0xffff, pdata->gpio_defaults[i]);
}
+ }
- wm8994->ldo_ena_always_driven = pdata->ldo_ena_always_driven;
+ wm8994->ldo_ena_always_driven = pdata->ldo_ena_always_driven;
- if (pdata->spkmode_pu)
- pulls |= WM8994_SPKMODE_PU;
- }
+ if (pdata->spkmode_pu)
+ pulls |= WM8994_SPKMODE_PU;
/* Disable unneeded pulls */
wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2,
diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c
index c58f9abcb35..158da5a81a6 100644
--- a/drivers/misc/atmel-ssc.c
+++ b/drivers/misc/atmel-ssc.c
@@ -18,6 +18,8 @@
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/of.h>
+
/* Serialize access to ssc_list and user count */
static DEFINE_SPINLOCK(user_lock);
static LIST_HEAD(ssc_list);
@@ -29,7 +31,13 @@ struct ssc_device *ssc_request(unsigned int ssc_num)
spin_lock(&user_lock);
list_for_each_entry(ssc, &ssc_list, list) {
- if (ssc->pdev->id == ssc_num) {
+ if (ssc->pdev->dev.of_node) {
+ if (of_alias_get_id(ssc->pdev->dev.of_node, "ssc")
+ == ssc_num) {
+ ssc_valid = 1;
+ break;
+ }
+ } else if (ssc->pdev->id == ssc_num) {
ssc_valid = 1;
break;
}
@@ -68,39 +76,93 @@ void ssc_free(struct ssc_device *ssc)
}
EXPORT_SYMBOL(ssc_free);
-static int __init ssc_probe(struct platform_device *pdev)
+static struct atmel_ssc_platform_data at91rm9200_config = {
+ .use_dma = 0,
+};
+
+static struct atmel_ssc_platform_data at91sam9g45_config = {
+ .use_dma = 1,
+};
+
+static const struct platform_device_id atmel_ssc_devtypes[] = {
+ {
+ .name = "at91rm9200_ssc",
+ .driver_data = (unsigned long) &at91rm9200_config,
+ }, {
+ .name = "at91sam9g45_ssc",
+ .driver_data = (unsigned long) &at91sam9g45_config,
+ }, {
+ /* sentinel */
+ }
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id atmel_ssc_dt_ids[] = {
+ {
+ .compatible = "atmel,at91rm9200-ssc",
+ .data = &at91rm9200_config,
+ }, {
+ .compatible = "atmel,at91sam9g45-ssc",
+ .data = &at91sam9g45_config,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, atmel_ssc_dt_ids);
+#endif
+
+static inline const struct atmel_ssc_platform_data * __init
+ atmel_ssc_get_driver_data(struct platform_device *pdev)
+{
+ if (pdev->dev.of_node) {
+ const struct of_device_id *match;
+ match = of_match_node(atmel_ssc_dt_ids, pdev->dev.of_node);
+ if (match == NULL)
+ return NULL;
+ return match->data;
+ }
+
+ return (struct atmel_ssc_platform_data *)
+ platform_get_device_id(pdev)->driver_data;
+}
+
+static int ssc_probe(struct platform_device *pdev)
{
- int retval = 0;
struct resource *regs;
struct ssc_device *ssc;
+ const struct atmel_ssc_platform_data *plat_dat;
- ssc = kzalloc(sizeof(struct ssc_device), GFP_KERNEL);
+ ssc = devm_kzalloc(&pdev->dev, sizeof(struct ssc_device), GFP_KERNEL);
if (!ssc) {
dev_dbg(&pdev->dev, "out of memory\n");
- retval = -ENOMEM;
- goto out;
+ return -ENOMEM;
}
+ ssc->pdev = pdev;
+
+ plat_dat = atmel_ssc_get_driver_data(pdev);
+ if (!plat_dat)
+ return -ENODEV;
+ ssc->pdata = (struct atmel_ssc_platform_data *)plat_dat;
+
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs) {
dev_dbg(&pdev->dev, "no mmio resource defined\n");
- retval = -ENXIO;
- goto out_free;
+ return -ENXIO;
}
- ssc->clk = clk_get(&pdev->dev, "pclk");
- if (IS_ERR(ssc->clk)) {
- dev_dbg(&pdev->dev, "no pclk clock defined\n");
- retval = -ENXIO;
- goto out_free;
- }
-
- ssc->pdev = pdev;
- ssc->regs = ioremap(regs->start, resource_size(regs));
+ ssc->regs = devm_request_and_ioremap(&pdev->dev, regs);
if (!ssc->regs) {
dev_dbg(&pdev->dev, "ioremap failed\n");
- retval = -EINVAL;
- goto out_clk;
+ return -EINVAL;
+ }
+
+ ssc->phybase = regs->start;
+
+ ssc->clk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(ssc->clk)) {
+ dev_dbg(&pdev->dev, "no pclk clock defined\n");
+ return -ENXIO;
}
/* disable all interrupts */
@@ -112,8 +174,7 @@ static int __init ssc_probe(struct platform_device *pdev)
ssc->irq = platform_get_irq(pdev, 0);
if (!ssc->irq) {
dev_dbg(&pdev->dev, "could not get irq\n");
- retval = -ENXIO;
- goto out_unmap;
+ return -ENXIO;
}
spin_lock(&user_lock);
@@ -125,16 +186,7 @@ static int __init ssc_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "Atmel SSC device at 0x%p (irq %d)\n",
ssc->regs, ssc->irq);
- goto out;
-
-out_unmap:
- iounmap(ssc->regs);
-out_clk:
- clk_put(ssc->clk);
-out_free:
- kfree(ssc);
-out:
- return retval;
+ return 0;
}
static int ssc_remove(struct platform_device *pdev)
@@ -142,34 +194,23 @@ static int ssc_remove(struct platform_device *pdev)
struct ssc_device *ssc = platform_get_drvdata(pdev);
spin_lock(&user_lock);
- iounmap(ssc->regs);
- clk_put(ssc->clk);
list_del(&ssc->list);
- kfree(ssc);
spin_unlock(&user_lock);
return 0;
}
static struct platform_driver ssc_driver = {
- .remove = ssc_remove,
.driver = {
.name = "ssc",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(atmel_ssc_dt_ids),
},
+ .id_table = atmel_ssc_devtypes,
+ .probe = ssc_probe,
+ .remove = ssc_remove,
};
-
-static int __init ssc_init(void)
-{
- return platform_driver_probe(&ssc_driver, ssc_probe);
-}
-module_init(ssc_init);
-
-static void __exit ssc_exit(void)
-{
- platform_driver_unregister(&ssc_driver);
-}
-module_exit(ssc_exit);
+module_platform_driver(ssc_driver);
MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>");
MODULE_DESCRIPTION("SSC driver for Atmel AVR32 and AT91");
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 737e4edc241..8d13c659452 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -533,7 +533,7 @@ config MMC_DW_PLTFM
If unsure, say Y.
config MMC_DW_EXYNOS
- tristate "Exynos specific extentions for Synopsys DW Memory Card Interface"
+ tristate "Exynos specific extensions for Synopsys DW Memory Card Interface"
depends on MMC_DW
select MMC_DW_PLTFM
help
diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c
index 891558de3ec..2de66b062f0 100644
--- a/drivers/mtd/maps/plat-ram.c
+++ b/drivers/mtd/maps/plat-ram.c
@@ -219,7 +219,7 @@ static int platram_probe(struct platform_device *pdev)
platram_setrw(info, PLATRAM_RW);
- /* check to see if there are any available partitions, or wether
+ /* check to see if there are any available partitions, or whether
* to add this device whole */
err = mtd_device_parse_register(info->mtd, pdata->probes, NULL,
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 295e4bedad9..79ded48e742 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -879,7 +879,7 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
if (chip->ecc.mode != NAND_ECC_HW)
return;
- /* change the behaviour depending on wether we are using
+ /* change the behaviour depending on whether we are using
* the large or small page nand device */
if (chip->page_shift > 10) {
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index 6dded569b11..21b68e5c14f 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -245,7 +245,7 @@ struct bonding {
struct delayed_work ad_work;
struct delayed_work mcast_work;
#ifdef CONFIG_DEBUG_FS
- /* debugging suport via debugfs */
+ /* debugging support via debugfs */
struct dentry *debug_dir;
#endif /* CONFIG_DEBUG_FS */
};
diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c
index 0338352bc03..70dba5d01ad 100644
--- a/drivers/net/ethernet/8390/ax88796.c
+++ b/drivers/net/ethernet/8390/ax88796.c
@@ -109,7 +109,7 @@ static inline struct ax_device *to_ax_dev(struct net_device *dev)
/*
* ax_initial_check
*
- * do an initial probe for the card to check wether it exists
+ * do an initial probe for the card to check whether it exists
* and is functional
*/
static int ax_initial_check(struct net_device *dev)
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
index b8b4b749daa..09b625e0fda 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
@@ -4318,7 +4318,7 @@ static int bnx2x_queue_comp_cmd(struct bnx2x *bp,
if (o->next_tx_only >= o->max_cos)
/* >= becuase tx only must always be smaller than cos since the
- * primary connection suports COS 0
+ * primary connection supports COS 0
*/
BNX2X_ERR("illegal value for next tx_only: %d. max cos was %d",
o->next_tx_only, o->max_cos);
diff --git a/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c b/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
index aef45d3113b..3dee68612c9 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
@@ -3307,7 +3307,7 @@ static void config_pcie(struct adapter *adap)
G_NUMFSTTRNSEQRX(t3_read_reg(adap, A_PCIE_MODE));
log2_width = fls(adap->params.pci.width) - 1;
acklat = ack_lat[log2_width][pldsize];
- if (val & 1) /* check LOsEnable */
+ if (val & PCI_EXP_LNKCTL_ASPM_L0S) /* check LOsEnable */
acklat += fst_trn_tx * 4;
rpllmt = rpl_tmr[log2_width][pldsize] + fst_trn_rx * 4;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 130dd9d5b49..a27b4ae20f4 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -3203,7 +3203,7 @@ static int adap_init1(struct adapter *adap, struct fw_caps_config_cmd *c)
memset(c, 0, sizeof(*c));
c->op_to_write = htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) |
FW_CMD_REQUEST | FW_CMD_READ);
- c->retval_len16 = htonl(FW_LEN16(*c));
+ c->cfvalid_to_len16 = htonl(FW_LEN16(*c));
ret = t4_wr_mbox(adap, adap->fn, c, sizeof(*c), c);
if (ret < 0)
return ret;
@@ -3397,7 +3397,7 @@ static int adap_init0_config(struct adapter *adapter, int reset)
htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) |
FW_CMD_REQUEST |
FW_CMD_READ);
- caps_cmd.retval_len16 =
+ caps_cmd.cfvalid_to_len16 =
htonl(FW_CAPS_CONFIG_CMD_CFVALID |
FW_CAPS_CONFIG_CMD_MEMTYPE_CF(mtype) |
FW_CAPS_CONFIG_CMD_MEMADDR64K_CF(maddr >> 16) |
@@ -3422,7 +3422,7 @@ static int adap_init0_config(struct adapter *adapter, int reset)
htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) |
FW_CMD_REQUEST |
FW_CMD_WRITE);
- caps_cmd.retval_len16 = htonl(FW_LEN16(caps_cmd));
+ caps_cmd.cfvalid_to_len16 = htonl(FW_LEN16(caps_cmd));
ret = t4_wr_mbox(adapter, adapter->mbox, &caps_cmd, sizeof(caps_cmd),
NULL);
if (ret < 0)
@@ -3497,7 +3497,7 @@ static int adap_init0_no_config(struct adapter *adapter, int reset)
memset(&caps_cmd, 0, sizeof(caps_cmd));
caps_cmd.op_to_write = htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) |
FW_CMD_REQUEST | FW_CMD_READ);
- caps_cmd.retval_len16 = htonl(FW_LEN16(caps_cmd));
+ caps_cmd.cfvalid_to_len16 = htonl(FW_LEN16(caps_cmd));
ret = t4_wr_mbox(adapter, adapter->mbox, &caps_cmd, sizeof(caps_cmd),
&caps_cmd);
if (ret < 0)
@@ -3929,7 +3929,7 @@ static int adap_init0(struct adapter *adap)
memset(&caps_cmd, 0, sizeof(caps_cmd));
caps_cmd.op_to_write = htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) |
FW_CMD_REQUEST | FW_CMD_READ);
- caps_cmd.retval_len16 = htonl(FW_LEN16(caps_cmd));
+ caps_cmd.cfvalid_to_len16 = htonl(FW_LEN16(caps_cmd));
ret = t4_wr_mbox(adap, adap->mbox, &caps_cmd, sizeof(caps_cmd),
&caps_cmd);
if (ret < 0)
diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c
index 3ecc087d732..fe9a2ea3588 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c
@@ -508,7 +508,7 @@ static inline void ring_fl_db(struct adapter *adap, struct sge_fl *q)
{
if (q->pend_cred >= 8) {
wmb();
- t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL), DBPRIO |
+ t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL), DBPRIO(1) |
QID(q->cntxt_id) | PIDX(q->pend_cred / 8));
q->pend_cred &= 7;
}
@@ -2082,10 +2082,10 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq,
goto fl_nomem;
flsz = fl->size / 8 + s->stat_len / sizeof(struct tx_desc);
- c.iqns_to_fl0congen = htonl(FW_IQ_CMD_FL0PACKEN |
+ c.iqns_to_fl0congen = htonl(FW_IQ_CMD_FL0PACKEN(1) |
FW_IQ_CMD_FL0FETCHRO(1) |
FW_IQ_CMD_FL0DATARO(1) |
- FW_IQ_CMD_FL0PADEN);
+ FW_IQ_CMD_FL0PADEN(1));
c.fl0dcaen_to_fl0cidxfthresh = htons(FW_IQ_CMD_FL0FBMIN(2) |
FW_IQ_CMD_FL0FBMAX(3));
c.fl0size = htons(flsz);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
index 45f2bea2e92..8d9c7547b07 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
@@ -648,12 +648,12 @@ static int sf1_read(struct adapter *adapter, unsigned int byte_cnt, int cont,
if (!byte_cnt || byte_cnt > 4)
return -EINVAL;
- if (t4_read_reg(adapter, SF_OP) & BUSY)
+ if (t4_read_reg(adapter, SF_OP) & SF_BUSY)
return -EBUSY;
cont = cont ? SF_CONT : 0;
lock = lock ? SF_LOCK : 0;
t4_write_reg(adapter, SF_OP, lock | cont | BYTECNT(byte_cnt - 1));
- ret = t4_wait_op_done(adapter, SF_OP, BUSY, 0, SF_ATTEMPTS, 5);
+ ret = t4_wait_op_done(adapter, SF_OP, SF_BUSY, 0, SF_ATTEMPTS, 5);
if (!ret)
*valp = t4_read_reg(adapter, SF_DATA);
return ret;
@@ -676,14 +676,14 @@ static int sf1_write(struct adapter *adapter, unsigned int byte_cnt, int cont,
{
if (!byte_cnt || byte_cnt > 4)
return -EINVAL;
- if (t4_read_reg(adapter, SF_OP) & BUSY)
+ if (t4_read_reg(adapter, SF_OP) & SF_BUSY)
return -EBUSY;
cont = cont ? SF_CONT : 0;
lock = lock ? SF_LOCK : 0;
t4_write_reg(adapter, SF_DATA, val);
t4_write_reg(adapter, SF_OP, lock |
cont | BYTECNT(byte_cnt - 1) | OP_WR);
- return t4_wait_op_done(adapter, SF_OP, BUSY, 0, SF_ATTEMPTS, 5);
+ return t4_wait_op_done(adapter, SF_OP, SF_BUSY, 0, SF_ATTEMPTS, 5);
}
/**
@@ -2252,14 +2252,14 @@ int t4_wol_pat_enable(struct adapter *adap, unsigned int port, unsigned int map,
t4_write_reg(adap, EPIO_REG(DATA0), mask0);
t4_write_reg(adap, EPIO_REG(OP), ADDRESS(i) | EPIOWR);
t4_read_reg(adap, EPIO_REG(OP)); /* flush */
- if (t4_read_reg(adap, EPIO_REG(OP)) & BUSY)
+ if (t4_read_reg(adap, EPIO_REG(OP)) & SF_BUSY)
return -ETIMEDOUT;
/* write CRC */
t4_write_reg(adap, EPIO_REG(DATA0), crc);
t4_write_reg(adap, EPIO_REG(OP), ADDRESS(i + 32) | EPIOWR);
t4_read_reg(adap, EPIO_REG(OP)); /* flush */
- if (t4_read_reg(adap, EPIO_REG(OP)) & BUSY)
+ if (t4_read_reg(adap, EPIO_REG(OP)) & SF_BUSY)
return -ETIMEDOUT;
}
#undef EPIO_REG
@@ -2405,7 +2405,7 @@ int t4_fw_hello(struct adapter *adap, unsigned int mbox, unsigned int evt_mbox,
retry:
memset(&c, 0, sizeof(c));
INIT_CMD(c, HELLO, WRITE);
- c.err_to_mbasyncnot = htonl(
+ c.err_to_clearinit = htonl(
FW_HELLO_CMD_MASTERDIS(master == MASTER_CANT) |
FW_HELLO_CMD_MASTERFORCE(master == MASTER_MUST) |
FW_HELLO_CMD_MBMASTER(master == MASTER_MUST ? mbox :
@@ -2426,7 +2426,7 @@ retry:
return ret;
}
- v = ntohl(c.err_to_mbasyncnot);
+ v = ntohl(c.err_to_clearinit);
master_mbox = FW_HELLO_CMD_MBMASTER_GET(v);
if (state) {
if (v & FW_HELLO_CMD_ERR)
@@ -2774,7 +2774,7 @@ int t4_fw_config_file(struct adapter *adap, unsigned int mbox,
htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) |
FW_CMD_REQUEST |
FW_CMD_READ);
- caps_cmd.retval_len16 =
+ caps_cmd.cfvalid_to_len16 =
htonl(FW_CAPS_CONFIG_CMD_CFVALID |
FW_CAPS_CONFIG_CMD_MEMTYPE_CF(mtype) |
FW_CAPS_CONFIG_CMD_MEMADDR64K_CF(maddr >> 16) |
@@ -2797,7 +2797,7 @@ int t4_fw_config_file(struct adapter *adap, unsigned int mbox,
htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) |
FW_CMD_REQUEST |
FW_CMD_WRITE);
- caps_cmd.retval_len16 = htonl(FW_LEN16(caps_cmd));
+ caps_cmd.cfvalid_to_len16 = htonl(FW_LEN16(caps_cmd));
return t4_wr_mbox(adap, mbox, &caps_cmd, sizeof(caps_cmd), NULL);
}
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
index eb71b8250b9..b760808fd6d 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
@@ -658,6 +658,7 @@ struct ulptx_sgl {
__be32 cmd_nsge;
#define ULPTX_CMD(x) ((x) << 24)
#define ULPTX_NSGE(x) ((x) << 0)
+#define ULPTX_MORE (1U << 23)
__be32 len0;
__be64 addr0;
struct ulptx_sge_pair sge[0];
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
index a1a8b57200f..75393f5cff4 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
@@ -67,7 +67,7 @@
#define QID_MASK 0xffff8000U
#define QID_SHIFT 15
#define QID(x) ((x) << QID_SHIFT)
-#define DBPRIO 0x00004000U
+#define DBPRIO(x) ((x) << 14)
#define PIDX_MASK 0x00003fffU
#define PIDX_SHIFT 0
#define PIDX(x) ((x) << PIDX_SHIFT)
@@ -193,6 +193,12 @@
#define SGE_FL_BUFFER_SIZE1 0x1048
#define SGE_FL_BUFFER_SIZE2 0x104c
#define SGE_FL_BUFFER_SIZE3 0x1050
+#define SGE_FL_BUFFER_SIZE4 0x1054
+#define SGE_FL_BUFFER_SIZE5 0x1058
+#define SGE_FL_BUFFER_SIZE6 0x105c
+#define SGE_FL_BUFFER_SIZE7 0x1060
+#define SGE_FL_BUFFER_SIZE8 0x1064
+
#define SGE_INGRESS_RX_THRESHOLD 0x10a0
#define THRESHOLD_0_MASK 0x3f000000U
#define THRESHOLD_0_SHIFT 24
@@ -217,6 +223,17 @@
#define EGRTHRESHOLD(x) ((x) << EGRTHRESHOLDshift)
#define EGRTHRESHOLD_GET(x) (((x) & EGRTHRESHOLD_MASK) >> EGRTHRESHOLDshift)
+#define SGE_DBFIFO_STATUS 0x10a4
+#define HP_INT_THRESH_SHIFT 28
+#define HP_INT_THRESH_MASK 0xfU
+#define HP_INT_THRESH(x) ((x) << HP_INT_THRESH_SHIFT)
+#define LP_INT_THRESH_SHIFT 12
+#define LP_INT_THRESH_MASK 0xfU
+#define LP_INT_THRESH(x) ((x) << LP_INT_THRESH_SHIFT)
+
+#define SGE_DOORBELL_CONTROL 0x10a8
+#define ENABLE_DROP (1 << 13)
+
#define SGE_TIMER_VALUE_0_AND_1 0x10b8
#define TIMERVALUE0_MASK 0xffff0000U
#define TIMERVALUE0_SHIFT 16
@@ -277,6 +294,10 @@
#define A_SGE_CTXT_CMD 0x11fc
#define A_SGE_DBQ_CTXT_BADDR 0x1084
+#define PCIE_PF_CFG 0x40
+#define AIVEC(x) ((x) << 4)
+#define AIVEC_MASK 0x3ffU
+
#define PCIE_PF_CLI 0x44
#define PCIE_INT_CAUSE 0x3004
#define UNXSPLCPLERR 0x20000000U
@@ -322,6 +343,13 @@
#define PCIE_MEM_ACCESS_OFFSET 0x306c
#define PCIE_FW 0x30b8
+#define PCIE_FW_ERR 0x80000000U
+#define PCIE_FW_INIT 0x40000000U
+#define PCIE_FW_HALT 0x20000000U
+#define PCIE_FW_MASTER_VLD 0x00008000U
+#define PCIE_FW_MASTER(x) ((x) << 12)
+#define PCIE_FW_MASTER_MASK 0x7
+#define PCIE_FW_MASTER_GET(x) (((x) >> 12) & PCIE_FW_MASTER_MASK)
#define PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS 0x5908
#define RNPP 0x80000000U
@@ -432,6 +460,9 @@
#define MBOWNER(x) ((x) << MBOWNER_SHIFT)
#define MBOWNER_GET(x) (((x) & MBOWNER_MASK) >> MBOWNER_SHIFT)
+#define CIM_PF_HOST_INT_ENABLE 0x288
+#define MBMSGRDYINTEN(x) ((x) << 19)
+
#define CIM_PF_HOST_INT_CAUSE 0x28c
#define MBMSGRDYINT 0x00080000U
@@ -922,7 +953,7 @@
#define SF_DATA 0x193f8
#define SF_OP 0x193fc
-#define BUSY 0x80000000U
+#define SF_BUSY 0x80000000U
#define SF_LOCK 0x00000010U
#define SF_CONT 0x00000008U
#define BYTECNT_MASK 0x00000006U
@@ -981,6 +1012,7 @@
#define I2CM 0x00000002U
#define CIM 0x00000001U
+#define PL_INT_ENABLE 0x19410
#define PL_INT_MAP0 0x19414
#define PL_RST 0x19428
#define PIORST 0x00000002U
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h
index a6364632b49..0abc864cdd3 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h
@@ -68,6 +68,7 @@ struct fw_wr_hdr {
};
#define FW_WR_OP(x) ((x) << 24)
+#define FW_WR_OP_GET(x) (((x) >> 24) & 0xff)
#define FW_WR_ATOMIC(x) ((x) << 23)
#define FW_WR_FLUSH(x) ((x) << 22)
#define FW_WR_COMPL(x) ((x) << 21)
@@ -222,6 +223,7 @@ struct fw_cmd_hdr {
#define FW_CMD_OP(x) ((x) << 24)
#define FW_CMD_OP_GET(x) (((x) >> 24) & 0xff)
#define FW_CMD_REQUEST (1U << 23)
+#define FW_CMD_REQUEST_GET(x) (((x) >> 23) & 0x1)
#define FW_CMD_READ (1U << 22)
#define FW_CMD_WRITE (1U << 21)
#define FW_CMD_EXEC (1U << 20)
@@ -229,6 +231,7 @@ struct fw_cmd_hdr {
#define FW_CMD_RETVAL(x) ((x) << 8)
#define FW_CMD_RETVAL_GET(x) (((x) >> 8) & 0xff)
#define FW_CMD_LEN16(x) ((x) << 0)
+#define FW_LEN16(fw_struct) FW_CMD_LEN16(sizeof(fw_struct) / 16)
enum fw_ldst_addrspc {
FW_LDST_ADDRSPC_FIRMWARE = 0x0001,
@@ -241,7 +244,8 @@ enum fw_ldst_addrspc {
FW_LDST_ADDRSPC_TP_MIB = 0x0012,
FW_LDST_ADDRSPC_MDIO = 0x0018,
FW_LDST_ADDRSPC_MPS = 0x0020,
- FW_LDST_ADDRSPC_FUNC = 0x0028
+ FW_LDST_ADDRSPC_FUNC = 0x0028,
+ FW_LDST_ADDRSPC_FUNC_PCIE = 0x0029,
};
enum fw_ldst_mps_fid {
@@ -303,6 +307,16 @@ struct fw_ldst_cmd {
__be64 data0;
__be64 data1;
} func;
+ struct fw_ldst_pcie {
+ u8 ctrl_to_fn;
+ u8 bnum;
+ u8 r;
+ u8 ext_r;
+ u8 select_naccess;
+ u8 pcie_fn;
+ __be16 nset_pkd;
+ __be32 data[12];
+ } pcie;
} u;
};
@@ -312,6 +326,9 @@ struct fw_ldst_cmd {
#define FW_LDST_CMD_FID(x) ((x) << 15)
#define FW_LDST_CMD_CTL(x) ((x) << 0)
#define FW_LDST_CMD_RPLCPF(x) ((x) << 0)
+#define FW_LDST_CMD_LC (1U << 4)
+#define FW_LDST_CMD_NACCESS(x) ((x) << 0)
+#define FW_LDST_CMD_FN(x) ((x) << 0)
struct fw_reset_cmd {
__be32 op_to_write;
@@ -333,7 +350,7 @@ enum fw_hellow_cmd {
struct fw_hello_cmd {
__be32 op_to_write;
__be32 retval_len16;
- __be32 err_to_mbasyncnot;
+ __be32 err_to_clearinit;
#define FW_HELLO_CMD_ERR (1U << 31)
#define FW_HELLO_CMD_INIT (1U << 30)
#define FW_HELLO_CMD_MASTERDIS(x) ((x) << 29)
@@ -343,6 +360,7 @@ struct fw_hello_cmd {
#define FW_HELLO_CMD_MBMASTER(x) ((x) << FW_HELLO_CMD_MBMASTER_SHIFT)
#define FW_HELLO_CMD_MBMASTER_GET(x) \
(((x) >> FW_HELLO_CMD_MBMASTER_SHIFT) & FW_HELLO_CMD_MBMASTER_MASK)
+#define FW_HELLO_CMD_MBASYNCNOTINT(x) ((x) << 23)
#define FW_HELLO_CMD_MBASYNCNOT(x) ((x) << 20)
#define FW_HELLO_CMD_STAGE(x) ((x) << 17)
#define FW_HELLO_CMD_CLEARINIT (1U << 16)
@@ -428,6 +446,7 @@ enum fw_caps_config_iscsi {
enum fw_caps_config_fcoe {
FW_CAPS_CONFIG_FCOE_INITIATOR = 0x00000001,
FW_CAPS_CONFIG_FCOE_TARGET = 0x00000002,
+ FW_CAPS_CONFIG_FCOE_CTRL_OFLD = 0x00000004,
};
enum fw_memtype_cf {
@@ -440,7 +459,7 @@ enum fw_memtype_cf {
struct fw_caps_config_cmd {
__be32 op_to_write;
- __be32 retval_len16;
+ __be32 cfvalid_to_len16;
__be32 r2;
__be32 hwmbitmap;
__be16 nbmcaps;
@@ -701,8 +720,8 @@ struct fw_iq_cmd {
#define FW_IQ_CMD_FL0FETCHRO(x) ((x) << 6)
#define FW_IQ_CMD_FL0HOSTFCMODE(x) ((x) << 4)
#define FW_IQ_CMD_FL0CPRIO(x) ((x) << 3)
-#define FW_IQ_CMD_FL0PADEN (1U << 2)
-#define FW_IQ_CMD_FL0PACKEN (1U << 1)
+#define FW_IQ_CMD_FL0PADEN(x) ((x) << 2)
+#define FW_IQ_CMD_FL0PACKEN(x) ((x) << 1)
#define FW_IQ_CMD_FL0CONGEN (1U << 0)
#define FW_IQ_CMD_FL0DCAEN(x) ((x) << 15)
@@ -1190,6 +1209,14 @@ enum fw_port_dcb_cfg_rc {
FW_PORT_DCB_CFG_ERROR = 0x1
};
+enum fw_port_dcb_type {
+ FW_PORT_DCB_TYPE_PGID = 0x00,
+ FW_PORT_DCB_TYPE_PGRATE = 0x01,
+ FW_PORT_DCB_TYPE_PRIORATE = 0x02,
+ FW_PORT_DCB_TYPE_PFC = 0x03,
+ FW_PORT_DCB_TYPE_APP_ID = 0x04,
+};
+
struct fw_port_cmd {
__be32 op_to_portid;
__be32 action_to_len16;
@@ -1257,6 +1284,7 @@ struct fw_port_cmd {
#define FW_PORT_CMD_TXIPG(x) ((x) << 19)
#define FW_PORT_CMD_LSTATUS (1U << 31)
+#define FW_PORT_CMD_LSTATUS_GET(x) (((x) >> 31) & 0x1)
#define FW_PORT_CMD_LSPEED(x) ((x) << 24)
#define FW_PORT_CMD_LSPEED_GET(x) (((x) >> 24) & 0x3f)
#define FW_PORT_CMD_TXPAUSE (1U << 23)
@@ -1305,6 +1333,9 @@ enum fw_port_module_type {
FW_PORT_MOD_TYPE_TWINAX_PASSIVE,
FW_PORT_MOD_TYPE_TWINAX_ACTIVE,
FW_PORT_MOD_TYPE_LRM,
+ FW_PORT_MOD_TYPE_ERROR = FW_PORT_CMD_MODTYPE_MASK - 3,
+ FW_PORT_MOD_TYPE_UNKNOWN = FW_PORT_CMD_MODTYPE_MASK - 2,
+ FW_PORT_MOD_TYPE_NOTSUPPORTED = FW_PORT_CMD_MODTYPE_MASK - 1,
FW_PORT_MOD_TYPE_NONE = FW_PORT_CMD_MODTYPE_MASK
};
diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
index f16745f4b36..92170d50d9d 100644
--- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
@@ -536,7 +536,7 @@ static inline void ring_fl_db(struct adapter *adapter, struct sge_fl *fl)
if (fl->pend_cred >= FL_PER_EQ_UNIT) {
wmb();
t4_write_reg(adapter, T4VF_SGE_BASE_ADDR + SGE_VF_KDOORBELL,
- DBPRIO |
+ DBPRIO(1) |
QID(fl->cntxt_id) |
PIDX(fl->pend_cred / FL_PER_EQ_UNIT));
fl->pend_cred %= FL_PER_EQ_UNIT;
@@ -952,7 +952,7 @@ static inline void ring_tx_db(struct adapter *adapter, struct sge_txq *tq,
* Warn if we write doorbells with the wrong priority and write
* descriptors before telling HW.
*/
- WARN_ON((QID(tq->cntxt_id) | PIDX(n)) & DBPRIO);
+ WARN_ON((QID(tq->cntxt_id) | PIDX(n)) & DBPRIO(1));
wmb();
t4_write_reg(adapter, T4VF_SGE_BASE_ADDR + SGE_VF_KDOORBELL,
QID(tq->cntxt_id) | PIDX(n));
@@ -2126,8 +2126,8 @@ int t4vf_sge_alloc_rxq(struct adapter *adapter, struct sge_rspq *rspq,
cmd.iqns_to_fl0congen =
cpu_to_be32(
FW_IQ_CMD_FL0HOSTFCMODE(SGE_HOSTFCMODE_NONE) |
- FW_IQ_CMD_FL0PACKEN |
- FW_IQ_CMD_FL0PADEN);
+ FW_IQ_CMD_FL0PACKEN(1) |
+ FW_IQ_CMD_FL0PADEN(1));
cmd.fl0dcaen_to_fl0cidxfthresh =
cpu_to_be16(
FW_IQ_CMD_FL0FBMIN(SGE_FETCHBURSTMIN_64B) |
diff --git a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c
index 9089d00f142..14e30515f6a 100644
--- a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c
@@ -1671,7 +1671,7 @@ static void e1000_get_wol(struct net_device *netdev,
/* apply any specific unsupported masks here */
switch (hw->device_id) {
case E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3:
- /* KSP3 does not suppport UCAST wake-ups */
+ /* KSP3 does not support UCAST wake-ups */
wol->supported &= ~WAKE_UCAST;
if (adapter->wol & E1000_WUFC_EX)
diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig
index 0029934748b..edfba937092 100644
--- a/drivers/net/ethernet/marvell/Kconfig
+++ b/drivers/net/ethernet/marvell/Kconfig
@@ -31,6 +31,30 @@ config MV643XX_ETH
Some boards that use the Discovery chipset are the Momenco
Ocelot C and Jaguar ATX and Pegasos II.
+config MVMDIO
+ tristate "Marvell MDIO interface support"
+ ---help---
+ This driver supports the MDIO interface found in the network
+ interface units of the Marvell EBU SoCs (Kirkwood, Orion5x,
+ Dove, Armada 370 and Armada XP).
+
+ For now, this driver is only needed for the MVNETA driver
+ (used on Armada 370 and XP), but it could be used in the
+ future by the MV643XX_ETH driver.
+
+config MVNETA
+ tristate "Marvell Armada 370/XP network interface support"
+ depends on MACH_ARMADA_370_XP
+ select PHYLIB
+ select MVMDIO
+ ---help---
+ This driver supports the network interface units in the
+ Marvell ARMADA XP and ARMADA 370 SoC family.
+
+ Note that this driver is distinct from the mv643xx_eth
+ driver, which should be used for the older Marvell SoCs
+ (Dove, Orion, Discovery, Kirkwood).
+
config PXA168_ETH
tristate "Marvell pxa168 ethernet support"
depends on CPU_PXA168
diff --git a/drivers/net/ethernet/marvell/Makefile b/drivers/net/ethernet/marvell/Makefile
index 57e3234a37b..7f63b4aac43 100644
--- a/drivers/net/ethernet/marvell/Makefile
+++ b/drivers/net/ethernet/marvell/Makefile
@@ -3,6 +3,8 @@
#
obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
+obj-$(CONFIG_MVMDIO) += mvmdio.o
+obj-$(CONFIG_MVNETA) += mvneta.o
obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
obj-$(CONFIG_SKGE) += skge.o
obj-$(CONFIG_SKY2) += sky2.o
diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c
new file mode 100644
index 00000000000..6d6002bab06
--- /dev/null
+++ b/drivers/net/ethernet/marvell/mvmdio.c
@@ -0,0 +1,228 @@
+/*
+ * Driver for the MDIO interface of Marvell network interfaces.
+ *
+ * Since the MDIO interface of Marvell network interfaces is shared
+ * between all network interfaces, having a single driver allows to
+ * handle concurrent accesses properly (you may have four Ethernet
+ * ports, but they in fact share the same SMI interface to access the
+ * MDIO bus). Moreover, this MDIO interface code is similar between
+ * the mv643xx_eth driver and the mvneta driver. For now, it is only
+ * used by the mvneta driver, but it could later be used by the
+ * mv643xx_eth driver as well.
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/phy.h>
+#include <linux/of_address.h>
+#include <linux/of_mdio.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+
+#define MVMDIO_SMI_DATA_SHIFT 0
+#define MVMDIO_SMI_PHY_ADDR_SHIFT 16
+#define MVMDIO_SMI_PHY_REG_SHIFT 21
+#define MVMDIO_SMI_READ_OPERATION BIT(26)
+#define MVMDIO_SMI_WRITE_OPERATION 0
+#define MVMDIO_SMI_READ_VALID BIT(27)
+#define MVMDIO_SMI_BUSY BIT(28)
+
+struct orion_mdio_dev {
+ struct mutex lock;
+ void __iomem *smireg;
+};
+
+/* Wait for the SMI unit to be ready for another operation
+ */
+static int orion_mdio_wait_ready(struct mii_bus *bus)
+{
+ struct orion_mdio_dev *dev = bus->priv;
+ int count;
+ u32 val;
+
+ count = 0;
+ while (1) {
+ val = readl(dev->smireg);
+ if (!(val & MVMDIO_SMI_BUSY))
+ break;
+
+ if (count > 100) {
+ dev_err(bus->parent, "Timeout: SMI busy for too long\n");
+ return -ETIMEDOUT;
+ }
+
+ udelay(10);
+ count++;
+ }
+
+ return 0;
+}
+
+static int orion_mdio_read(struct mii_bus *bus, int mii_id,
+ int regnum)
+{
+ struct orion_mdio_dev *dev = bus->priv;
+ int count;
+ u32 val;
+ int ret;
+
+ mutex_lock(&dev->lock);
+
+ ret = orion_mdio_wait_ready(bus);
+ if (ret < 0) {
+ mutex_unlock(&dev->lock);
+ return ret;
+ }
+
+ writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) |
+ (regnum << MVMDIO_SMI_PHY_REG_SHIFT) |
+ MVMDIO_SMI_READ_OPERATION),
+ dev->smireg);
+
+ /* Wait for the value to become available */
+ count = 0;
+ while (1) {
+ val = readl(dev->smireg);
+ if (val & MVMDIO_SMI_READ_VALID)
+ break;
+
+ if (count > 100) {
+ dev_err(bus->parent, "Timeout when reading PHY\n");
+ mutex_unlock(&dev->lock);
+ return -ETIMEDOUT;
+ }
+
+ udelay(10);
+ count++;
+ }
+
+ mutex_unlock(&dev->lock);
+
+ return val & 0xFFFF;
+}
+
+static int orion_mdio_write(struct mii_bus *bus, int mii_id,
+ int regnum, u16 value)
+{
+ struct orion_mdio_dev *dev = bus->priv;
+ int ret;
+
+ mutex_lock(&dev->lock);
+
+ ret = orion_mdio_wait_ready(bus);
+ if (ret < 0) {
+ mutex_unlock(&dev->lock);
+ return ret;
+ }
+
+ writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) |
+ (regnum << MVMDIO_SMI_PHY_REG_SHIFT) |
+ MVMDIO_SMI_WRITE_OPERATION |
+ (value << MVMDIO_SMI_DATA_SHIFT)),
+ dev->smireg);
+
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+static int orion_mdio_reset(struct mii_bus *bus)
+{
+ return 0;
+}
+
+static int __devinit orion_mdio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct mii_bus *bus;
+ struct orion_mdio_dev *dev;
+ int i, ret;
+
+ bus = mdiobus_alloc_size(sizeof(struct orion_mdio_dev));
+ if (!bus) {
+ dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
+ return -ENOMEM;
+ }
+
+ bus->name = "orion_mdio_bus";
+ bus->read = orion_mdio_read;
+ bus->write = orion_mdio_write;
+ bus->reset = orion_mdio_reset;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii",
+ dev_name(&pdev->dev));
+ bus->parent = &pdev->dev;
+
+ bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+ if (!bus->irq) {
+ dev_err(&pdev->dev, "Cannot allocate PHY IRQ array\n");
+ mdiobus_free(bus);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < PHY_MAX_ADDR; i++)
+ bus->irq[i] = PHY_POLL;
+
+ dev = bus->priv;
+ dev->smireg = of_iomap(pdev->dev.of_node, 0);
+ if (!dev->smireg) {
+ dev_err(&pdev->dev, "No SMI register address given in DT\n");
+ kfree(bus->irq);
+ mdiobus_free(bus);
+ return -ENODEV;
+ }
+
+ mutex_init(&dev->lock);
+
+ ret = of_mdiobus_register(bus, np);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
+ iounmap(dev->smireg);
+ kfree(bus->irq);
+ mdiobus_free(bus);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, bus);
+
+ return 0;
+}
+
+static int __devexit orion_mdio_remove(struct platform_device *pdev)
+{
+ struct mii_bus *bus = platform_get_drvdata(pdev);
+ mdiobus_unregister(bus);
+ kfree(bus->irq);
+ mdiobus_free(bus);
+ return 0;
+}
+
+static const struct of_device_id orion_mdio_match[] = {
+ { .compatible = "marvell,orion-mdio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, orion_mdio_match);
+
+static struct platform_driver orion_mdio_driver = {
+ .probe = orion_mdio_probe,
+ .remove = __devexit_p(orion_mdio_remove),
+ .driver = {
+ .name = "orion-mdio",
+ .of_match_table = orion_mdio_match,
+ },
+};
+
+module_platform_driver(orion_mdio_driver);
+
+MODULE_DESCRIPTION("Marvell MDIO interface driver");
+MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
new file mode 100644
index 00000000000..3f8086b9f5e
--- /dev/null
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -0,0 +1,2848 @@
+/*
+ * Driver for Marvell NETA network card for Armada XP and Armada 370 SoCs.
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Rami Rosen <rosenr@marvell.com>
+ * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
+#include <linux/skbuff.h>
+#include <linux/inetdevice.h>
+#include <linux/mbus.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/of_address.h>
+#include <linux/phy.h>
+#include <linux/clk.h>
+
+/* Registers */
+#define MVNETA_RXQ_CONFIG_REG(q) (0x1400 + ((q) << 2))
+#define MVNETA_RXQ_HW_BUF_ALLOC BIT(1)
+#define MVNETA_RXQ_PKT_OFFSET_ALL_MASK (0xf << 8)
+#define MVNETA_RXQ_PKT_OFFSET_MASK(offs) ((offs) << 8)
+#define MVNETA_RXQ_THRESHOLD_REG(q) (0x14c0 + ((q) << 2))
+#define MVNETA_RXQ_NON_OCCUPIED(v) ((v) << 16)
+#define MVNETA_RXQ_BASE_ADDR_REG(q) (0x1480 + ((q) << 2))
+#define MVNETA_RXQ_SIZE_REG(q) (0x14a0 + ((q) << 2))
+#define MVNETA_RXQ_BUF_SIZE_SHIFT 19
+#define MVNETA_RXQ_BUF_SIZE_MASK (0x1fff << 19)
+#define MVNETA_RXQ_STATUS_REG(q) (0x14e0 + ((q) << 2))
+#define MVNETA_RXQ_OCCUPIED_ALL_MASK 0x3fff
+#define MVNETA_RXQ_STATUS_UPDATE_REG(q) (0x1500 + ((q) << 2))
+#define MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT 16
+#define MVNETA_RXQ_ADD_NON_OCCUPIED_MAX 255
+#define MVNETA_PORT_RX_RESET 0x1cc0
+#define MVNETA_PORT_RX_DMA_RESET BIT(0)
+#define MVNETA_PHY_ADDR 0x2000
+#define MVNETA_PHY_ADDR_MASK 0x1f
+#define MVNETA_MBUS_RETRY 0x2010
+#define MVNETA_UNIT_INTR_CAUSE 0x2080
+#define MVNETA_UNIT_CONTROL 0x20B0
+#define MVNETA_PHY_POLLING_ENABLE BIT(1)
+#define MVNETA_WIN_BASE(w) (0x2200 + ((w) << 3))
+#define MVNETA_WIN_SIZE(w) (0x2204 + ((w) << 3))
+#define MVNETA_WIN_REMAP(w) (0x2280 + ((w) << 2))
+#define MVNETA_BASE_ADDR_ENABLE 0x2290
+#define MVNETA_PORT_CONFIG 0x2400
+#define MVNETA_UNI_PROMISC_MODE BIT(0)
+#define MVNETA_DEF_RXQ(q) ((q) << 1)
+#define MVNETA_DEF_RXQ_ARP(q) ((q) << 4)
+#define MVNETA_TX_UNSET_ERR_SUM BIT(12)
+#define MVNETA_DEF_RXQ_TCP(q) ((q) << 16)
+#define MVNETA_DEF_RXQ_UDP(q) ((q) << 19)
+#define MVNETA_DEF_RXQ_BPDU(q) ((q) << 22)
+#define MVNETA_RX_CSUM_WITH_PSEUDO_HDR BIT(25)
+#define MVNETA_PORT_CONFIG_DEFL_VALUE(q) (MVNETA_DEF_RXQ(q) | \
+ MVNETA_DEF_RXQ_ARP(q) | \
+ MVNETA_DEF_RXQ_TCP(q) | \
+ MVNETA_DEF_RXQ_UDP(q) | \
+ MVNETA_DEF_RXQ_BPDU(q) | \
+ MVNETA_TX_UNSET_ERR_SUM | \
+ MVNETA_RX_CSUM_WITH_PSEUDO_HDR)
+#define MVNETA_PORT_CONFIG_EXTEND 0x2404
+#define MVNETA_MAC_ADDR_LOW 0x2414
+#define MVNETA_MAC_ADDR_HIGH 0x2418
+#define MVNETA_SDMA_CONFIG 0x241c
+#define MVNETA_SDMA_BRST_SIZE_16 4
+#define MVNETA_NO_DESC_SWAP 0x0
+#define MVNETA_RX_BRST_SZ_MASK(burst) ((burst) << 1)
+#define MVNETA_RX_NO_DATA_SWAP BIT(4)
+#define MVNETA_TX_NO_DATA_SWAP BIT(5)
+#define MVNETA_TX_BRST_SZ_MASK(burst) ((burst) << 22)
+#define MVNETA_PORT_STATUS 0x2444
+#define MVNETA_TX_IN_PRGRS BIT(1)
+#define MVNETA_TX_FIFO_EMPTY BIT(8)
+#define MVNETA_RX_MIN_FRAME_SIZE 0x247c
+#define MVNETA_TYPE_PRIO 0x24bc
+#define MVNETA_FORCE_UNI BIT(21)
+#define MVNETA_TXQ_CMD_1 0x24e4
+#define MVNETA_TXQ_CMD 0x2448
+#define MVNETA_TXQ_DISABLE_SHIFT 8
+#define MVNETA_TXQ_ENABLE_MASK 0x000000ff
+#define MVNETA_ACC_MODE 0x2500
+#define MVNETA_CPU_MAP(cpu) (0x2540 + ((cpu) << 2))
+#define MVNETA_CPU_RXQ_ACCESS_ALL_MASK 0x000000ff
+#define MVNETA_CPU_TXQ_ACCESS_ALL_MASK 0x0000ff00
+#define MVNETA_RXQ_TIME_COAL_REG(q) (0x2580 + ((q) << 2))
+#define MVNETA_INTR_NEW_CAUSE 0x25a0
+#define MVNETA_RX_INTR_MASK(nr_rxqs) (((1 << nr_rxqs) - 1) << 8)
+#define MVNETA_INTR_NEW_MASK 0x25a4
+#define MVNETA_INTR_OLD_CAUSE 0x25a8
+#define MVNETA_INTR_OLD_MASK 0x25ac
+#define MVNETA_INTR_MISC_CAUSE 0x25b0
+#define MVNETA_INTR_MISC_MASK 0x25b4
+#define MVNETA_INTR_ENABLE 0x25b8
+#define MVNETA_TXQ_INTR_ENABLE_ALL_MASK 0x0000ff00
+#define MVNETA_RXQ_INTR_ENABLE_ALL_MASK 0xff000000
+#define MVNETA_RXQ_CMD 0x2680
+#define MVNETA_RXQ_DISABLE_SHIFT 8
+#define MVNETA_RXQ_ENABLE_MASK 0x000000ff
+#define MVETH_TXQ_TOKEN_COUNT_REG(q) (0x2700 + ((q) << 4))
+#define MVETH_TXQ_TOKEN_CFG_REG(q) (0x2704 + ((q) << 4))
+#define MVNETA_GMAC_CTRL_0 0x2c00
+#define MVNETA_GMAC_MAX_RX_SIZE_SHIFT 2
+#define MVNETA_GMAC_MAX_RX_SIZE_MASK 0x7ffc
+#define MVNETA_GMAC0_PORT_ENABLE BIT(0)
+#define MVNETA_GMAC_CTRL_2 0x2c08
+#define MVNETA_GMAC2_PSC_ENABLE BIT(3)
+#define MVNETA_GMAC2_PORT_RGMII BIT(4)
+#define MVNETA_GMAC2_PORT_RESET BIT(6)
+#define MVNETA_GMAC_STATUS 0x2c10
+#define MVNETA_GMAC_LINK_UP BIT(0)
+#define MVNETA_GMAC_SPEED_1000 BIT(1)
+#define MVNETA_GMAC_SPEED_100 BIT(2)
+#define MVNETA_GMAC_FULL_DUPLEX BIT(3)
+#define MVNETA_GMAC_RX_FLOW_CTRL_ENABLE BIT(4)
+#define MVNETA_GMAC_TX_FLOW_CTRL_ENABLE BIT(5)
+#define MVNETA_GMAC_RX_FLOW_CTRL_ACTIVE BIT(6)
+#define MVNETA_GMAC_TX_FLOW_CTRL_ACTIVE BIT(7)
+#define MVNETA_GMAC_AUTONEG_CONFIG 0x2c0c
+#define MVNETA_GMAC_FORCE_LINK_DOWN BIT(0)
+#define MVNETA_GMAC_FORCE_LINK_PASS BIT(1)
+#define MVNETA_GMAC_CONFIG_MII_SPEED BIT(5)
+#define MVNETA_GMAC_CONFIG_GMII_SPEED BIT(6)
+#define MVNETA_GMAC_CONFIG_FULL_DUPLEX BIT(12)
+#define MVNETA_MIB_COUNTERS_BASE 0x3080
+#define MVNETA_MIB_LATE_COLLISION 0x7c
+#define MVNETA_DA_FILT_SPEC_MCAST 0x3400
+#define MVNETA_DA_FILT_OTH_MCAST 0x3500
+#define MVNETA_DA_FILT_UCAST_BASE 0x3600
+#define MVNETA_TXQ_BASE_ADDR_REG(q) (0x3c00 + ((q) << 2))
+#define MVNETA_TXQ_SIZE_REG(q) (0x3c20 + ((q) << 2))
+#define MVNETA_TXQ_SENT_THRESH_ALL_MASK 0x3fff0000
+#define MVNETA_TXQ_SENT_THRESH_MASK(coal) ((coal) << 16)
+#define MVNETA_TXQ_UPDATE_REG(q) (0x3c60 + ((q) << 2))
+#define MVNETA_TXQ_DEC_SENT_SHIFT 16
+#define MVNETA_TXQ_STATUS_REG(q) (0x3c40 + ((q) << 2))
+#define MVNETA_TXQ_SENT_DESC_SHIFT 16
+#define MVNETA_TXQ_SENT_DESC_MASK 0x3fff0000
+#define MVNETA_PORT_TX_RESET 0x3cf0
+#define MVNETA_PORT_TX_DMA_RESET BIT(0)
+#define MVNETA_TX_MTU 0x3e0c
+#define MVNETA_TX_TOKEN_SIZE 0x3e14
+#define MVNETA_TX_TOKEN_SIZE_MAX 0xffffffff
+#define MVNETA_TXQ_TOKEN_SIZE_REG(q) (0x3e40 + ((q) << 2))
+#define MVNETA_TXQ_TOKEN_SIZE_MAX 0x7fffffff
+
+#define MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK 0xff
+
+/* Descriptor ring Macros */
+#define MVNETA_QUEUE_NEXT_DESC(q, index) \
+ (((index) < (q)->last_desc) ? ((index) + 1) : 0)
+
+/* Various constants */
+
+/* Coalescing */
+#define MVNETA_TXDONE_COAL_PKTS 16
+#define MVNETA_RX_COAL_PKTS 32
+#define MVNETA_RX_COAL_USEC 100
+
+/* Timer */
+#define MVNETA_TX_DONE_TIMER_PERIOD 10
+
+/* Napi polling weight */
+#define MVNETA_RX_POLL_WEIGHT 64
+
+/* The two bytes Marvell header. Either contains a special value used
+ * by Marvell switches when a specific hardware mode is enabled (not
+ * supported by this driver) or is filled automatically by zeroes on
+ * the RX side. Those two bytes being at the front of the Ethernet
+ * header, they allow to have the IP header aligned on a 4 bytes
+ * boundary automatically: the hardware skips those two bytes on its
+ * own.
+ */
+#define MVNETA_MH_SIZE 2
+
+#define MVNETA_VLAN_TAG_LEN 4
+
+#define MVNETA_CPU_D_CACHE_LINE_SIZE 32
+#define MVNETA_TX_CSUM_MAX_SIZE 9800
+#define MVNETA_ACC_MODE_EXT 1
+
+/* Timeout constants */
+#define MVNETA_TX_DISABLE_TIMEOUT_MSEC 1000
+#define MVNETA_RX_DISABLE_TIMEOUT_MSEC 1000
+#define MVNETA_TX_FIFO_EMPTY_TIMEOUT 10000
+
+#define MVNETA_TX_MTU_MAX 0x3ffff
+
+/* Max number of Rx descriptors */
+#define MVNETA_MAX_RXD 128
+
+/* Max number of Tx descriptors */
+#define MVNETA_MAX_TXD 532
+
+/* descriptor aligned size */
+#define MVNETA_DESC_ALIGNED_SIZE 32
+
+#define MVNETA_RX_PKT_SIZE(mtu) \
+ ALIGN((mtu) + MVNETA_MH_SIZE + MVNETA_VLAN_TAG_LEN + \
+ ETH_HLEN + ETH_FCS_LEN, \
+ MVNETA_CPU_D_CACHE_LINE_SIZE)
+
+#define MVNETA_RX_BUF_SIZE(pkt_size) ((pkt_size) + NET_SKB_PAD)
+
+struct mvneta_stats {
+ struct u64_stats_sync syncp;
+ u64 packets;
+ u64 bytes;
+};
+
+struct mvneta_port {
+ int pkt_size;
+ void __iomem *base;
+ struct mvneta_rx_queue *rxqs;
+ struct mvneta_tx_queue *txqs;
+ struct timer_list tx_done_timer;
+ struct net_device *dev;
+
+ u32 cause_rx_tx;
+ struct napi_struct napi;
+
+ /* Flags */
+ unsigned long flags;
+#define MVNETA_F_TX_DONE_TIMER_BIT 0
+
+ /* Napi weight */
+ int weight;
+
+ /* Core clock */
+ struct clk *clk;
+ u8 mcast_count[256];
+ u16 tx_ring_size;
+ u16 rx_ring_size;
+ struct mvneta_stats tx_stats;
+ struct mvneta_stats rx_stats;
+
+ struct mii_bus *mii_bus;
+ struct phy_device *phy_dev;
+ phy_interface_t phy_interface;
+ struct device_node *phy_node;
+ unsigned int link;
+ unsigned int duplex;
+ unsigned int speed;
+};
+
+/* The mvneta_tx_desc and mvneta_rx_desc structures describe the
+ * layout of the transmit and reception DMA descriptors, and their
+ * layout is therefore defined by the hardware design
+ */
+struct mvneta_tx_desc {
+ u32 command; /* Options used by HW for packet transmitting.*/
+#define MVNETA_TX_L3_OFF_SHIFT 0
+#define MVNETA_TX_IP_HLEN_SHIFT 8
+#define MVNETA_TX_L4_UDP BIT(16)
+#define MVNETA_TX_L3_IP6 BIT(17)
+#define MVNETA_TXD_IP_CSUM BIT(18)
+#define MVNETA_TXD_Z_PAD BIT(19)
+#define MVNETA_TXD_L_DESC BIT(20)
+#define MVNETA_TXD_F_DESC BIT(21)
+#define MVNETA_TXD_FLZ_DESC (MVNETA_TXD_Z_PAD | \
+ MVNETA_TXD_L_DESC | \
+ MVNETA_TXD_F_DESC)
+#define MVNETA_TX_L4_CSUM_FULL BIT(30)
+#define MVNETA_TX_L4_CSUM_NOT BIT(31)
+
+ u16 reserverd1; /* csum_l4 (for future use) */
+ u16 data_size; /* Data size of transmitted packet in bytes */
+ u32 buf_phys_addr; /* Physical addr of transmitted buffer */
+ u32 reserved2; /* hw_cmd - (for future use, PMT) */
+ u32 reserved3[4]; /* Reserved - (for future use) */
+};
+
+struct mvneta_rx_desc {
+ u32 status; /* Info about received packet */
+#define MVNETA_RXD_ERR_CRC 0x0
+#define MVNETA_RXD_ERR_SUMMARY BIT(16)
+#define MVNETA_RXD_ERR_OVERRUN BIT(17)
+#define MVNETA_RXD_ERR_LEN BIT(18)
+#define MVNETA_RXD_ERR_RESOURCE (BIT(17) | BIT(18))
+#define MVNETA_RXD_ERR_CODE_MASK (BIT(17) | BIT(18))
+#define MVNETA_RXD_L3_IP4 BIT(25)
+#define MVNETA_RXD_FIRST_LAST_DESC (BIT(26) | BIT(27))
+#define MVNETA_RXD_L4_CSUM_OK BIT(30)
+
+ u16 reserved1; /* pnc_info - (for future use, PnC) */
+ u16 data_size; /* Size of received packet in bytes */
+ u32 buf_phys_addr; /* Physical address of the buffer */
+ u32 reserved2; /* pnc_flow_id (for future use, PnC) */
+ u32 buf_cookie; /* cookie for access to RX buffer in rx path */
+ u16 reserved3; /* prefetch_cmd, for future use */
+ u16 reserved4; /* csum_l4 - (for future use, PnC) */
+ u32 reserved5; /* pnc_extra PnC (for future use, PnC) */
+ u32 reserved6; /* hw_cmd (for future use, PnC and HWF) */
+};
+
+struct mvneta_tx_queue {
+ /* Number of this TX queue, in the range 0-7 */
+ u8 id;
+
+ /* Number of TX DMA descriptors in the descriptor ring */
+ int size;
+
+ /* Number of currently used TX DMA descriptor in the
+ * descriptor ring
+ */
+ int count;
+
+ /* Array of transmitted skb */
+ struct sk_buff **tx_skb;
+
+ /* Index of last TX DMA descriptor that was inserted */
+ int txq_put_index;
+
+ /* Index of the TX DMA descriptor to be cleaned up */
+ int txq_get_index;
+
+ u32 done_pkts_coal;
+
+ /* Virtual address of the TX DMA descriptors array */
+ struct mvneta_tx_desc *descs;
+
+ /* DMA address of the TX DMA descriptors array */
+ dma_addr_t descs_phys;
+
+ /* Index of the last TX DMA descriptor */
+ int last_desc;
+
+ /* Index of the next TX DMA descriptor to process */
+ int next_desc_to_proc;
+};
+
+struct mvneta_rx_queue {
+ /* rx queue number, in the range 0-7 */
+ u8 id;
+
+ /* num of rx descriptors in the rx descriptor ring */
+ int size;
+
+ /* counter of times when mvneta_refill() failed */
+ int missed;
+
+ u32 pkts_coal;
+ u32 time_coal;
+
+ /* Virtual address of the RX DMA descriptors array */
+ struct mvneta_rx_desc *descs;
+
+ /* DMA address of the RX DMA descriptors array */
+ dma_addr_t descs_phys;
+
+ /* Index of the last RX DMA descriptor */
+ int last_desc;
+
+ /* Index of the next RX DMA descriptor to process */
+ int next_desc_to_proc;
+};
+
+static int rxq_number = 8;
+static int txq_number = 8;
+
+static int rxq_def;
+static int txq_def;
+
+#define MVNETA_DRIVER_NAME "mvneta"
+#define MVNETA_DRIVER_VERSION "1.0"
+
+/* Utility/helper methods */
+
+/* Write helper method */
+static void mvreg_write(struct mvneta_port *pp, u32 offset, u32 data)
+{
+ writel(data, pp->base + offset);
+}
+
+/* Read helper method */
+static u32 mvreg_read(struct mvneta_port *pp, u32 offset)
+{
+ return readl(pp->base + offset);
+}
+
+/* Increment txq get counter */
+static void mvneta_txq_inc_get(struct mvneta_tx_queue *txq)
+{
+ txq->txq_get_index++;
+ if (txq->txq_get_index == txq->size)
+ txq->txq_get_index = 0;
+}
+
+/* Increment txq put counter */
+static void mvneta_txq_inc_put(struct mvneta_tx_queue *txq)
+{
+ txq->txq_put_index++;
+ if (txq->txq_put_index == txq->size)
+ txq->txq_put_index = 0;
+}
+
+
+/* Clear all MIB counters */
+static void mvneta_mib_counters_clear(struct mvneta_port *pp)
+{
+ int i;
+ u32 dummy;
+
+ /* Perform dummy reads from MIB counters */
+ for (i = 0; i < MVNETA_MIB_LATE_COLLISION; i += 4)
+ dummy = mvreg_read(pp, (MVNETA_MIB_COUNTERS_BASE + i));
+}
+
+/* Get System Network Statistics */
+struct rtnl_link_stats64 *mvneta_get_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+ unsigned int start;
+
+ memset(stats, 0, sizeof(struct rtnl_link_stats64));
+
+ do {
+ start = u64_stats_fetch_begin_bh(&pp->rx_stats.syncp);
+ stats->rx_packets = pp->rx_stats.packets;
+ stats->rx_bytes = pp->rx_stats.bytes;
+ } while (u64_stats_fetch_retry_bh(&pp->rx_stats.syncp, start));
+
+
+ do {
+ start = u64_stats_fetch_begin_bh(&pp->tx_stats.syncp);
+ stats->tx_packets = pp->tx_stats.packets;
+ stats->tx_bytes = pp->tx_stats.bytes;
+ } while (u64_stats_fetch_retry_bh(&pp->tx_stats.syncp, start));
+
+ stats->rx_errors = dev->stats.rx_errors;
+ stats->rx_dropped = dev->stats.rx_dropped;
+
+ stats->tx_dropped = dev->stats.tx_dropped;
+
+ return stats;
+}
+
+/* Rx descriptors helper methods */
+
+/* Checks whether the given RX descriptor is both the first and the
+ * last descriptor for the RX packet. Each RX packet is currently
+ * received through a single RX descriptor, so not having each RX
+ * descriptor with its first and last bits set is an error
+ */
+static int mvneta_rxq_desc_is_first_last(struct mvneta_rx_desc *desc)
+{
+ return (desc->status & MVNETA_RXD_FIRST_LAST_DESC) ==
+ MVNETA_RXD_FIRST_LAST_DESC;
+}
+
+/* Add number of descriptors ready to receive new packets */
+static void mvneta_rxq_non_occup_desc_add(struct mvneta_port *pp,
+ struct mvneta_rx_queue *rxq,
+ int ndescs)
+{
+ /* Only MVNETA_RXQ_ADD_NON_OCCUPIED_MAX (255) descriptors can
+ * be added at once
+ */
+ while (ndescs > MVNETA_RXQ_ADD_NON_OCCUPIED_MAX) {
+ mvreg_write(pp, MVNETA_RXQ_STATUS_UPDATE_REG(rxq->id),
+ (MVNETA_RXQ_ADD_NON_OCCUPIED_MAX <<
+ MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT));
+ ndescs -= MVNETA_RXQ_ADD_NON_OCCUPIED_MAX;
+ }
+
+ mvreg_write(pp, MVNETA_RXQ_STATUS_UPDATE_REG(rxq->id),
+ (ndescs << MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT));
+}
+
+/* Get number of RX descriptors occupied by received packets */
+static int mvneta_rxq_busy_desc_num_get(struct mvneta_port *pp,
+ struct mvneta_rx_queue *rxq)
+{
+ u32 val;
+
+ val = mvreg_read(pp, MVNETA_RXQ_STATUS_REG(rxq->id));
+ return val & MVNETA_RXQ_OCCUPIED_ALL_MASK;
+}
+
+/* Update num of rx desc called upon return from rx path or
+ * from mvneta_rxq_drop_pkts().
+ */
+static void mvneta_rxq_desc_num_update(struct mvneta_port *pp,
+ struct mvneta_rx_queue *rxq,
+ int rx_done, int rx_filled)
+{
+ u32 val;
+
+ if ((rx_done <= 0xff) && (rx_filled <= 0xff)) {
+ val = rx_done |
+ (rx_filled << MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT);
+ mvreg_write(pp, MVNETA_RXQ_STATUS_UPDATE_REG(rxq->id), val);
+ return;
+ }
+
+ /* Only 255 descriptors can be added at once */
+ while ((rx_done > 0) || (rx_filled > 0)) {
+ if (rx_done <= 0xff) {
+ val = rx_done;
+ rx_done = 0;
+ } else {
+ val = 0xff;
+ rx_done -= 0xff;
+ }
+ if (rx_filled <= 0xff) {
+ val |= rx_filled << MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT;
+ rx_filled = 0;
+ } else {
+ val |= 0xff << MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT;
+ rx_filled -= 0xff;
+ }
+ mvreg_write(pp, MVNETA_RXQ_STATUS_UPDATE_REG(rxq->id), val);
+ }
+}
+
+/* Get pointer to next RX descriptor to be processed by SW */
+static struct mvneta_rx_desc *
+mvneta_rxq_next_desc_get(struct mvneta_rx_queue *rxq)
+{
+ int rx_desc = rxq->next_desc_to_proc;
+
+ rxq->next_desc_to_proc = MVNETA_QUEUE_NEXT_DESC(rxq, rx_desc);
+ return rxq->descs + rx_desc;
+}
+
+/* Change maximum receive size of the port. */
+static void mvneta_max_rx_size_set(struct mvneta_port *pp, int max_rx_size)
+{
+ u32 val;
+
+ val = mvreg_read(pp, MVNETA_GMAC_CTRL_0);
+ val &= ~MVNETA_GMAC_MAX_RX_SIZE_MASK;
+ val |= ((max_rx_size - MVNETA_MH_SIZE) / 2) <<
+ MVNETA_GMAC_MAX_RX_SIZE_SHIFT;
+ mvreg_write(pp, MVNETA_GMAC_CTRL_0, val);
+}
+
+
+/* Set rx queue offset */
+static void mvneta_rxq_offset_set(struct mvneta_port *pp,
+ struct mvneta_rx_queue *rxq,
+ int offset)
+{
+ u32 val;
+
+ val = mvreg_read(pp, MVNETA_RXQ_CONFIG_REG(rxq->id));
+ val &= ~MVNETA_RXQ_PKT_OFFSET_ALL_MASK;
+
+ /* Offset is in */
+ val |= MVNETA_RXQ_PKT_OFFSET_MASK(offset >> 3);
+ mvreg_write(pp, MVNETA_RXQ_CONFIG_REG(rxq->id), val);
+}
+
+
+/* Tx descriptors helper methods */
+
+/* Update HW with number of TX descriptors to be sent */
+static void mvneta_txq_pend_desc_add(struct mvneta_port *pp,
+ struct mvneta_tx_queue *txq,
+ int pend_desc)
+{
+ u32 val;
+
+ /* Only 255 descriptors can be added at once ; Assume caller
+ * process TX desriptors in quanta less than 256
+ */
+ val = pend_desc;
+ mvreg_write(pp, MVNETA_TXQ_UPDATE_REG(txq->id), val);
+}
+
+/* Get pointer to next TX descriptor to be processed (send) by HW */
+static struct mvneta_tx_desc *
+mvneta_txq_next_desc_get(struct mvneta_tx_queue *txq)
+{
+ int tx_desc = txq->next_desc_to_proc;
+
+ txq->next_desc_to_proc = MVNETA_QUEUE_NEXT_DESC(txq, tx_desc);
+ return txq->descs + tx_desc;
+}
+
+/* Release the last allocated TX descriptor. Useful to handle DMA
+ * mapping failures in the TX path.
+ */
+static void mvneta_txq_desc_put(struct mvneta_tx_queue *txq)
+{
+ if (txq->next_desc_to_proc == 0)
+ txq->next_desc_to_proc = txq->last_desc - 1;
+ else
+ txq->next_desc_to_proc--;
+}
+
+/* Set rxq buf size */
+static void mvneta_rxq_buf_size_set(struct mvneta_port *pp,
+ struct mvneta_rx_queue *rxq,
+ int buf_size)
+{
+ u32 val;
+
+ val = mvreg_read(pp, MVNETA_RXQ_SIZE_REG(rxq->id));
+
+ val &= ~MVNETA_RXQ_BUF_SIZE_MASK;
+ val |= ((buf_size >> 3) << MVNETA_RXQ_BUF_SIZE_SHIFT);
+
+ mvreg_write(pp, MVNETA_RXQ_SIZE_REG(rxq->id), val);
+}
+
+/* Disable buffer management (BM) */
+static void mvneta_rxq_bm_disable(struct mvneta_port *pp,
+ struct mvneta_rx_queue *rxq)
+{
+ u32 val;
+
+ val = mvreg_read(pp, MVNETA_RXQ_CONFIG_REG(rxq->id));
+ val &= ~MVNETA_RXQ_HW_BUF_ALLOC;
+ mvreg_write(pp, MVNETA_RXQ_CONFIG_REG(rxq->id), val);
+}
+
+
+
+/* Sets the RGMII Enable bit (RGMIIEn) in port MAC control register */
+static void __devinit mvneta_gmac_rgmii_set(struct mvneta_port *pp, int enable)
+{
+ u32 val;
+
+ val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
+
+ if (enable)
+ val |= MVNETA_GMAC2_PORT_RGMII;
+ else
+ val &= ~MVNETA_GMAC2_PORT_RGMII;
+
+ mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
+}
+
+/* Config SGMII port */
+static void __devinit mvneta_port_sgmii_config(struct mvneta_port *pp)
+{
+ u32 val;
+
+ val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
+ val |= MVNETA_GMAC2_PSC_ENABLE;
+ mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
+}
+
+/* Start the Ethernet port RX and TX activity */
+static void mvneta_port_up(struct mvneta_port *pp)
+{
+ int queue;
+ u32 q_map;
+
+ /* Enable all initialized TXs. */
+ mvneta_mib_counters_clear(pp);
+ q_map = 0;
+ for (queue = 0; queue < txq_number; queue++) {
+ struct mvneta_tx_queue *txq = &pp->txqs[queue];
+ if (txq->descs != NULL)
+ q_map |= (1 << queue);
+ }
+ mvreg_write(pp, MVNETA_TXQ_CMD, q_map);
+
+ /* Enable all initialized RXQs. */
+ q_map = 0;
+ for (queue = 0; queue < rxq_number; queue++) {
+ struct mvneta_rx_queue *rxq = &pp->rxqs[queue];
+ if (rxq->descs != NULL)
+ q_map |= (1 << queue);
+ }
+
+ mvreg_write(pp, MVNETA_RXQ_CMD, q_map);
+}
+
+/* Stop the Ethernet port activity */
+static void mvneta_port_down(struct mvneta_port *pp)
+{
+ u32 val;
+ int count;
+
+ /* Stop Rx port activity. Check port Rx activity. */
+ val = mvreg_read(pp, MVNETA_RXQ_CMD) & MVNETA_RXQ_ENABLE_MASK;
+
+ /* Issue stop command for active channels only */
+ if (val != 0)
+ mvreg_write(pp, MVNETA_RXQ_CMD,
+ val << MVNETA_RXQ_DISABLE_SHIFT);
+
+ /* Wait for all Rx activity to terminate. */
+ count = 0;
+ do {
+ if (count++ >= MVNETA_RX_DISABLE_TIMEOUT_MSEC) {
+ netdev_warn(pp->dev,
+ "TIMEOUT for RX stopped ! rx_queue_cmd: 0x08%x\n",
+ val);
+ break;
+ }
+ mdelay(1);
+
+ val = mvreg_read(pp, MVNETA_RXQ_CMD);
+ } while (val & 0xff);
+
+ /* Stop Tx port activity. Check port Tx activity. Issue stop
+ * command for active channels only
+ */
+ val = (mvreg_read(pp, MVNETA_TXQ_CMD)) & MVNETA_TXQ_ENABLE_MASK;
+
+ if (val != 0)
+ mvreg_write(pp, MVNETA_TXQ_CMD,
+ (val << MVNETA_TXQ_DISABLE_SHIFT));
+
+ /* Wait for all Tx activity to terminate. */
+ count = 0;
+ do {
+ if (count++ >= MVNETA_TX_DISABLE_TIMEOUT_MSEC) {
+ netdev_warn(pp->dev,
+ "TIMEOUT for TX stopped status=0x%08x\n",
+ val);
+ break;
+ }
+ mdelay(1);
+
+ /* Check TX Command reg that all Txqs are stopped */
+ val = mvreg_read(pp, MVNETA_TXQ_CMD);
+
+ } while (val & 0xff);
+
+ /* Double check to verify that TX FIFO is empty */
+ count = 0;
+ do {
+ if (count++ >= MVNETA_TX_FIFO_EMPTY_TIMEOUT) {
+ netdev_warn(pp->dev,
+ "TX FIFO empty timeout status=0x08%x\n",
+ val);
+ break;
+ }
+ mdelay(1);
+
+ val = mvreg_read(pp, MVNETA_PORT_STATUS);
+ } while (!(val & MVNETA_TX_FIFO_EMPTY) &&
+ (val & MVNETA_TX_IN_PRGRS));
+
+ udelay(200);
+}
+
+/* Enable the port by setting the port enable bit of the MAC control register */
+static void mvneta_port_enable(struct mvneta_port *pp)
+{
+ u32 val;
+
+ /* Enable port */
+ val = mvreg_read(pp, MVNETA_GMAC_CTRL_0);
+ val |= MVNETA_GMAC0_PORT_ENABLE;
+ mvreg_write(pp, MVNETA_GMAC_CTRL_0, val);
+}
+
+/* Disable the port and wait for about 200 usec before retuning */
+static void mvneta_port_disable(struct mvneta_port *pp)
+{
+ u32 val;
+
+ /* Reset the Enable bit in the Serial Control Register */
+ val = mvreg_read(pp, MVNETA_GMAC_CTRL_0);
+ val &= ~MVNETA_GMAC0_PORT_ENABLE;
+ mvreg_write(pp, MVNETA_GMAC_CTRL_0, val);
+
+ udelay(200);
+}
+
+/* Multicast tables methods */
+
+/* Set all entries in Unicast MAC Table; queue==-1 means reject all */
+static void mvneta_set_ucast_table(struct mvneta_port *pp, int queue)
+{
+ int offset;
+ u32 val;
+
+ if (queue == -1) {
+ val = 0;
+ } else {
+ val = 0x1 | (queue << 1);
+ val |= (val << 24) | (val << 16) | (val << 8);
+ }
+
+ for (offset = 0; offset <= 0xc; offset += 4)
+ mvreg_write(pp, MVNETA_DA_FILT_UCAST_BASE + offset, val);
+}
+
+/* Set all entries in Special Multicast MAC Table; queue==-1 means reject all */
+static void mvneta_set_special_mcast_table(struct mvneta_port *pp, int queue)
+{
+ int offset;
+ u32 val;
+
+ if (queue == -1) {
+ val = 0;
+ } else {
+ val = 0x1 | (queue << 1);
+ val |= (val << 24) | (val << 16) | (val << 8);
+ }
+
+ for (offset = 0; offset <= 0xfc; offset += 4)
+ mvreg_write(pp, MVNETA_DA_FILT_SPEC_MCAST + offset, val);
+
+}
+
+/* Set all entries in Other Multicast MAC Table. queue==-1 means reject all */
+static void mvneta_set_other_mcast_table(struct mvneta_port *pp, int queue)
+{
+ int offset;
+ u32 val;
+
+ if (queue == -1) {
+ memset(pp->mcast_count, 0, sizeof(pp->mcast_count));
+ val = 0;
+ } else {
+ memset(pp->mcast_count, 1, sizeof(pp->mcast_count));
+ val = 0x1 | (queue << 1);
+ val |= (val << 24) | (val << 16) | (val << 8);
+ }
+
+ for (offset = 0; offset <= 0xfc; offset += 4)
+ mvreg_write(pp, MVNETA_DA_FILT_OTH_MCAST + offset, val);
+}
+
+/* This method sets defaults to the NETA port:
+ * Clears interrupt Cause and Mask registers.
+ * Clears all MAC tables.
+ * Sets defaults to all registers.
+ * Resets RX and TX descriptor rings.
+ * Resets PHY.
+ * This method can be called after mvneta_port_down() to return the port
+ * settings to defaults.
+ */
+static void mvneta_defaults_set(struct mvneta_port *pp)
+{
+ int cpu;
+ int queue;
+ u32 val;
+
+ /* Clear all Cause registers */
+ mvreg_write(pp, MVNETA_INTR_NEW_CAUSE, 0);
+ mvreg_write(pp, MVNETA_INTR_OLD_CAUSE, 0);
+ mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0);
+
+ /* Mask all interrupts */
+ mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
+ mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0);
+ mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0);
+ mvreg_write(pp, MVNETA_INTR_ENABLE, 0);
+
+ /* Enable MBUS Retry bit16 */
+ mvreg_write(pp, MVNETA_MBUS_RETRY, 0x20);
+
+ /* Set CPU queue access map - all CPUs have access to all RX
+ * queues and to all TX queues
+ */
+ for (cpu = 0; cpu < CONFIG_NR_CPUS; cpu++)
+ mvreg_write(pp, MVNETA_CPU_MAP(cpu),
+ (MVNETA_CPU_RXQ_ACCESS_ALL_MASK |
+ MVNETA_CPU_TXQ_ACCESS_ALL_MASK));
+
+ /* Reset RX and TX DMAs */
+ mvreg_write(pp, MVNETA_PORT_RX_RESET, MVNETA_PORT_RX_DMA_RESET);
+ mvreg_write(pp, MVNETA_PORT_TX_RESET, MVNETA_PORT_TX_DMA_RESET);
+
+ /* Disable Legacy WRR, Disable EJP, Release from reset */
+ mvreg_write(pp, MVNETA_TXQ_CMD_1, 0);
+ for (queue = 0; queue < txq_number; queue++) {
+ mvreg_write(pp, MVETH_TXQ_TOKEN_COUNT_REG(queue), 0);
+ mvreg_write(pp, MVETH_TXQ_TOKEN_CFG_REG(queue), 0);
+ }
+
+ mvreg_write(pp, MVNETA_PORT_TX_RESET, 0);
+ mvreg_write(pp, MVNETA_PORT_RX_RESET, 0);
+
+ /* Set Port Acceleration Mode */
+ val = MVNETA_ACC_MODE_EXT;
+ mvreg_write(pp, MVNETA_ACC_MODE, val);
+
+ /* Update val of portCfg register accordingly with all RxQueue types */
+ val = MVNETA_PORT_CONFIG_DEFL_VALUE(rxq_def);
+ mvreg_write(pp, MVNETA_PORT_CONFIG, val);
+
+ val = 0;
+ mvreg_write(pp, MVNETA_PORT_CONFIG_EXTEND, val);
+ mvreg_write(pp, MVNETA_RX_MIN_FRAME_SIZE, 64);
+
+ /* Build PORT_SDMA_CONFIG_REG */
+ val = 0;
+
+ /* Default burst size */
+ val |= MVNETA_TX_BRST_SZ_MASK(MVNETA_SDMA_BRST_SIZE_16);
+ val |= MVNETA_RX_BRST_SZ_MASK(MVNETA_SDMA_BRST_SIZE_16);
+
+ val |= (MVNETA_RX_NO_DATA_SWAP | MVNETA_TX_NO_DATA_SWAP |
+ MVNETA_NO_DESC_SWAP);
+
+ /* Assign port SDMA configuration */
+ mvreg_write(pp, MVNETA_SDMA_CONFIG, val);
+
+ mvneta_set_ucast_table(pp, -1);
+ mvneta_set_special_mcast_table(pp, -1);
+ mvneta_set_other_mcast_table(pp, -1);
+
+ /* Set port interrupt enable register - default enable all */
+ mvreg_write(pp, MVNETA_INTR_ENABLE,
+ (MVNETA_RXQ_INTR_ENABLE_ALL_MASK
+ | MVNETA_TXQ_INTR_ENABLE_ALL_MASK));
+}
+
+/* Set max sizes for tx queues */
+static void mvneta_txq_max_tx_size_set(struct mvneta_port *pp, int max_tx_size)
+
+{
+ u32 val, size, mtu;
+ int queue;
+
+ mtu = max_tx_size * 8;
+ if (mtu > MVNETA_TX_MTU_MAX)
+ mtu = MVNETA_TX_MTU_MAX;
+
+ /* Set MTU */
+ val = mvreg_read(pp, MVNETA_TX_MTU);
+ val &= ~MVNETA_TX_MTU_MAX;
+ val |= mtu;
+ mvreg_write(pp, MVNETA_TX_MTU, val);
+
+ /* TX token size and all TXQs token size must be larger that MTU */
+ val = mvreg_read(pp, MVNETA_TX_TOKEN_SIZE);
+
+ size = val & MVNETA_TX_TOKEN_SIZE_MAX;
+ if (size < mtu) {
+ size = mtu;
+ val &= ~MVNETA_TX_TOKEN_SIZE_MAX;
+ val |= size;
+ mvreg_write(pp, MVNETA_TX_TOKEN_SIZE, val);
+ }
+ for (queue = 0; queue < txq_number; queue++) {
+ val = mvreg_read(pp, MVNETA_TXQ_TOKEN_SIZE_REG(queue));
+
+ size = val & MVNETA_TXQ_TOKEN_SIZE_MAX;
+ if (size < mtu) {
+ size = mtu;
+ val &= ~MVNETA_TXQ_TOKEN_SIZE_MAX;
+ val |= size;
+ mvreg_write(pp, MVNETA_TXQ_TOKEN_SIZE_REG(queue), val);
+ }
+ }
+}
+
+/* Set unicast address */
+static void mvneta_set_ucast_addr(struct mvneta_port *pp, u8 last_nibble,
+ int queue)
+{
+ unsigned int unicast_reg;
+ unsigned int tbl_offset;
+ unsigned int reg_offset;
+
+ /* Locate the Unicast table entry */
+ last_nibble = (0xf & last_nibble);
+
+ /* offset from unicast tbl base */
+ tbl_offset = (last_nibble / 4) * 4;
+
+ /* offset within the above reg */
+ reg_offset = last_nibble % 4;
+
+ unicast_reg = mvreg_read(pp, (MVNETA_DA_FILT_UCAST_BASE + tbl_offset));
+
+ if (queue == -1) {
+ /* Clear accepts frame bit at specified unicast DA tbl entry */
+ unicast_reg &= ~(0xff << (8 * reg_offset));
+ } else {
+ unicast_reg &= ~(0xff << (8 * reg_offset));
+ unicast_reg |= ((0x01 | (queue << 1)) << (8 * reg_offset));
+ }
+
+ mvreg_write(pp, (MVNETA_DA_FILT_UCAST_BASE + tbl_offset), unicast_reg);
+}
+
+/* Set mac address */
+static void mvneta_mac_addr_set(struct mvneta_port *pp, unsigned char *addr,
+ int queue)
+{
+ unsigned int mac_h;
+ unsigned int mac_l;
+
+ if (queue != -1) {
+ mac_l = (addr[4] << 8) | (addr[5]);
+ mac_h = (addr[0] << 24) | (addr[1] << 16) |
+ (addr[2] << 8) | (addr[3] << 0);
+
+ mvreg_write(pp, MVNETA_MAC_ADDR_LOW, mac_l);
+ mvreg_write(pp, MVNETA_MAC_ADDR_HIGH, mac_h);
+ }
+
+ /* Accept frames of this address */
+ mvneta_set_ucast_addr(pp, addr[5], queue);
+}
+
+/* Set the number of packets that will be received before RX interrupt
+ * will be generated by HW.
+ */
+static void mvneta_rx_pkts_coal_set(struct mvneta_port *pp,
+ struct mvneta_rx_queue *rxq, u32 value)
+{
+ mvreg_write(pp, MVNETA_RXQ_THRESHOLD_REG(rxq->id),
+ value | MVNETA_RXQ_NON_OCCUPIED(0));
+ rxq->pkts_coal = value;
+}
+
+/* Set the time delay in usec before RX interrupt will be generated by
+ * HW.
+ */
+static void mvneta_rx_time_coal_set(struct mvneta_port *pp,
+ struct mvneta_rx_queue *rxq, u32 value)
+{
+ u32 val;
+ unsigned long clk_rate;
+
+ clk_rate = clk_get_rate(pp->clk);
+ val = (clk_rate / 1000000) * value;
+
+ mvreg_write(pp, MVNETA_RXQ_TIME_COAL_REG(rxq->id), val);
+ rxq->time_coal = value;
+}
+
+/* Set threshold for TX_DONE pkts coalescing */
+static void mvneta_tx_done_pkts_coal_set(struct mvneta_port *pp,
+ struct mvneta_tx_queue *txq, u32 value)
+{
+ u32 val;
+
+ val = mvreg_read(pp, MVNETA_TXQ_SIZE_REG(txq->id));
+
+ val &= ~MVNETA_TXQ_SENT_THRESH_ALL_MASK;
+ val |= MVNETA_TXQ_SENT_THRESH_MASK(value);
+
+ mvreg_write(pp, MVNETA_TXQ_SIZE_REG(txq->id), val);
+
+ txq->done_pkts_coal = value;
+}
+
+/* Trigger tx done timer in MVNETA_TX_DONE_TIMER_PERIOD msecs */
+static void mvneta_add_tx_done_timer(struct mvneta_port *pp)
+{
+ if (test_and_set_bit(MVNETA_F_TX_DONE_TIMER_BIT, &pp->flags) == 0) {
+ pp->tx_done_timer.expires = jiffies +
+ msecs_to_jiffies(MVNETA_TX_DONE_TIMER_PERIOD);
+ add_timer(&pp->tx_done_timer);
+ }
+}
+
+
+/* Handle rx descriptor fill by setting buf_cookie and buf_phys_addr */
+static void mvneta_rx_desc_fill(struct mvneta_rx_desc *rx_desc,
+ u32 phys_addr, u32 cookie)
+{
+ rx_desc->buf_cookie = cookie;
+ rx_desc->buf_phys_addr = phys_addr;
+}
+
+/* Decrement sent descriptors counter */
+static void mvneta_txq_sent_desc_dec(struct mvneta_port *pp,
+ struct mvneta_tx_queue *txq,
+ int sent_desc)
+{
+ u32 val;
+
+ /* Only 255 TX descriptors can be updated at once */
+ while (sent_desc > 0xff) {
+ val = 0xff << MVNETA_TXQ_DEC_SENT_SHIFT;
+ mvreg_write(pp, MVNETA_TXQ_UPDATE_REG(txq->id), val);
+ sent_desc = sent_desc - 0xff;
+ }
+
+ val = sent_desc << MVNETA_TXQ_DEC_SENT_SHIFT;
+ mvreg_write(pp, MVNETA_TXQ_UPDATE_REG(txq->id), val);
+}
+
+/* Get number of TX descriptors already sent by HW */
+static int mvneta_txq_sent_desc_num_get(struct mvneta_port *pp,
+ struct mvneta_tx_queue *txq)
+{
+ u32 val;
+ int sent_desc;
+
+ val = mvreg_read(pp, MVNETA_TXQ_STATUS_REG(txq->id));
+ sent_desc = (val & MVNETA_TXQ_SENT_DESC_MASK) >>
+ MVNETA_TXQ_SENT_DESC_SHIFT;
+
+ return sent_desc;
+}
+
+/* Get number of sent descriptors and decrement counter.
+ * The number of sent descriptors is returned.
+ */
+static int mvneta_txq_sent_desc_proc(struct mvneta_port *pp,
+ struct mvneta_tx_queue *txq)
+{
+ int sent_desc;
+
+ /* Get number of sent descriptors */
+ sent_desc = mvneta_txq_sent_desc_num_get(pp, txq);
+
+ /* Decrement sent descriptors counter */
+ if (sent_desc)
+ mvneta_txq_sent_desc_dec(pp, txq, sent_desc);
+
+ return sent_desc;
+}
+
+/* Set TXQ descriptors fields relevant for CSUM calculation */
+static u32 mvneta_txq_desc_csum(int l3_offs, int l3_proto,
+ int ip_hdr_len, int l4_proto)
+{
+ u32 command;
+
+ /* Fields: L3_offset, IP_hdrlen, L3_type, G_IPv4_chk,
+ * G_L4_chk, L4_type; required only for checksum
+ * calculation
+ */
+ command = l3_offs << MVNETA_TX_L3_OFF_SHIFT;
+ command |= ip_hdr_len << MVNETA_TX_IP_HLEN_SHIFT;
+
+ if (l3_proto == swab16(ETH_P_IP))
+ command |= MVNETA_TXD_IP_CSUM;
+ else
+ command |= MVNETA_TX_L3_IP6;
+
+ if (l4_proto == IPPROTO_TCP)
+ command |= MVNETA_TX_L4_CSUM_FULL;
+ else if (l4_proto == IPPROTO_UDP)
+ command |= MVNETA_TX_L4_UDP | MVNETA_TX_L4_CSUM_FULL;
+ else
+ command |= MVNETA_TX_L4_CSUM_NOT;
+
+ return command;
+}
+
+
+/* Display more error info */
+static void mvneta_rx_error(struct mvneta_port *pp,
+ struct mvneta_rx_desc *rx_desc)
+{
+ u32 status = rx_desc->status;
+
+ if (!mvneta_rxq_desc_is_first_last(rx_desc)) {
+ netdev_err(pp->dev,
+ "bad rx status %08x (buffer oversize), size=%d\n",
+ rx_desc->status, rx_desc->data_size);
+ return;
+ }
+
+ switch (status & MVNETA_RXD_ERR_CODE_MASK) {
+ case MVNETA_RXD_ERR_CRC:
+ netdev_err(pp->dev, "bad rx status %08x (crc error), size=%d\n",
+ status, rx_desc->data_size);
+ break;
+ case MVNETA_RXD_ERR_OVERRUN:
+ netdev_err(pp->dev, "bad rx status %08x (overrun error), size=%d\n",
+ status, rx_desc->data_size);
+ break;
+ case MVNETA_RXD_ERR_LEN:
+ netdev_err(pp->dev, "bad rx status %08x (max frame length error), size=%d\n",
+ status, rx_desc->data_size);
+ break;
+ case MVNETA_RXD_ERR_RESOURCE:
+ netdev_err(pp->dev, "bad rx status %08x (resource error), size=%d\n",
+ status, rx_desc->data_size);
+ break;
+ }
+}
+
+/* Handle RX checksum offload */
+static void mvneta_rx_csum(struct mvneta_port *pp,
+ struct mvneta_rx_desc *rx_desc,
+ struct sk_buff *skb)
+{
+ if ((rx_desc->status & MVNETA_RXD_L3_IP4) &&
+ (rx_desc->status & MVNETA_RXD_L4_CSUM_OK)) {
+ skb->csum = 0;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ return;
+ }
+
+ skb->ip_summed = CHECKSUM_NONE;
+}
+
+/* Return tx queue pointer (find last set bit) according to causeTxDone reg */
+static struct mvneta_tx_queue *mvneta_tx_done_policy(struct mvneta_port *pp,
+ u32 cause)
+{
+ int queue = fls(cause) - 1;
+
+ return (queue < 0 || queue >= txq_number) ? NULL : &pp->txqs[queue];
+}
+
+/* Free tx queue skbuffs */
+static void mvneta_txq_bufs_free(struct mvneta_port *pp,
+ struct mvneta_tx_queue *txq, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ struct mvneta_tx_desc *tx_desc = txq->descs +
+ txq->txq_get_index;
+ struct sk_buff *skb = txq->tx_skb[txq->txq_get_index];
+
+ mvneta_txq_inc_get(txq);
+
+ if (!skb)
+ continue;
+
+ dma_unmap_single(pp->dev->dev.parent, tx_desc->buf_phys_addr,
+ tx_desc->data_size, DMA_TO_DEVICE);
+ dev_kfree_skb_any(skb);
+ }
+}
+
+/* Handle end of transmission */
+static int mvneta_txq_done(struct mvneta_port *pp,
+ struct mvneta_tx_queue *txq)
+{
+ struct netdev_queue *nq = netdev_get_tx_queue(pp->dev, txq->id);
+ int tx_done;
+
+ tx_done = mvneta_txq_sent_desc_proc(pp, txq);
+ if (tx_done == 0)
+ return tx_done;
+ mvneta_txq_bufs_free(pp, txq, tx_done);
+
+ txq->count -= tx_done;
+
+ if (netif_tx_queue_stopped(nq)) {
+ if (txq->size - txq->count >= MAX_SKB_FRAGS + 1)
+ netif_tx_wake_queue(nq);
+ }
+
+ return tx_done;
+}
+
+/* Refill processing */
+static int mvneta_rx_refill(struct mvneta_port *pp,
+ struct mvneta_rx_desc *rx_desc)
+
+{
+ dma_addr_t phys_addr;
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb(pp->dev, pp->pkt_size);
+ if (!skb)
+ return -ENOMEM;
+
+ phys_addr = dma_map_single(pp->dev->dev.parent, skb->head,
+ MVNETA_RX_BUF_SIZE(pp->pkt_size),
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(pp->dev->dev.parent, phys_addr))) {
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ mvneta_rx_desc_fill(rx_desc, phys_addr, (u32)skb);
+
+ return 0;
+}
+
+/* Handle tx checksum */
+static u32 mvneta_skb_tx_csum(struct mvneta_port *pp, struct sk_buff *skb)
+{
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ int ip_hdr_len = 0;
+ u8 l4_proto;
+
+ if (skb->protocol == htons(ETH_P_IP)) {
+ struct iphdr *ip4h = ip_hdr(skb);
+
+ /* Calculate IPv4 checksum and L4 checksum */
+ ip_hdr_len = ip4h->ihl;
+ l4_proto = ip4h->protocol;
+ } else if (skb->protocol == htons(ETH_P_IPV6)) {
+ struct ipv6hdr *ip6h = ipv6_hdr(skb);
+
+ /* Read l4_protocol from one of IPv6 extra headers */
+ if (skb_network_header_len(skb) > 0)
+ ip_hdr_len = (skb_network_header_len(skb) >> 2);
+ l4_proto = ip6h->nexthdr;
+ } else
+ return MVNETA_TX_L4_CSUM_NOT;
+
+ return mvneta_txq_desc_csum(skb_network_offset(skb),
+ skb->protocol, ip_hdr_len, l4_proto);
+ }
+
+ return MVNETA_TX_L4_CSUM_NOT;
+}
+
+/* Returns rx queue pointer (find last set bit) according to causeRxTx
+ * value
+ */
+static struct mvneta_rx_queue *mvneta_rx_policy(struct mvneta_port *pp,
+ u32 cause)
+{
+ int queue = fls(cause >> 8) - 1;
+
+ return (queue < 0 || queue >= rxq_number) ? NULL : &pp->rxqs[queue];
+}
+
+/* Drop packets received by the RXQ and free buffers */
+static void mvneta_rxq_drop_pkts(struct mvneta_port *pp,
+ struct mvneta_rx_queue *rxq)
+{
+ int rx_done, i;
+
+ rx_done = mvneta_rxq_busy_desc_num_get(pp, rxq);
+ for (i = 0; i < rxq->size; i++) {
+ struct mvneta_rx_desc *rx_desc = rxq->descs + i;
+ struct sk_buff *skb = (struct sk_buff *)rx_desc->buf_cookie;
+
+ dev_kfree_skb_any(skb);
+ dma_unmap_single(pp->dev->dev.parent, rx_desc->buf_phys_addr,
+ rx_desc->data_size, DMA_FROM_DEVICE);
+ }
+
+ if (rx_done)
+ mvneta_rxq_desc_num_update(pp, rxq, rx_done, rx_done);
+}
+
+/* Main rx processing */
+static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
+ struct mvneta_rx_queue *rxq)
+{
+ struct net_device *dev = pp->dev;
+ int rx_done, rx_filled;
+
+ /* Get number of received packets */
+ rx_done = mvneta_rxq_busy_desc_num_get(pp, rxq);
+
+ if (rx_todo > rx_done)
+ rx_todo = rx_done;
+
+ rx_done = 0;
+ rx_filled = 0;
+
+ /* Fairness NAPI loop */
+ while (rx_done < rx_todo) {
+ struct mvneta_rx_desc *rx_desc = mvneta_rxq_next_desc_get(rxq);
+ struct sk_buff *skb;
+ u32 rx_status;
+ int rx_bytes, err;
+
+ prefetch(rx_desc);
+ rx_done++;
+ rx_filled++;
+ rx_status = rx_desc->status;
+ skb = (struct sk_buff *)rx_desc->buf_cookie;
+
+ if (!mvneta_rxq_desc_is_first_last(rx_desc) ||
+ (rx_status & MVNETA_RXD_ERR_SUMMARY)) {
+ dev->stats.rx_errors++;
+ mvneta_rx_error(pp, rx_desc);
+ mvneta_rx_desc_fill(rx_desc, rx_desc->buf_phys_addr,
+ (u32)skb);
+ continue;
+ }
+
+ dma_unmap_single(pp->dev->dev.parent, rx_desc->buf_phys_addr,
+ rx_desc->data_size, DMA_FROM_DEVICE);
+
+ rx_bytes = rx_desc->data_size -
+ (ETH_FCS_LEN + MVNETA_MH_SIZE);
+ u64_stats_update_begin(&pp->rx_stats.syncp);
+ pp->rx_stats.packets++;
+ pp->rx_stats.bytes += rx_bytes;
+ u64_stats_update_end(&pp->rx_stats.syncp);
+
+ /* Linux processing */
+ skb_reserve(skb, MVNETA_MH_SIZE);
+ skb_put(skb, rx_bytes);
+
+ skb->protocol = eth_type_trans(skb, dev);
+
+ mvneta_rx_csum(pp, rx_desc, skb);
+
+ napi_gro_receive(&pp->napi, skb);
+
+ /* Refill processing */
+ err = mvneta_rx_refill(pp, rx_desc);
+ if (err) {
+ netdev_err(pp->dev, "Linux processing - Can't refill\n");
+ rxq->missed++;
+ rx_filled--;
+ }
+ }
+
+ /* Update rxq management counters */
+ mvneta_rxq_desc_num_update(pp, rxq, rx_done, rx_filled);
+
+ return rx_done;
+}
+
+/* Handle tx fragmentation processing */
+static int mvneta_tx_frag_process(struct mvneta_port *pp, struct sk_buff *skb,
+ struct mvneta_tx_queue *txq)
+{
+ struct mvneta_tx_desc *tx_desc;
+ int i;
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ void *addr = page_address(frag->page.p) + frag->page_offset;
+
+ tx_desc = mvneta_txq_next_desc_get(txq);
+ tx_desc->data_size = frag->size;
+
+ tx_desc->buf_phys_addr =
+ dma_map_single(pp->dev->dev.parent, addr,
+ tx_desc->data_size, DMA_TO_DEVICE);
+
+ if (dma_mapping_error(pp->dev->dev.parent,
+ tx_desc->buf_phys_addr)) {
+ mvneta_txq_desc_put(txq);
+ goto error;
+ }
+
+ if (i == (skb_shinfo(skb)->nr_frags - 1)) {
+ /* Last descriptor */
+ tx_desc->command = MVNETA_TXD_L_DESC | MVNETA_TXD_Z_PAD;
+
+ txq->tx_skb[txq->txq_put_index] = skb;
+
+ mvneta_txq_inc_put(txq);
+ } else {
+ /* Descriptor in the middle: Not First, Not Last */
+ tx_desc->command = 0;
+
+ txq->tx_skb[txq->txq_put_index] = NULL;
+ mvneta_txq_inc_put(txq);
+ }
+ }
+
+ return 0;
+
+error:
+ /* Release all descriptors that were used to map fragments of
+ * this packet, as well as the corresponding DMA mappings
+ */
+ for (i = i - 1; i >= 0; i--) {
+ tx_desc = txq->descs + i;
+ dma_unmap_single(pp->dev->dev.parent,
+ tx_desc->buf_phys_addr,
+ tx_desc->data_size,
+ DMA_TO_DEVICE);
+ mvneta_txq_desc_put(txq);
+ }
+
+ return -ENOMEM;
+}
+
+/* Main tx processing */
+static int mvneta_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+ struct mvneta_tx_queue *txq = &pp->txqs[txq_def];
+ struct mvneta_tx_desc *tx_desc;
+ struct netdev_queue *nq;
+ int frags = 0;
+ u32 tx_cmd;
+
+ if (!netif_running(dev))
+ goto out;
+
+ frags = skb_shinfo(skb)->nr_frags + 1;
+ nq = netdev_get_tx_queue(dev, txq_def);
+
+ /* Get a descriptor for the first part of the packet */
+ tx_desc = mvneta_txq_next_desc_get(txq);
+
+ tx_cmd = mvneta_skb_tx_csum(pp, skb);
+
+ tx_desc->data_size = skb_headlen(skb);
+
+ tx_desc->buf_phys_addr = dma_map_single(dev->dev.parent, skb->data,
+ tx_desc->data_size,
+ DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(dev->dev.parent,
+ tx_desc->buf_phys_addr))) {
+ mvneta_txq_desc_put(txq);
+ frags = 0;
+ goto out;
+ }
+
+ if (frags == 1) {
+ /* First and Last descriptor */
+ tx_cmd |= MVNETA_TXD_FLZ_DESC;
+ tx_desc->command = tx_cmd;
+ txq->tx_skb[txq->txq_put_index] = skb;
+ mvneta_txq_inc_put(txq);
+ } else {
+ /* First but not Last */
+ tx_cmd |= MVNETA_TXD_F_DESC;
+ txq->tx_skb[txq->txq_put_index] = NULL;
+ mvneta_txq_inc_put(txq);
+ tx_desc->command = tx_cmd;
+ /* Continue with other skb fragments */
+ if (mvneta_tx_frag_process(pp, skb, txq)) {
+ dma_unmap_single(dev->dev.parent,
+ tx_desc->buf_phys_addr,
+ tx_desc->data_size,
+ DMA_TO_DEVICE);
+ mvneta_txq_desc_put(txq);
+ frags = 0;
+ goto out;
+ }
+ }
+
+ txq->count += frags;
+ mvneta_txq_pend_desc_add(pp, txq, frags);
+
+ if (txq->size - txq->count < MAX_SKB_FRAGS + 1)
+ netif_tx_stop_queue(nq);
+
+out:
+ if (frags > 0) {
+ u64_stats_update_begin(&pp->tx_stats.syncp);
+ pp->tx_stats.packets++;
+ pp->tx_stats.bytes += skb->len;
+ u64_stats_update_end(&pp->tx_stats.syncp);
+
+ } else {
+ dev->stats.tx_dropped++;
+ dev_kfree_skb_any(skb);
+ }
+
+ if (txq->count >= MVNETA_TXDONE_COAL_PKTS)
+ mvneta_txq_done(pp, txq);
+
+ /* If after calling mvneta_txq_done, count equals
+ * frags, we need to set the timer
+ */
+ if (txq->count == frags && frags > 0)
+ mvneta_add_tx_done_timer(pp);
+
+ return NETDEV_TX_OK;
+}
+
+
+/* Free tx resources, when resetting a port */
+static void mvneta_txq_done_force(struct mvneta_port *pp,
+ struct mvneta_tx_queue *txq)
+
+{
+ int tx_done = txq->count;
+
+ mvneta_txq_bufs_free(pp, txq, tx_done);
+
+ /* reset txq */
+ txq->count = 0;
+ txq->txq_put_index = 0;
+ txq->txq_get_index = 0;
+}
+
+/* handle tx done - called from tx done timer callback */
+static u32 mvneta_tx_done_gbe(struct mvneta_port *pp, u32 cause_tx_done,
+ int *tx_todo)
+{
+ struct mvneta_tx_queue *txq;
+ u32 tx_done = 0;
+ struct netdev_queue *nq;
+
+ *tx_todo = 0;
+ while (cause_tx_done != 0) {
+ txq = mvneta_tx_done_policy(pp, cause_tx_done);
+ if (!txq)
+ break;
+
+ nq = netdev_get_tx_queue(pp->dev, txq->id);
+ __netif_tx_lock(nq, smp_processor_id());
+
+ if (txq->count) {
+ tx_done += mvneta_txq_done(pp, txq);
+ *tx_todo += txq->count;
+ }
+
+ __netif_tx_unlock(nq);
+ cause_tx_done &= ~((1 << txq->id));
+ }
+
+ return tx_done;
+}
+
+/* Compute crc8 of the specified address, using a unique algorithm ,
+ * according to hw spec, different than generic crc8 algorithm
+ */
+static int mvneta_addr_crc(unsigned char *addr)
+{
+ int crc = 0;
+ int i;
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ int j;
+
+ crc = (crc ^ addr[i]) << 8;
+ for (j = 7; j >= 0; j--) {
+ if (crc & (0x100 << j))
+ crc ^= 0x107 << j;
+ }
+ }
+
+ return crc;
+}
+
+/* This method controls the net device special MAC multicast support.
+ * The Special Multicast Table for MAC addresses supports MAC of the form
+ * 0x01-00-5E-00-00-XX (where XX is between 0x00 and 0xFF).
+ * The MAC DA[7:0] bits are used as a pointer to the Special Multicast
+ * Table entries in the DA-Filter table. This method set the Special
+ * Multicast Table appropriate entry.
+ */
+static void mvneta_set_special_mcast_addr(struct mvneta_port *pp,
+ unsigned char last_byte,
+ int queue)
+{
+ unsigned int smc_table_reg;
+ unsigned int tbl_offset;
+ unsigned int reg_offset;
+
+ /* Register offset from SMC table base */
+ tbl_offset = (last_byte / 4);
+ /* Entry offset within the above reg */
+ reg_offset = last_byte % 4;
+
+ smc_table_reg = mvreg_read(pp, (MVNETA_DA_FILT_SPEC_MCAST
+ + tbl_offset * 4));
+
+ if (queue == -1)
+ smc_table_reg &= ~(0xff << (8 * reg_offset));
+ else {
+ smc_table_reg &= ~(0xff << (8 * reg_offset));
+ smc_table_reg |= ((0x01 | (queue << 1)) << (8 * reg_offset));
+ }
+
+ mvreg_write(pp, MVNETA_DA_FILT_SPEC_MCAST + tbl_offset * 4,
+ smc_table_reg);
+}
+
+/* This method controls the network device Other MAC multicast support.
+ * The Other Multicast Table is used for multicast of another type.
+ * A CRC-8 is used as an index to the Other Multicast Table entries
+ * in the DA-Filter table.
+ * The method gets the CRC-8 value from the calling routine and
+ * sets the Other Multicast Table appropriate entry according to the
+ * specified CRC-8 .
+ */
+static void mvneta_set_other_mcast_addr(struct mvneta_port *pp,
+ unsigned char crc8,
+ int queue)
+{
+ unsigned int omc_table_reg;
+ unsigned int tbl_offset;
+ unsigned int reg_offset;
+
+ tbl_offset = (crc8 / 4) * 4; /* Register offset from OMC table base */
+ reg_offset = crc8 % 4; /* Entry offset within the above reg */
+
+ omc_table_reg = mvreg_read(pp, MVNETA_DA_FILT_OTH_MCAST + tbl_offset);
+
+ if (queue == -1) {
+ /* Clear accepts frame bit at specified Other DA table entry */
+ omc_table_reg &= ~(0xff << (8 * reg_offset));
+ } else {
+ omc_table_reg &= ~(0xff << (8 * reg_offset));
+ omc_table_reg |= ((0x01 | (queue << 1)) << (8 * reg_offset));
+ }
+
+ mvreg_write(pp, MVNETA_DA_FILT_OTH_MCAST + tbl_offset, omc_table_reg);
+}
+
+/* The network device supports multicast using two tables:
+ * 1) Special Multicast Table for MAC addresses of the form
+ * 0x01-00-5E-00-00-XX (where XX is between 0x00 and 0xFF).
+ * The MAC DA[7:0] bits are used as a pointer to the Special Multicast
+ * Table entries in the DA-Filter table.
+ * 2) Other Multicast Table for multicast of another type. A CRC-8 value
+ * is used as an index to the Other Multicast Table entries in the
+ * DA-Filter table.
+ */
+static int mvneta_mcast_addr_set(struct mvneta_port *pp, unsigned char *p_addr,
+ int queue)
+{
+ unsigned char crc_result = 0;
+
+ if (memcmp(p_addr, "\x01\x00\x5e\x00\x00", 5) == 0) {
+ mvneta_set_special_mcast_addr(pp, p_addr[5], queue);
+ return 0;
+ }
+
+ crc_result = mvneta_addr_crc(p_addr);
+ if (queue == -1) {
+ if (pp->mcast_count[crc_result] == 0) {
+ netdev_info(pp->dev, "No valid Mcast for crc8=0x%02x\n",
+ crc_result);
+ return -EINVAL;
+ }
+
+ pp->mcast_count[crc_result]--;
+ if (pp->mcast_count[crc_result] != 0) {
+ netdev_info(pp->dev,
+ "After delete there are %d valid Mcast for crc8=0x%02x\n",
+ pp->mcast_count[crc_result], crc_result);
+ return -EINVAL;
+ }
+ } else
+ pp->mcast_count[crc_result]++;
+
+ mvneta_set_other_mcast_addr(pp, crc_result, queue);
+
+ return 0;
+}
+
+/* Configure Fitering mode of Ethernet port */
+static void mvneta_rx_unicast_promisc_set(struct mvneta_port *pp,
+ int is_promisc)
+{
+ u32 port_cfg_reg, val;
+
+ port_cfg_reg = mvreg_read(pp, MVNETA_PORT_CONFIG);
+
+ val = mvreg_read(pp, MVNETA_TYPE_PRIO);
+
+ /* Set / Clear UPM bit in port configuration register */
+ if (is_promisc) {
+ /* Accept all Unicast addresses */
+ port_cfg_reg |= MVNETA_UNI_PROMISC_MODE;
+ val |= MVNETA_FORCE_UNI;
+ mvreg_write(pp, MVNETA_MAC_ADDR_LOW, 0xffff);
+ mvreg_write(pp, MVNETA_MAC_ADDR_HIGH, 0xffffffff);
+ } else {
+ /* Reject all Unicast addresses */
+ port_cfg_reg &= ~MVNETA_UNI_PROMISC_MODE;
+ val &= ~MVNETA_FORCE_UNI;
+ }
+
+ mvreg_write(pp, MVNETA_PORT_CONFIG, port_cfg_reg);
+ mvreg_write(pp, MVNETA_TYPE_PRIO, val);
+}
+
+/* register unicast and multicast addresses */
+static void mvneta_set_rx_mode(struct net_device *dev)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+ struct netdev_hw_addr *ha;
+
+ if (dev->flags & IFF_PROMISC) {
+ /* Accept all: Multicast + Unicast */
+ mvneta_rx_unicast_promisc_set(pp, 1);
+ mvneta_set_ucast_table(pp, rxq_def);
+ mvneta_set_special_mcast_table(pp, rxq_def);
+ mvneta_set_other_mcast_table(pp, rxq_def);
+ } else {
+ /* Accept single Unicast */
+ mvneta_rx_unicast_promisc_set(pp, 0);
+ mvneta_set_ucast_table(pp, -1);
+ mvneta_mac_addr_set(pp, dev->dev_addr, rxq_def);
+
+ if (dev->flags & IFF_ALLMULTI) {
+ /* Accept all multicast */
+ mvneta_set_special_mcast_table(pp, rxq_def);
+ mvneta_set_other_mcast_table(pp, rxq_def);
+ } else {
+ /* Accept only initialized multicast */
+ mvneta_set_special_mcast_table(pp, -1);
+ mvneta_set_other_mcast_table(pp, -1);
+
+ if (!netdev_mc_empty(dev)) {
+ netdev_for_each_mc_addr(ha, dev) {
+ mvneta_mcast_addr_set(pp, ha->addr,
+ rxq_def);
+ }
+ }
+ }
+ }
+}
+
+/* Interrupt handling - the callback for request_irq() */
+static irqreturn_t mvneta_isr(int irq, void *dev_id)
+{
+ struct mvneta_port *pp = (struct mvneta_port *)dev_id;
+
+ /* Mask all interrupts */
+ mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
+
+ napi_schedule(&pp->napi);
+
+ return IRQ_HANDLED;
+}
+
+/* NAPI handler
+ * Bits 0 - 7 of the causeRxTx register indicate that are transmitted
+ * packets on the corresponding TXQ (Bit 0 is for TX queue 1).
+ * Bits 8 -15 of the cause Rx Tx register indicate that are received
+ * packets on the corresponding RXQ (Bit 8 is for RX queue 0).
+ * Each CPU has its own causeRxTx register
+ */
+static int mvneta_poll(struct napi_struct *napi, int budget)
+{
+ int rx_done = 0;
+ u32 cause_rx_tx;
+ unsigned long flags;
+ struct mvneta_port *pp = netdev_priv(napi->dev);
+
+ if (!netif_running(pp->dev)) {
+ napi_complete(napi);
+ return rx_done;
+ }
+
+ /* Read cause register */
+ cause_rx_tx = mvreg_read(pp, MVNETA_INTR_NEW_CAUSE) &
+ MVNETA_RX_INTR_MASK(rxq_number);
+
+ /* For the case where the last mvneta_poll did not process all
+ * RX packets
+ */
+ cause_rx_tx |= pp->cause_rx_tx;
+ if (rxq_number > 1) {
+ while ((cause_rx_tx != 0) && (budget > 0)) {
+ int count;
+ struct mvneta_rx_queue *rxq;
+ /* get rx queue number from cause_rx_tx */
+ rxq = mvneta_rx_policy(pp, cause_rx_tx);
+ if (!rxq)
+ break;
+
+ /* process the packet in that rx queue */
+ count = mvneta_rx(pp, budget, rxq);
+ rx_done += count;
+ budget -= count;
+ if (budget > 0) {
+ /* set off the rx bit of the
+ * corresponding bit in the cause rx
+ * tx register, so that next iteration
+ * will find the next rx queue where
+ * packets are received on
+ */
+ cause_rx_tx &= ~((1 << rxq->id) << 8);
+ }
+ }
+ } else {
+ rx_done = mvneta_rx(pp, budget, &pp->rxqs[rxq_def]);
+ budget -= rx_done;
+ }
+
+ if (budget > 0) {
+ cause_rx_tx = 0;
+ napi_complete(napi);
+ local_irq_save(flags);
+ mvreg_write(pp, MVNETA_INTR_NEW_MASK,
+ MVNETA_RX_INTR_MASK(rxq_number));
+ local_irq_restore(flags);
+ }
+
+ pp->cause_rx_tx = cause_rx_tx;
+ return rx_done;
+}
+
+/* tx done timer callback */
+static void mvneta_tx_done_timer_callback(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct mvneta_port *pp = netdev_priv(dev);
+ int tx_done = 0, tx_todo = 0;
+
+ if (!netif_running(dev))
+ return ;
+
+ clear_bit(MVNETA_F_TX_DONE_TIMER_BIT, &pp->flags);
+
+ tx_done = mvneta_tx_done_gbe(pp,
+ (((1 << txq_number) - 1) &
+ MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK),
+ &tx_todo);
+ if (tx_todo > 0)
+ mvneta_add_tx_done_timer(pp);
+}
+
+/* Handle rxq fill: allocates rxq skbs; called when initializing a port */
+static int mvneta_rxq_fill(struct mvneta_port *pp, struct mvneta_rx_queue *rxq,
+ int num)
+{
+ struct net_device *dev = pp->dev;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ struct sk_buff *skb;
+ struct mvneta_rx_desc *rx_desc;
+ unsigned long phys_addr;
+
+ skb = dev_alloc_skb(pp->pkt_size);
+ if (!skb) {
+ netdev_err(dev, "%s:rxq %d, %d of %d buffs filled\n",
+ __func__, rxq->id, i, num);
+ break;
+ }
+
+ rx_desc = rxq->descs + i;
+ memset(rx_desc, 0, sizeof(struct mvneta_rx_desc));
+ phys_addr = dma_map_single(dev->dev.parent, skb->head,
+ MVNETA_RX_BUF_SIZE(pp->pkt_size),
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(dev->dev.parent, phys_addr))) {
+ dev_kfree_skb(skb);
+ break;
+ }
+
+ mvneta_rx_desc_fill(rx_desc, phys_addr, (u32)skb);
+ }
+
+ /* Add this number of RX descriptors as non occupied (ready to
+ * get packets)
+ */
+ mvneta_rxq_non_occup_desc_add(pp, rxq, i);
+
+ return i;
+}
+
+/* Free all packets pending transmit from all TXQs and reset TX port */
+static void mvneta_tx_reset(struct mvneta_port *pp)
+{
+ int queue;
+
+ /* free the skb's in the hal tx ring */
+ for (queue = 0; queue < txq_number; queue++)
+ mvneta_txq_done_force(pp, &pp->txqs[queue]);
+
+ mvreg_write(pp, MVNETA_PORT_TX_RESET, MVNETA_PORT_TX_DMA_RESET);
+ mvreg_write(pp, MVNETA_PORT_TX_RESET, 0);
+}
+
+static void mvneta_rx_reset(struct mvneta_port *pp)
+{
+ mvreg_write(pp, MVNETA_PORT_RX_RESET, MVNETA_PORT_RX_DMA_RESET);
+ mvreg_write(pp, MVNETA_PORT_RX_RESET, 0);
+}
+
+/* Rx/Tx queue initialization/cleanup methods */
+
+/* Create a specified RX queue */
+static int mvneta_rxq_init(struct mvneta_port *pp,
+ struct mvneta_rx_queue *rxq)
+
+{
+ rxq->size = pp->rx_ring_size;
+
+ /* Allocate memory for RX descriptors */
+ rxq->descs = dma_alloc_coherent(pp->dev->dev.parent,
+ rxq->size * MVNETA_DESC_ALIGNED_SIZE,
+ &rxq->descs_phys, GFP_KERNEL);
+ if (rxq->descs == NULL) {
+ netdev_err(pp->dev,
+ "rxq=%d: Can't allocate %d bytes for %d RX descr\n",
+ rxq->id, rxq->size * MVNETA_DESC_ALIGNED_SIZE,
+ rxq->size);
+ return -ENOMEM;
+ }
+
+ BUG_ON(rxq->descs !=
+ PTR_ALIGN(rxq->descs, MVNETA_CPU_D_CACHE_LINE_SIZE));
+
+ rxq->last_desc = rxq->size - 1;
+
+ /* Set Rx descriptors queue starting address */
+ mvreg_write(pp, MVNETA_RXQ_BASE_ADDR_REG(rxq->id), rxq->descs_phys);
+ mvreg_write(pp, MVNETA_RXQ_SIZE_REG(rxq->id), rxq->size);
+
+ /* Set Offset */
+ mvneta_rxq_offset_set(pp, rxq, NET_SKB_PAD);
+
+ /* Set coalescing pkts and time */
+ mvneta_rx_pkts_coal_set(pp, rxq, rxq->pkts_coal);
+ mvneta_rx_time_coal_set(pp, rxq, rxq->time_coal);
+
+ /* Fill RXQ with buffers from RX pool */
+ mvneta_rxq_buf_size_set(pp, rxq, MVNETA_RX_BUF_SIZE(pp->pkt_size));
+ mvneta_rxq_bm_disable(pp, rxq);
+ mvneta_rxq_fill(pp, rxq, rxq->size);
+
+ return 0;
+}
+
+/* Cleanup Rx queue */
+static void mvneta_rxq_deinit(struct mvneta_port *pp,
+ struct mvneta_rx_queue *rxq)
+{
+ mvneta_rxq_drop_pkts(pp, rxq);
+
+ if (rxq->descs)
+ dma_free_coherent(pp->dev->dev.parent,
+ rxq->size * MVNETA_DESC_ALIGNED_SIZE,
+ rxq->descs,
+ rxq->descs_phys);
+
+ rxq->descs = NULL;
+ rxq->last_desc = 0;
+ rxq->next_desc_to_proc = 0;
+ rxq->descs_phys = 0;
+}
+
+/* Create and initialize a tx queue */
+static int mvneta_txq_init(struct mvneta_port *pp,
+ struct mvneta_tx_queue *txq)
+{
+ txq->size = pp->tx_ring_size;
+
+ /* Allocate memory for TX descriptors */
+ txq->descs = dma_alloc_coherent(pp->dev->dev.parent,
+ txq->size * MVNETA_DESC_ALIGNED_SIZE,
+ &txq->descs_phys, GFP_KERNEL);
+ if (txq->descs == NULL) {
+ netdev_err(pp->dev,
+ "txQ=%d: Can't allocate %d bytes for %d TX descr\n",
+ txq->id, txq->size * MVNETA_DESC_ALIGNED_SIZE,
+ txq->size);
+ return -ENOMEM;
+ }
+
+ /* Make sure descriptor address is cache line size aligned */
+ BUG_ON(txq->descs !=
+ PTR_ALIGN(txq->descs, MVNETA_CPU_D_CACHE_LINE_SIZE));
+
+ txq->last_desc = txq->size - 1;
+
+ /* Set maximum bandwidth for enabled TXQs */
+ mvreg_write(pp, MVETH_TXQ_TOKEN_CFG_REG(txq->id), 0x03ffffff);
+ mvreg_write(pp, MVETH_TXQ_TOKEN_COUNT_REG(txq->id), 0x3fffffff);
+
+ /* Set Tx descriptors queue starting address */
+ mvreg_write(pp, MVNETA_TXQ_BASE_ADDR_REG(txq->id), txq->descs_phys);
+ mvreg_write(pp, MVNETA_TXQ_SIZE_REG(txq->id), txq->size);
+
+ txq->tx_skb = kmalloc(txq->size * sizeof(*txq->tx_skb), GFP_KERNEL);
+ if (txq->tx_skb == NULL) {
+ dma_free_coherent(pp->dev->dev.parent,
+ txq->size * MVNETA_DESC_ALIGNED_SIZE,
+ txq->descs, txq->descs_phys);
+ return -ENOMEM;
+ }
+ mvneta_tx_done_pkts_coal_set(pp, txq, txq->done_pkts_coal);
+
+ return 0;
+}
+
+/* Free allocated resources when mvneta_txq_init() fails to allocate memory*/
+static void mvneta_txq_deinit(struct mvneta_port *pp,
+ struct mvneta_tx_queue *txq)
+{
+ kfree(txq->tx_skb);
+
+ if (txq->descs)
+ dma_free_coherent(pp->dev->dev.parent,
+ txq->size * MVNETA_DESC_ALIGNED_SIZE,
+ txq->descs, txq->descs_phys);
+
+ txq->descs = NULL;
+ txq->last_desc = 0;
+ txq->next_desc_to_proc = 0;
+ txq->descs_phys = 0;
+
+ /* Set minimum bandwidth for disabled TXQs */
+ mvreg_write(pp, MVETH_TXQ_TOKEN_CFG_REG(txq->id), 0);
+ mvreg_write(pp, MVETH_TXQ_TOKEN_COUNT_REG(txq->id), 0);
+
+ /* Set Tx descriptors queue starting address and size */
+ mvreg_write(pp, MVNETA_TXQ_BASE_ADDR_REG(txq->id), 0);
+ mvreg_write(pp, MVNETA_TXQ_SIZE_REG(txq->id), 0);
+}
+
+/* Cleanup all Tx queues */
+static void mvneta_cleanup_txqs(struct mvneta_port *pp)
+{
+ int queue;
+
+ for (queue = 0; queue < txq_number; queue++)
+ mvneta_txq_deinit(pp, &pp->txqs[queue]);
+}
+
+/* Cleanup all Rx queues */
+static void mvneta_cleanup_rxqs(struct mvneta_port *pp)
+{
+ int queue;
+
+ for (queue = 0; queue < rxq_number; queue++)
+ mvneta_rxq_deinit(pp, &pp->rxqs[queue]);
+}
+
+
+/* Init all Rx queues */
+static int mvneta_setup_rxqs(struct mvneta_port *pp)
+{
+ int queue;
+
+ for (queue = 0; queue < rxq_number; queue++) {
+ int err = mvneta_rxq_init(pp, &pp->rxqs[queue]);
+ if (err) {
+ netdev_err(pp->dev, "%s: can't create rxq=%d\n",
+ __func__, queue);
+ mvneta_cleanup_rxqs(pp);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/* Init all tx queues */
+static int mvneta_setup_txqs(struct mvneta_port *pp)
+{
+ int queue;
+
+ for (queue = 0; queue < txq_number; queue++) {
+ int err = mvneta_txq_init(pp, &pp->txqs[queue]);
+ if (err) {
+ netdev_err(pp->dev, "%s: can't create txq=%d\n",
+ __func__, queue);
+ mvneta_cleanup_txqs(pp);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static void mvneta_start_dev(struct mvneta_port *pp)
+{
+ mvneta_max_rx_size_set(pp, pp->pkt_size);
+ mvneta_txq_max_tx_size_set(pp, pp->pkt_size);
+
+ /* start the Rx/Tx activity */
+ mvneta_port_enable(pp);
+
+ /* Enable polling on the port */
+ napi_enable(&pp->napi);
+
+ /* Unmask interrupts */
+ mvreg_write(pp, MVNETA_INTR_NEW_MASK,
+ MVNETA_RX_INTR_MASK(rxq_number));
+
+ phy_start(pp->phy_dev);
+ netif_tx_start_all_queues(pp->dev);
+}
+
+static void mvneta_stop_dev(struct mvneta_port *pp)
+{
+ phy_stop(pp->phy_dev);
+
+ napi_disable(&pp->napi);
+
+ netif_carrier_off(pp->dev);
+
+ mvneta_port_down(pp);
+ netif_tx_stop_all_queues(pp->dev);
+
+ /* Stop the port activity */
+ mvneta_port_disable(pp);
+
+ /* Clear all ethernet port interrupts */
+ mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0);
+ mvreg_write(pp, MVNETA_INTR_OLD_CAUSE, 0);
+
+ /* Mask all ethernet port interrupts */
+ mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
+ mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0);
+ mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0);
+
+ mvneta_tx_reset(pp);
+ mvneta_rx_reset(pp);
+}
+
+/* tx timeout callback - display a message and stop/start the network device */
+static void mvneta_tx_timeout(struct net_device *dev)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+
+ netdev_info(dev, "tx timeout\n");
+ mvneta_stop_dev(pp);
+ mvneta_start_dev(pp);
+}
+
+/* Return positive if MTU is valid */
+static int mvneta_check_mtu_valid(struct net_device *dev, int mtu)
+{
+ if (mtu < 68) {
+ netdev_err(dev, "cannot change mtu to less than 68\n");
+ return -EINVAL;
+ }
+
+ /* 9676 == 9700 - 20 and rounding to 8 */
+ if (mtu > 9676) {
+ netdev_info(dev, "Illegal MTU value %d, round to 9676\n", mtu);
+ mtu = 9676;
+ }
+
+ if (!IS_ALIGNED(MVNETA_RX_PKT_SIZE(mtu), 8)) {
+ netdev_info(dev, "Illegal MTU value %d, rounding to %d\n",
+ mtu, ALIGN(MVNETA_RX_PKT_SIZE(mtu), 8));
+ mtu = ALIGN(MVNETA_RX_PKT_SIZE(mtu), 8);
+ }
+
+ return mtu;
+}
+
+/* Change the device mtu */
+static int mvneta_change_mtu(struct net_device *dev, int mtu)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+ int ret;
+
+ mtu = mvneta_check_mtu_valid(dev, mtu);
+ if (mtu < 0)
+ return -EINVAL;
+
+ dev->mtu = mtu;
+
+ if (!netif_running(dev))
+ return 0;
+
+ /* The interface is running, so we have to force a
+ * reallocation of the RXQs
+ */
+ mvneta_stop_dev(pp);
+
+ mvneta_cleanup_txqs(pp);
+ mvneta_cleanup_rxqs(pp);
+
+ pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu);
+
+ ret = mvneta_setup_rxqs(pp);
+ if (ret) {
+ netdev_err(pp->dev, "unable to setup rxqs after MTU change\n");
+ return ret;
+ }
+
+ mvneta_setup_txqs(pp);
+
+ mvneta_start_dev(pp);
+ mvneta_port_up(pp);
+
+ return 0;
+}
+
+/* Handle setting mac address */
+static int mvneta_set_mac_addr(struct net_device *dev, void *addr)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+ u8 *mac = addr + 2;
+ int i;
+
+ if (netif_running(dev))
+ return -EBUSY;
+
+ /* Remove previous address table entry */
+ mvneta_mac_addr_set(pp, dev->dev_addr, -1);
+
+ /* Set new addr in hw */
+ mvneta_mac_addr_set(pp, mac, rxq_def);
+
+ /* Set addr in the device */
+ for (i = 0; i < ETH_ALEN; i++)
+ dev->dev_addr[i] = mac[i];
+
+ return 0;
+}
+
+static void mvneta_adjust_link(struct net_device *ndev)
+{
+ struct mvneta_port *pp = netdev_priv(ndev);
+ struct phy_device *phydev = pp->phy_dev;
+ int status_change = 0;
+
+ if (phydev->link) {
+ if ((pp->speed != phydev->speed) ||
+ (pp->duplex != phydev->duplex)) {
+ u32 val;
+
+ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
+ val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED |
+ MVNETA_GMAC_CONFIG_GMII_SPEED |
+ MVNETA_GMAC_CONFIG_FULL_DUPLEX);
+
+ if (phydev->duplex)
+ val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
+
+ if (phydev->speed == SPEED_1000)
+ val |= MVNETA_GMAC_CONFIG_GMII_SPEED;
+ else
+ val |= MVNETA_GMAC_CONFIG_MII_SPEED;
+
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+
+ pp->duplex = phydev->duplex;
+ pp->speed = phydev->speed;
+ }
+ }
+
+ if (phydev->link != pp->link) {
+ if (!phydev->link) {
+ pp->duplex = -1;
+ pp->speed = 0;
+ }
+
+ pp->link = phydev->link;
+ status_change = 1;
+ }
+
+ if (status_change) {
+ if (phydev->link) {
+ u32 val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
+ val |= (MVNETA_GMAC_FORCE_LINK_PASS |
+ MVNETA_GMAC_FORCE_LINK_DOWN);
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+ mvneta_port_up(pp);
+ netdev_info(pp->dev, "link up\n");
+ } else {
+ mvneta_port_down(pp);
+ netdev_info(pp->dev, "link down\n");
+ }
+ }
+}
+
+static int mvneta_mdio_probe(struct mvneta_port *pp)
+{
+ struct phy_device *phy_dev;
+
+ phy_dev = of_phy_connect(pp->dev, pp->phy_node, mvneta_adjust_link, 0,
+ pp->phy_interface);
+ if (!phy_dev) {
+ netdev_err(pp->dev, "could not find the PHY\n");
+ return -ENODEV;
+ }
+
+ phy_dev->supported &= PHY_GBIT_FEATURES;
+ phy_dev->advertising = phy_dev->supported;
+
+ pp->phy_dev = phy_dev;
+ pp->link = 0;
+ pp->duplex = 0;
+ pp->speed = 0;
+
+ return 0;
+}
+
+static void mvneta_mdio_remove(struct mvneta_port *pp)
+{
+ phy_disconnect(pp->phy_dev);
+ pp->phy_dev = NULL;
+}
+
+static int mvneta_open(struct net_device *dev)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+ int ret;
+
+ mvneta_mac_addr_set(pp, dev->dev_addr, rxq_def);
+
+ pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu);
+
+ ret = mvneta_setup_rxqs(pp);
+ if (ret)
+ return ret;
+
+ ret = mvneta_setup_txqs(pp);
+ if (ret)
+ goto err_cleanup_rxqs;
+
+ /* Connect to port interrupt line */
+ ret = request_irq(pp->dev->irq, mvneta_isr, 0,
+ MVNETA_DRIVER_NAME, pp);
+ if (ret) {
+ netdev_err(pp->dev, "cannot request irq %d\n", pp->dev->irq);
+ goto err_cleanup_txqs;
+ }
+
+ /* In default link is down */
+ netif_carrier_off(pp->dev);
+
+ ret = mvneta_mdio_probe(pp);
+ if (ret < 0) {
+ netdev_err(dev, "cannot probe MDIO bus\n");
+ goto err_free_irq;
+ }
+
+ mvneta_start_dev(pp);
+
+ return 0;
+
+err_free_irq:
+ free_irq(pp->dev->irq, pp);
+err_cleanup_txqs:
+ mvneta_cleanup_txqs(pp);
+err_cleanup_rxqs:
+ mvneta_cleanup_rxqs(pp);
+ return ret;
+}
+
+/* Stop the port, free port interrupt line */
+static int mvneta_stop(struct net_device *dev)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+
+ mvneta_stop_dev(pp);
+ mvneta_mdio_remove(pp);
+ free_irq(dev->irq, pp);
+ mvneta_cleanup_rxqs(pp);
+ mvneta_cleanup_txqs(pp);
+ del_timer(&pp->tx_done_timer);
+ clear_bit(MVNETA_F_TX_DONE_TIMER_BIT, &pp->flags);
+
+ return 0;
+}
+
+/* Ethtool methods */
+
+/* Get settings (phy address, speed) for ethtools */
+int mvneta_ethtool_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+
+ if (!pp->phy_dev)
+ return -ENODEV;
+
+ return phy_ethtool_gset(pp->phy_dev, cmd);
+}
+
+/* Set settings (phy address, speed) for ethtools */
+int mvneta_ethtool_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+
+ if (!pp->phy_dev)
+ return -ENODEV;
+
+ return phy_ethtool_sset(pp->phy_dev, cmd);
+}
+
+/* Set interrupt coalescing for ethtools */
+static int mvneta_ethtool_set_coalesce(struct net_device *dev,
+ struct ethtool_coalesce *c)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+ int queue;
+
+ for (queue = 0; queue < rxq_number; queue++) {
+ struct mvneta_rx_queue *rxq = &pp->rxqs[queue];
+ rxq->time_coal = c->rx_coalesce_usecs;
+ rxq->pkts_coal = c->rx_max_coalesced_frames;
+ mvneta_rx_pkts_coal_set(pp, rxq, rxq->pkts_coal);
+ mvneta_rx_time_coal_set(pp, rxq, rxq->time_coal);
+ }
+
+ for (queue = 0; queue < txq_number; queue++) {
+ struct mvneta_tx_queue *txq = &pp->txqs[queue];
+ txq->done_pkts_coal = c->tx_max_coalesced_frames;
+ mvneta_tx_done_pkts_coal_set(pp, txq, txq->done_pkts_coal);
+ }
+
+ return 0;
+}
+
+/* get coalescing for ethtools */
+static int mvneta_ethtool_get_coalesce(struct net_device *dev,
+ struct ethtool_coalesce *c)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+
+ c->rx_coalesce_usecs = pp->rxqs[0].time_coal;
+ c->rx_max_coalesced_frames = pp->rxqs[0].pkts_coal;
+
+ c->tx_max_coalesced_frames = pp->txqs[0].done_pkts_coal;
+ return 0;
+}
+
+
+static void mvneta_ethtool_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ strlcpy(drvinfo->driver, MVNETA_DRIVER_NAME,
+ sizeof(drvinfo->driver));
+ strlcpy(drvinfo->version, MVNETA_DRIVER_VERSION,
+ sizeof(drvinfo->version));
+ strlcpy(drvinfo->bus_info, dev_name(&dev->dev),
+ sizeof(drvinfo->bus_info));
+}
+
+
+static void mvneta_ethtool_get_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ring)
+{
+ struct mvneta_port *pp = netdev_priv(netdev);
+
+ ring->rx_max_pending = MVNETA_MAX_RXD;
+ ring->tx_max_pending = MVNETA_MAX_TXD;
+ ring->rx_pending = pp->rx_ring_size;
+ ring->tx_pending = pp->tx_ring_size;
+}
+
+static int mvneta_ethtool_set_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *ring)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+
+ if ((ring->rx_pending == 0) || (ring->tx_pending == 0))
+ return -EINVAL;
+ pp->rx_ring_size = ring->rx_pending < MVNETA_MAX_RXD ?
+ ring->rx_pending : MVNETA_MAX_RXD;
+ pp->tx_ring_size = ring->tx_pending < MVNETA_MAX_TXD ?
+ ring->tx_pending : MVNETA_MAX_TXD;
+
+ if (netif_running(dev)) {
+ mvneta_stop(dev);
+ if (mvneta_open(dev)) {
+ netdev_err(dev,
+ "error on opening device after ring param change\n");
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static const struct net_device_ops mvneta_netdev_ops = {
+ .ndo_open = mvneta_open,
+ .ndo_stop = mvneta_stop,
+ .ndo_start_xmit = mvneta_tx,
+ .ndo_set_rx_mode = mvneta_set_rx_mode,
+ .ndo_set_mac_address = mvneta_set_mac_addr,
+ .ndo_change_mtu = mvneta_change_mtu,
+ .ndo_tx_timeout = mvneta_tx_timeout,
+ .ndo_get_stats64 = mvneta_get_stats64,
+};
+
+const struct ethtool_ops mvneta_eth_tool_ops = {
+ .get_link = ethtool_op_get_link,
+ .get_settings = mvneta_ethtool_get_settings,
+ .set_settings = mvneta_ethtool_set_settings,
+ .set_coalesce = mvneta_ethtool_set_coalesce,
+ .get_coalesce = mvneta_ethtool_get_coalesce,
+ .get_drvinfo = mvneta_ethtool_get_drvinfo,
+ .get_ringparam = mvneta_ethtool_get_ringparam,
+ .set_ringparam = mvneta_ethtool_set_ringparam,
+};
+
+/* Initialize hw */
+static int __devinit mvneta_init(struct mvneta_port *pp, int phy_addr)
+{
+ int queue;
+
+ /* Disable port */
+ mvneta_port_disable(pp);
+
+ /* Set port default values */
+ mvneta_defaults_set(pp);
+
+ pp->txqs = kzalloc(txq_number * sizeof(struct mvneta_tx_queue),
+ GFP_KERNEL);
+ if (!pp->txqs)
+ return -ENOMEM;
+
+ /* Initialize TX descriptor rings */
+ for (queue = 0; queue < txq_number; queue++) {
+ struct mvneta_tx_queue *txq = &pp->txqs[queue];
+ txq->id = queue;
+ txq->size = pp->tx_ring_size;
+ txq->done_pkts_coal = MVNETA_TXDONE_COAL_PKTS;
+ }
+
+ pp->rxqs = kzalloc(rxq_number * sizeof(struct mvneta_rx_queue),
+ GFP_KERNEL);
+ if (!pp->rxqs) {
+ kfree(pp->txqs);
+ return -ENOMEM;
+ }
+
+ /* Create Rx descriptor rings */
+ for (queue = 0; queue < rxq_number; queue++) {
+ struct mvneta_rx_queue *rxq = &pp->rxqs[queue];
+ rxq->id = queue;
+ rxq->size = pp->rx_ring_size;
+ rxq->pkts_coal = MVNETA_RX_COAL_PKTS;
+ rxq->time_coal = MVNETA_RX_COAL_USEC;
+ }
+
+ return 0;
+}
+
+static void mvneta_deinit(struct mvneta_port *pp)
+{
+ kfree(pp->txqs);
+ kfree(pp->rxqs);
+}
+
+/* platform glue : initialize decoding windows */
+static void __devinit
+mvneta_conf_mbus_windows(struct mvneta_port *pp,
+ const struct mbus_dram_target_info *dram)
+{
+ u32 win_enable;
+ u32 win_protect;
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ mvreg_write(pp, MVNETA_WIN_BASE(i), 0);
+ mvreg_write(pp, MVNETA_WIN_SIZE(i), 0);
+
+ if (i < 4)
+ mvreg_write(pp, MVNETA_WIN_REMAP(i), 0);
+ }
+
+ win_enable = 0x3f;
+ win_protect = 0;
+
+ for (i = 0; i < dram->num_cs; i++) {
+ const struct mbus_dram_window *cs = dram->cs + i;
+ mvreg_write(pp, MVNETA_WIN_BASE(i), (cs->base & 0xffff0000) |
+ (cs->mbus_attr << 8) | dram->mbus_dram_target_id);
+
+ mvreg_write(pp, MVNETA_WIN_SIZE(i),
+ (cs->size - 1) & 0xffff0000);
+
+ win_enable &= ~(1 << i);
+ win_protect |= 3 << (2 * i);
+ }
+
+ mvreg_write(pp, MVNETA_BASE_ADDR_ENABLE, win_enable);
+}
+
+/* Power up the port */
+static void __devinit mvneta_port_power_up(struct mvneta_port *pp, int phy_mode)
+{
+ u32 val;
+
+ /* MAC Cause register should be cleared */
+ mvreg_write(pp, MVNETA_UNIT_INTR_CAUSE, 0);
+
+ if (phy_mode == PHY_INTERFACE_MODE_SGMII)
+ mvneta_port_sgmii_config(pp);
+
+ mvneta_gmac_rgmii_set(pp, 1);
+
+ /* Cancel Port Reset */
+ val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
+ val &= ~MVNETA_GMAC2_PORT_RESET;
+ mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
+
+ while ((mvreg_read(pp, MVNETA_GMAC_CTRL_2) &
+ MVNETA_GMAC2_PORT_RESET) != 0)
+ continue;
+}
+
+/* Device initialization routine */
+static int __devinit mvneta_probe(struct platform_device *pdev)
+{
+ const struct mbus_dram_target_info *dram_target_info;
+ struct device_node *dn = pdev->dev.of_node;
+ struct device_node *phy_node;
+ u32 phy_addr;
+ struct mvneta_port *pp;
+ struct net_device *dev;
+ const char *mac_addr;
+ int phy_mode;
+ int err;
+
+ /* Our multiqueue support is not complete, so for now, only
+ * allow the usage of the first RX queue
+ */
+ if (rxq_def != 0) {
+ dev_err(&pdev->dev, "Invalid rxq_def argument: %d\n", rxq_def);
+ return -EINVAL;
+ }
+
+ dev = alloc_etherdev_mq(sizeof(struct mvneta_port), 8);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->irq = irq_of_parse_and_map(dn, 0);
+ if (dev->irq == 0) {
+ err = -EINVAL;
+ goto err_free_netdev;
+ }
+
+ phy_node = of_parse_phandle(dn, "phy", 0);
+ if (!phy_node) {
+ dev_err(&pdev->dev, "no associated PHY\n");
+ err = -ENODEV;
+ goto err_free_irq;
+ }
+
+ phy_mode = of_get_phy_mode(dn);
+ if (phy_mode < 0) {
+ dev_err(&pdev->dev, "incorrect phy-mode\n");
+ err = -EINVAL;
+ goto err_free_irq;
+ }
+
+ mac_addr = of_get_mac_address(dn);
+
+ if (!mac_addr || !is_valid_ether_addr(mac_addr))
+ eth_hw_addr_random(dev);
+ else
+ memcpy(dev->dev_addr, mac_addr, ETH_ALEN);
+
+ dev->tx_queue_len = MVNETA_MAX_TXD;
+ dev->watchdog_timeo = 5 * HZ;
+ dev->netdev_ops = &mvneta_netdev_ops;
+
+ SET_ETHTOOL_OPS(dev, &mvneta_eth_tool_ops);
+
+ pp = netdev_priv(dev);
+
+ pp->tx_done_timer.function = mvneta_tx_done_timer_callback;
+ init_timer(&pp->tx_done_timer);
+ clear_bit(MVNETA_F_TX_DONE_TIMER_BIT, &pp->flags);
+
+ pp->weight = MVNETA_RX_POLL_WEIGHT;
+ pp->phy_node = phy_node;
+ pp->phy_interface = phy_mode;
+
+ pp->base = of_iomap(dn, 0);
+ if (pp->base == NULL) {
+ err = -ENOMEM;
+ goto err_free_irq;
+ }
+
+ pp->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(pp->clk)) {
+ err = PTR_ERR(pp->clk);
+ goto err_unmap;
+ }
+
+ clk_prepare_enable(pp->clk);
+
+ pp->tx_done_timer.data = (unsigned long)dev;
+
+ pp->tx_ring_size = MVNETA_MAX_TXD;
+ pp->rx_ring_size = MVNETA_MAX_RXD;
+
+ pp->dev = dev;
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ err = mvneta_init(pp, phy_addr);
+ if (err < 0) {
+ dev_err(&pdev->dev, "can't init eth hal\n");
+ goto err_clk;
+ }
+ mvneta_port_power_up(pp, phy_mode);
+
+ dram_target_info = mv_mbus_dram_info();
+ if (dram_target_info)
+ mvneta_conf_mbus_windows(pp, dram_target_info);
+
+ netif_napi_add(dev, &pp->napi, mvneta_poll, pp->weight);
+
+ err = register_netdev(dev);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to register\n");
+ goto err_deinit;
+ }
+
+ dev->features = NETIF_F_SG | NETIF_F_IP_CSUM;
+ dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM;
+ dev->priv_flags |= IFF_UNICAST_FLT;
+
+ netdev_info(dev, "mac: %pM\n", dev->dev_addr);
+
+ platform_set_drvdata(pdev, pp->dev);
+
+ return 0;
+
+err_deinit:
+ mvneta_deinit(pp);
+err_clk:
+ clk_disable_unprepare(pp->clk);
+err_unmap:
+ iounmap(pp->base);
+err_free_irq:
+ irq_dispose_mapping(dev->irq);
+err_free_netdev:
+ free_netdev(dev);
+ return err;
+}
+
+/* Device removal routine */
+static int __devexit mvneta_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct mvneta_port *pp = netdev_priv(dev);
+
+ unregister_netdev(dev);
+ mvneta_deinit(pp);
+ clk_disable_unprepare(pp->clk);
+ iounmap(pp->base);
+ irq_dispose_mapping(dev->irq);
+ free_netdev(dev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static const struct of_device_id mvneta_match[] = {
+ { .compatible = "marvell,armada-370-neta" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mvneta_match);
+
+static struct platform_driver mvneta_driver = {
+ .probe = mvneta_probe,
+ .remove = __devexit_p(mvneta_remove),
+ .driver = {
+ .name = MVNETA_DRIVER_NAME,
+ .of_match_table = mvneta_match,
+ },
+};
+
+module_platform_driver(mvneta_driver);
+
+MODULE_DESCRIPTION("Marvell NETA Ethernet Driver - www.marvell.com");
+MODULE_AUTHOR("Rami Rosen <rosenr@marvell.com>, Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
+MODULE_LICENSE("GPL");
+
+module_param(rxq_number, int, S_IRUGO);
+module_param(txq_number, int, S_IRUGO);
+
+module_param(rxq_def, int, S_IRUGO);
+module_param(txq_def, int, S_IRUGO);
diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c
index 3d1899ff107..fdc5f23d8e9 100644
--- a/drivers/net/ethernet/mellanox/mlx4/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c
@@ -1498,6 +1498,7 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd,
u32 reply;
u8 is_going_down = 0;
int i;
+ unsigned long flags;
slave_state[slave].comm_toggle ^= 1;
reply = (u32) slave_state[slave].comm_toggle << 31;
@@ -1576,12 +1577,12 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd,
mlx4_warn(dev, "Bad comm cmd:%d from slave:%d\n", cmd, slave);
goto reset_slave;
}
- spin_lock(&priv->mfunc.master.slave_state_lock);
+ spin_lock_irqsave(&priv->mfunc.master.slave_state_lock, flags);
if (!slave_state[slave].is_slave_going_down)
slave_state[slave].last_cmd = cmd;
else
is_going_down = 1;
- spin_unlock(&priv->mfunc.master.slave_state_lock);
+ spin_unlock_irqrestore(&priv->mfunc.master.slave_state_lock, flags);
if (is_going_down) {
mlx4_warn(dev, "Slave is going down aborting command(%d)"
" executing from slave:%d\n",
@@ -1597,10 +1598,10 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd,
reset_slave:
/* cleanup any slave resources */
mlx4_delete_all_resources_for_slave(dev, slave);
- spin_lock(&priv->mfunc.master.slave_state_lock);
+ spin_lock_irqsave(&priv->mfunc.master.slave_state_lock, flags);
if (!slave_state[slave].is_slave_going_down)
slave_state[slave].last_cmd = MLX4_COMM_CMD_RESET;
- spin_unlock(&priv->mfunc.master.slave_state_lock);
+ spin_unlock_irqrestore(&priv->mfunc.master.slave_state_lock, flags);
/*with slave in the middle of flr, no need to clean resources again.*/
inform_slave_state:
memset(&slave_state[slave].event_eq, 0,
@@ -1755,7 +1756,7 @@ int mlx4_multi_func_init(struct mlx4_dev *dev)
spin_lock_init(&s_state->lock);
}
- memset(&priv->mfunc.master.cmd_eqe, 0, sizeof(struct mlx4_eqe));
+ memset(&priv->mfunc.master.cmd_eqe, 0, dev->caps.eqe_size);
priv->mfunc.master.cmd_eqe.type = MLX4_EVENT_TYPE_CMD;
INIT_WORK(&priv->mfunc.master.comm_work,
mlx4_master_comm_channel);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c
index aa9c2f6cf3c..b8d0854a7ad 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c
@@ -51,7 +51,7 @@ int mlx4_en_create_cq(struct mlx4_en_priv *priv,
int err;
cq->size = entries;
- cq->buf_size = cq->size * sizeof(struct mlx4_cqe);
+ cq->buf_size = cq->size * mdev->dev->caps.cqe_size;
cq->ring = ring;
cq->is_tx = mode;
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 7d1287f81a3..75a3f467bb5 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -1604,6 +1604,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
goto out;
}
priv->rx_ring_num = prof->rx_ring_num;
+ priv->cqe_factor = (mdev->dev->caps.cqe_size == 64) ? 1 : 0;
priv->mac_index = -1;
priv->msg_enable = MLX4_EN_MSG_LEVEL;
spin_lock_init(&priv->stats_lock);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index f76c9671f36..fed26d867f4 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -566,6 +566,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
struct ethhdr *ethh;
dma_addr_t dma;
u64 s_mac;
+ int factor = priv->cqe_factor;
if (!priv->port_up)
return 0;
@@ -574,7 +575,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
* descriptor offset can be deduced from the CQE index instead of
* reading 'cqe->index' */
index = cq->mcq.cons_index & ring->size_mask;
- cqe = &cq->buf[index];
+ cqe = &cq->buf[(index << factor) + factor];
/* Process all completed CQEs */
while (XNOR(cqe->owner_sr_opcode & MLX4_CQE_OWNER_MASK,
@@ -709,7 +710,7 @@ next:
++cq->mcq.cons_index;
index = (cq->mcq.cons_index) & ring->size_mask;
- cqe = &cq->buf[index];
+ cqe = &cq->buf[(index << factor) + factor];
if (++polled == budget)
goto out;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
index 1f571d00915..2b799f4f1c3 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
@@ -315,12 +315,13 @@ static void mlx4_en_process_tx_cq(struct net_device *dev, struct mlx4_en_cq *cq)
struct mlx4_cqe *buf = cq->buf;
u32 packets = 0;
u32 bytes = 0;
+ int factor = priv->cqe_factor;
if (!priv->port_up)
return;
index = cons_index & size_mask;
- cqe = &buf[index];
+ cqe = &buf[(index << factor) + factor];
ring_index = ring->cons & size_mask;
/* Process all completed CQEs */
@@ -349,7 +350,7 @@ static void mlx4_en_process_tx_cq(struct net_device *dev, struct mlx4_en_cq *cq)
++cons_index;
index = cons_index & size_mask;
- cqe = &buf[index];
+ cqe = &buf[(index << factor) + factor];
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c
index b84a88bc44d..251ae2f9311 100644
--- a/drivers/net/ethernet/mellanox/mlx4/eq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/eq.c
@@ -101,15 +101,21 @@ static void eq_set_ci(struct mlx4_eq *eq, int req_not)
mb();
}
-static struct mlx4_eqe *get_eqe(struct mlx4_eq *eq, u32 entry)
+static struct mlx4_eqe *get_eqe(struct mlx4_eq *eq, u32 entry, u8 eqe_factor)
{
- unsigned long off = (entry & (eq->nent - 1)) * MLX4_EQ_ENTRY_SIZE;
- return eq->page_list[off / PAGE_SIZE].buf + off % PAGE_SIZE;
+ /* (entry & (eq->nent - 1)) gives us a cyclic array */
+ unsigned long offset = (entry & (eq->nent - 1)) * (MLX4_EQ_ENTRY_SIZE << eqe_factor);
+ /* CX3 is capable of extending the EQE from 32 to 64 bytes.
+ * When this feature is enabled, the first (in the lower addresses)
+ * 32 bytes in the 64 byte EQE are reserved and the next 32 bytes
+ * contain the legacy EQE information.
+ */
+ return eq->page_list[offset / PAGE_SIZE].buf + (offset + (eqe_factor ? MLX4_EQ_ENTRY_SIZE : 0)) % PAGE_SIZE;
}
-static struct mlx4_eqe *next_eqe_sw(struct mlx4_eq *eq)
+static struct mlx4_eqe *next_eqe_sw(struct mlx4_eq *eq, u8 eqe_factor)
{
- struct mlx4_eqe *eqe = get_eqe(eq, eq->cons_index);
+ struct mlx4_eqe *eqe = get_eqe(eq, eq->cons_index, eqe_factor);
return !!(eqe->owner & 0x80) ^ !!(eq->cons_index & eq->nent) ? NULL : eqe;
}
@@ -177,7 +183,7 @@ static void slave_event(struct mlx4_dev *dev, u8 slave, struct mlx4_eqe *eqe)
return;
}
- memcpy(s_eqe, eqe, sizeof(struct mlx4_eqe) - 1);
+ memcpy(s_eqe, eqe, dev->caps.eqe_size - 1);
s_eqe->slave_id = slave;
/* ensure all information is written before setting the ownersip bit */
wmb();
@@ -401,6 +407,7 @@ void mlx4_master_handle_slave_flr(struct work_struct *work)
struct mlx4_slave_state *slave_state = priv->mfunc.master.slave_state;
int i;
int err;
+ unsigned long flags;
mlx4_dbg(dev, "mlx4_handle_slave_flr\n");
@@ -412,10 +419,10 @@ void mlx4_master_handle_slave_flr(struct work_struct *work)
mlx4_delete_all_resources_for_slave(dev, i);
/*return the slave to running mode*/
- spin_lock(&priv->mfunc.master.slave_state_lock);
+ spin_lock_irqsave(&priv->mfunc.master.slave_state_lock, flags);
slave_state[i].last_cmd = MLX4_COMM_CMD_RESET;
slave_state[i].is_slave_going_down = 0;
- spin_unlock(&priv->mfunc.master.slave_state_lock);
+ spin_unlock_irqrestore(&priv->mfunc.master.slave_state_lock, flags);
/*notify the FW:*/
err = mlx4_cmd(dev, 0, i, 0, MLX4_CMD_INFORM_FLR_DONE,
MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED);
@@ -440,8 +447,9 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
u8 update_slave_state;
int i;
enum slave_port_gen_event gen_event;
+ unsigned long flags;
- while ((eqe = next_eqe_sw(eq))) {
+ while ((eqe = next_eqe_sw(eq, dev->caps.eqe_factor))) {
/*
* Make sure we read EQ entry contents after we've
* checked the ownership bit.
@@ -647,13 +655,13 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
} else
update_slave_state = 1;
- spin_lock(&priv->mfunc.master.slave_state_lock);
+ spin_lock_irqsave(&priv->mfunc.master.slave_state_lock, flags);
if (update_slave_state) {
priv->mfunc.master.slave_state[flr_slave].active = false;
priv->mfunc.master.slave_state[flr_slave].last_cmd = MLX4_COMM_CMD_FLR;
priv->mfunc.master.slave_state[flr_slave].is_slave_going_down = 1;
}
- spin_unlock(&priv->mfunc.master.slave_state_lock);
+ spin_unlock_irqrestore(&priv->mfunc.master.slave_state_lock, flags);
queue_work(priv->mfunc.master.comm_wq,
&priv->mfunc.master.slave_flr_event_work);
break;
@@ -864,7 +872,8 @@ static int mlx4_create_eq(struct mlx4_dev *dev, int nent,
eq->dev = dev;
eq->nent = roundup_pow_of_two(max(nent, 2));
- npages = PAGE_ALIGN(eq->nent * MLX4_EQ_ENTRY_SIZE) / PAGE_SIZE;
+ /* CX3 is capable of extending the CQE/EQE from 32 to 64 bytes */
+ npages = PAGE_ALIGN(eq->nent * (MLX4_EQ_ENTRY_SIZE << dev->caps.eqe_factor)) / PAGE_SIZE;
eq->page_list = kmalloc(npages * sizeof *eq->page_list,
GFP_KERNEL);
@@ -966,8 +975,9 @@ static void mlx4_free_eq(struct mlx4_dev *dev,
struct mlx4_priv *priv = mlx4_priv(dev);
struct mlx4_cmd_mailbox *mailbox;
int err;
- int npages = PAGE_ALIGN(MLX4_EQ_ENTRY_SIZE * eq->nent) / PAGE_SIZE;
int i;
+ /* CX3 is capable of extending the CQE/EQE from 32 to 64 bytes */
+ int npages = PAGE_ALIGN((MLX4_EQ_ENTRY_SIZE << dev->caps.eqe_factor) * eq->nent) / PAGE_SIZE;
mailbox = mlx4_alloc_cmd_mailbox(dev);
if (IS_ERR(mailbox))
@@ -1267,7 +1277,7 @@ int mlx4_test_interrupts(struct mlx4_dev *dev)
/* Temporary use polling for command completions */
mlx4_cmd_use_polling(dev);
- /* Map the new eq to handle all asyncronous events */
+ /* Map the new eq to handle all asynchronous events */
err = mlx4_MAP_EQ(dev, get_async_ev_mask(dev), 0,
priv->eq_table.eq[i].eqn);
if (err) {
diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c
index 4f30b99324c..9a9de51ecc9 100644
--- a/drivers/net/ethernet/mellanox/mlx4/fw.c
+++ b/drivers/net/ethernet/mellanox/mlx4/fw.c
@@ -110,6 +110,8 @@ static void dump_dev_cap_flags(struct mlx4_dev *dev, u64 flags)
[42] = "Multicast VEP steering support",
[48] = "Counters support",
[59] = "Port management change event support",
+ [61] = "64 byte EQE support",
+ [62] = "64 byte CQE support",
};
int i;
@@ -235,7 +237,7 @@ int mlx4_QUERY_FUNC_CAP_wrapper(struct mlx4_dev *dev, int slave,
field = dev->caps.num_ports;
MLX4_PUT(outbox->buf, field, QUERY_FUNC_CAP_NUM_PORTS_OFFSET);
- size = 0; /* no PF behaviour is set for now */
+ size = dev->caps.function_caps; /* set PF behaviours */
MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_PF_BHVR_OFFSET);
field = 0; /* protected FMR support not available as yet */
@@ -1237,6 +1239,24 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param)
if (dev->caps.flags & MLX4_DEV_CAP_FLAG_COUNTERS)
*(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1 << 4);
+ /* CX3 is capable of extending CQEs/EQEs from 32 to 64 bytes */
+ if (dev->caps.flags & MLX4_DEV_CAP_FLAG_64B_EQE) {
+ *(inbox + INIT_HCA_EQE_CQE_OFFSETS / 4) |= cpu_to_be32(1 << 29);
+ dev->caps.eqe_size = 64;
+ dev->caps.eqe_factor = 1;
+ } else {
+ dev->caps.eqe_size = 32;
+ dev->caps.eqe_factor = 0;
+ }
+
+ if (dev->caps.flags & MLX4_DEV_CAP_FLAG_64B_CQE) {
+ *(inbox + INIT_HCA_EQE_CQE_OFFSETS / 4) |= cpu_to_be32(1 << 30);
+ dev->caps.cqe_size = 64;
+ dev->caps.userspace_caps |= MLX4_USER_DEV_CAP_64B_CQE;
+ } else {
+ dev->caps.cqe_size = 32;
+ }
+
/* QPC/EEC/CQC/EQC/RDMARC attributes */
MLX4_PUT(inbox, param->qpc_base, INIT_HCA_QPC_BASE_OFFSET);
@@ -1319,6 +1339,7 @@ int mlx4_QUERY_HCA(struct mlx4_dev *dev,
struct mlx4_cmd_mailbox *mailbox;
__be32 *outbox;
int err;
+ u8 byte_field;
#define QUERY_HCA_GLOBAL_CAPS_OFFSET 0x04
@@ -1370,6 +1391,13 @@ int mlx4_QUERY_HCA(struct mlx4_dev *dev,
INIT_HCA_LOG_MC_TABLE_SZ_OFFSET);
}
+ /* CX3 is capable of extending CQEs/EQEs from 32 to 64 bytes */
+ MLX4_GET(byte_field, outbox, INIT_HCA_EQE_CQE_OFFSETS);
+ if (byte_field & 0x20) /* 64-bytes eqe enabled */
+ param->dev_cap_enabled |= MLX4_DEV_CAP_64B_EQE_ENABLED;
+ if (byte_field & 0x40) /* 64-bytes cqe enabled */
+ param->dev_cap_enabled |= MLX4_DEV_CAP_64B_CQE_ENABLED;
+
/* TPT attributes */
MLX4_GET(param->dmpt_base, outbox, INIT_HCA_DMPT_BASE_OFFSET);
diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.h b/drivers/net/ethernet/mellanox/mlx4/fw.h
index 85abe9c11a2..2c2e7ade2a3 100644
--- a/drivers/net/ethernet/mellanox/mlx4/fw.h
+++ b/drivers/net/ethernet/mellanox/mlx4/fw.h
@@ -172,6 +172,7 @@ struct mlx4_init_hca_param {
u8 log_uar_sz;
u8 uar_page_sz; /* log pg sz in 4k chunks */
u8 fs_hash_enable_bits;
+ u64 dev_cap_enabled;
};
struct mlx4_init_ib_param {
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index 200cc0ec805..b2acbe7706a 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -95,8 +95,14 @@ MODULE_PARM_DESC(log_num_mgm_entry_size, "log mgm size, that defines the num"
" Not in use with device managed"
" flow steering");
+static bool enable_64b_cqe_eqe;
+module_param(enable_64b_cqe_eqe, bool, 0444);
+MODULE_PARM_DESC(enable_64b_cqe_eqe,
+ "Enable 64 byte CQEs/EQEs when the the FW supports this");
+
#define HCA_GLOBAL_CAP_MASK 0
-#define PF_CONTEXT_BEHAVIOUR_MASK 0
+
+#define PF_CONTEXT_BEHAVIOUR_MASK MLX4_FUNC_CAP_64B_EQE_CQE
static char mlx4_version[] =
DRV_NAME ": Mellanox ConnectX core driver v"
@@ -386,6 +392,21 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
dev->caps.reserved_qps_cnt[MLX4_QP_REGION_FC_EXCH];
dev->caps.sqp_demux = (mlx4_is_master(dev)) ? MLX4_MAX_NUM_SLAVES : 0;
+
+ if (!enable_64b_cqe_eqe) {
+ if (dev_cap->flags &
+ (MLX4_DEV_CAP_FLAG_64B_CQE | MLX4_DEV_CAP_FLAG_64B_EQE)) {
+ mlx4_warn(dev, "64B EQEs/CQEs supported by the device but not enabled\n");
+ dev->caps.flags &= ~MLX4_DEV_CAP_FLAG_64B_CQE;
+ dev->caps.flags &= ~MLX4_DEV_CAP_FLAG_64B_EQE;
+ }
+ }
+
+ if ((dev_cap->flags &
+ (MLX4_DEV_CAP_FLAG_64B_CQE | MLX4_DEV_CAP_FLAG_64B_EQE)) &&
+ mlx4_is_master(dev))
+ dev->caps.function_caps |= MLX4_FUNC_CAP_64B_EQE_CQE;
+
return 0;
}
/*The function checks if there are live vf, return the num of them*/
@@ -599,6 +620,21 @@ static int mlx4_slave_cap(struct mlx4_dev *dev)
goto err_mem;
}
+ if (hca_param.dev_cap_enabled & MLX4_DEV_CAP_64B_EQE_ENABLED) {
+ dev->caps.eqe_size = 64;
+ dev->caps.eqe_factor = 1;
+ } else {
+ dev->caps.eqe_size = 32;
+ dev->caps.eqe_factor = 0;
+ }
+
+ if (hca_param.dev_cap_enabled & MLX4_DEV_CAP_64B_CQE_ENABLED) {
+ dev->caps.cqe_size = 64;
+ dev->caps.userspace_caps |= MLX4_USER_DEV_CAP_64B_CQE;
+ } else {
+ dev->caps.cqe_size = 32;
+ }
+
return 0;
err_mem:
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index 334ec483480..8d54412ada6 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -473,6 +473,7 @@ struct mlx4_en_priv {
int mac_index;
unsigned max_mtu;
int base_qpn;
+ int cqe_factor;
struct mlx4_en_rss_map rss_map;
__be32 ctrl_flags;
diff --git a/drivers/net/ethernet/micrel/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c
index 093d594435e..83f0ea929d3 100644
--- a/drivers/net/ethernet/micrel/ksz884x.c
+++ b/drivers/net/ethernet/micrel/ksz884x.c
@@ -6769,7 +6769,7 @@ static int stp;
/*
* This enables fast aging in the KSZ8842 switch. Not sure what situation
* needs that. However, fast aging is used to flush the dynamic MAC table when
- * STP suport is enabled.
+ * STP support is enabled.
*/
static int fast_aging;
diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge_mcp_gen_header.h b/drivers/net/ethernet/myricom/myri10ge/myri10ge_mcp_gen_header.h
index 7ec4b864a55..75ec5e7cf50 100644
--- a/drivers/net/ethernet/myricom/myri10ge/myri10ge_mcp_gen_header.h
+++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge_mcp_gen_header.h
@@ -27,7 +27,7 @@ struct mcp_gen_header {
*
* Fields below this comment are extensions added in later versions
* of this struct, drivers should compare the header_length against
- * offsetof(field) to check wether a given MCP implements them.
+ * offsetof(field) to check whether a given MCP implements them.
*
* Never remove any field. Keep everything naturally align.
*/
diff --git a/drivers/net/sungem_phy.c b/drivers/net/sungem_phy.c
index 58f13adaa54..ae7cd7f3656 100644
--- a/drivers/net/sungem_phy.c
+++ b/drivers/net/sungem_phy.c
@@ -608,7 +608,7 @@ static int bcm5421_poll_link(struct mii_phy* phy)
if ( mode == BCM54XX_COPPER)
return genmii_poll_link(phy);
- /* try to find out wether we have a link */
+ /* try to find out whether we have a link */
phy_write(phy, MII_NCONFIG, 0x2000);
phy_reg = phy_read(phy, MII_NCONFIG);
@@ -634,7 +634,7 @@ static int bcm5421_read_link(struct mii_phy* phy)
phy->speed = SPEED_1000;
- /* find out wether we are running half- or full duplex */
+ /* find out whether we are running half- or full duplex */
phy_write(phy, MII_NCONFIG, 0x2000);
phy_reg = phy_read(phy, MII_NCONFIG);
@@ -681,7 +681,7 @@ static int bcm5461_poll_link(struct mii_phy* phy)
if ( mode == BCM54XX_COPPER)
return genmii_poll_link(phy);
- /* find out wether we have a link */
+ /* find out whether we have a link */
phy_write(phy, MII_NCONFIG, 0x7000);
phy_reg = phy_read(phy, MII_NCONFIG);
@@ -710,7 +710,7 @@ static int bcm5461_read_link(struct mii_phy* phy)
phy->speed = SPEED_1000;
- /* find out wether we are running half- or full duplex */
+ /* find out whether we are running half- or full duplex */
phy_write(phy, MII_NCONFIG, 0x7000);
phy_reg = phy_read(phy, MII_NCONFIG);
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 2ac2164a1e3..40b426edc9e 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -297,13 +297,12 @@ static void tun_flow_cleanup(unsigned long data)
spin_unlock_bh(&tun->lock);
}
-static void tun_flow_update(struct tun_struct *tun, struct sk_buff *skb,
+static void tun_flow_update(struct tun_struct *tun, u32 rxhash,
u16 queue_index)
{
struct hlist_head *head;
struct tun_flow_entry *e;
unsigned long delay = tun->ageing_time;
- u32 rxhash = skb_get_rxhash(skb);
if (!rxhash)
return;
@@ -1010,6 +1009,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
int copylen;
bool zerocopy = false;
int err;
+ u32 rxhash;
if (!(tun->flags & TUN_NO_PI)) {
if ((len -= sizeof(pi)) > total_len)
@@ -1162,12 +1162,13 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY;
}
+ rxhash = skb_get_rxhash(skb);
netif_rx_ni(skb);
tun->dev->stats.rx_packets++;
tun->dev->stats.rx_bytes += len;
- tun_flow_update(tun, skb, tfile->queue_index);
+ tun_flow_update(tun, rxhash, tfile->queue_index);
return total_len;
}
diff --git a/drivers/net/wimax/i2400m/debugfs.c b/drivers/net/wimax/i2400m/debugfs.c
index 4b66ab1d0e5..6702da838b0 100644
--- a/drivers/net/wimax/i2400m/debugfs.c
+++ b/drivers/net/wimax/i2400m/debugfs.c
@@ -209,6 +209,7 @@ int debugfs_i2400m_reset_set(void *data, u64 val)
result = i2400m_reset(i2400m, rt);
if (result >= 0)
result = 0;
+ break;
default:
result = -EINVAL;
}
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 8e9b826f878..7ae73fbd913 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -114,23 +114,23 @@ static void ath_pci_aspm_init(struct ath_common *common)
if ((ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) &&
(AR_SREV_9285(ah))) {
- /* Bluetooth coexistance requires disabling ASPM. */
+ /* Bluetooth coexistence requires disabling ASPM. */
pcie_capability_clear_word(pdev, PCI_EXP_LNKCTL,
- PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
+ PCI_EXP_LNKCTL_ASPM_L0S | PCI_EXP_LNKCTL_ASPM_L1);
/*
* Both upstream and downstream PCIe components should
* have the same ASPM settings.
*/
pcie_capability_clear_word(parent, PCI_EXP_LNKCTL,
- PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
+ PCI_EXP_LNKCTL_ASPM_L0S | PCI_EXP_LNKCTL_ASPM_L1);
ath_info(common, "Disabling ASPM since BTCOEX is enabled\n");
return;
}
pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &aspm);
- if (aspm & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1)) {
+ if (aspm & (PCI_EXP_LNKCTL_ASPM_L0S | PCI_EXP_LNKCTL_ASPM_L1)) {
ah->aspm_enabled = true;
/* Initialize PCIe PM and SERDES registers. */
ath9k_hw_configpcipowersave(ah, false);
diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c
index 4a4e98f7180..4374079dfc2 100644
--- a/drivers/net/wireless/atmel.c
+++ b/drivers/net/wireless/atmel.c
@@ -2963,7 +2963,7 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc)
ssid_el_p[1] = priv->SSID_size;
memcpy(ssid_el_p + 2, priv->SSID, priv->SSID_size);
ssid_el_p[2 + priv->SSID_size] = WLAN_EID_SUPP_RATES;
- ssid_el_p[3 + priv->SSID_size] = 4; /* len of suported rates */
+ ssid_el_p[3 + priv->SSID_size] = 4; /* len of supported rates */
memcpy(ssid_el_p + 4 + priv->SSID_size, atmel_basic_rates, 4);
atmel_transmit_management_frame(priv, &header, (void *)&body, bodysize);
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.h b/drivers/net/wireless/ipw2x00/ipw2100.h
index 97312524249..5fe17cbab1f 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.h
+++ b/drivers/net/wireless/ipw2x00/ipw2100.h
@@ -1045,7 +1045,7 @@ typedef enum _ORDINAL_TABLE_1 { // NS - means Not Supported by FW
IPW_ORD_POWER_MGMT_MODE, // Power mode - 0=CAM, 1=PSP
IPW_ORD_POWER_MGMT_INDEX, //NS //
IPW_ORD_COUNTRY_CODE, // IEEE country code as recv'd from beacon
- IPW_ORD_COUNTRY_CHANNELS, // channels suported by country
+ IPW_ORD_COUNTRY_CHANNELS, // channels supported by country
// IPW_ORD_COUNTRY_CHANNELS:
// For 11b the lower 2-byte are used for channels from 1-14
// and the higher 2-byte are not used.
diff --git a/drivers/net/wireless/iwlegacy/4965.h b/drivers/net/wireless/iwlegacy/4965.h
index 2d092f32854..1b15b0b2292 100644
--- a/drivers/net/wireless/iwlegacy/4965.h
+++ b/drivers/net/wireless/iwlegacy/4965.h
@@ -917,10 +917,6 @@ struct il4965_scd_bc_tbl {
/* PCI registers */
#define PCI_CFG_RETRY_TIMEOUT 0x041
-/* PCI register values */
-#define PCI_CFG_LINK_CTRL_VAL_L0S_EN 0x01
-#define PCI_CFG_LINK_CTRL_VAL_L1_EN 0x02
-
#define IL4965_DEFAULT_TX_RETRY 15
/* EEPROM */
diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c
index 318ed3c9fe7..7e16d10a7f1 100644
--- a/drivers/net/wireless/iwlegacy/common.c
+++ b/drivers/net/wireless/iwlegacy/common.c
@@ -1183,9 +1183,10 @@ EXPORT_SYMBOL(il_power_update_mode);
void
il_power_initialize(struct il_priv *il)
{
- u16 lctl = il_pcie_link_ctl(il);
+ u16 lctl;
- il->power_data.pci_pm = !(lctl & PCI_CFG_LINK_CTRL_VAL_L0S_EN);
+ pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &lctl);
+ il->power_data.pci_pm = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S);
il->power_data.debug_sleep_level_override = -1;
@@ -4233,9 +4234,8 @@ il_apm_init(struct il_priv *il)
* power savings, even without L1.
*/
if (il->cfg->set_l0s) {
- lctl = il_pcie_link_ctl(il);
- if ((lctl & PCI_CFG_LINK_CTRL_VAL_L1_EN) ==
- PCI_CFG_LINK_CTRL_VAL_L1_EN) {
+ pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &lctl);
+ if (lctl & PCI_EXP_LNKCTL_ASPM_L1) {
/* L1-ASPM enabled; disable(!) L0S */
il_set_bit(il, CSR_GIO_REG,
CSR_GIO_REG_VAL_L0S_ENABLED);
diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h
index e254cba4557..a9a569f432f 100644
--- a/drivers/net/wireless/iwlegacy/common.h
+++ b/drivers/net/wireless/iwlegacy/common.h
@@ -1829,14 +1829,6 @@ int il_enqueue_hcmd(struct il_priv *il, struct il_host_cmd *cmd);
* PCI *
*****************************************************/
-static inline u16
-il_pcie_link_ctl(struct il_priv *il)
-{
- u16 pci_lnk_ctl;
- pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &pci_lnk_ctl);
- return pci_lnk_ctl;
-}
-
void il_bg_watchdog(unsigned long data);
u32 il_usecs_to_beacons(struct il_priv *il, u32 usec, u32 beacon_interval);
__le32 il_add_beacon_time(struct il_priv *il, u32 base, u32 addon,
@@ -2434,10 +2426,6 @@ struct il_tfd {
/* PCI registers */
#define PCI_CFG_RETRY_TIMEOUT 0x041
-/* PCI register values */
-#define PCI_CFG_LINK_CTRL_VAL_L0S_EN 0x01
-#define PCI_CFG_LINK_CTRL_VAL_L1_EN 0x02
-
struct il_rate_info {
u8 plcp; /* uCode API: RATE_6M_PLCP, etc. */
u8 plcp_siso; /* uCode API: RATE_SISO_6M_PLCP, etc. */
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index d66cad4a7d6..35708b959ad 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -94,8 +94,6 @@ static void iwl_pcie_set_pwr_vmain(struct iwl_trans *trans)
/* PCI registers */
#define PCI_CFG_RETRY_TIMEOUT 0x041
-#define PCI_CFG_LINK_CTRL_VAL_L0S_EN 0x01
-#define PCI_CFG_LINK_CTRL_VAL_L1_EN 0x02
static void iwl_pcie_apm_config(struct iwl_trans *trans)
{
@@ -111,9 +109,7 @@ static void iwl_pcie_apm_config(struct iwl_trans *trans)
* power savings, even without L1.
*/
pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_LNKCTL, &lctl);
-
- if ((lctl & PCI_CFG_LINK_CTRL_VAL_L1_EN) ==
- PCI_CFG_LINK_CTRL_VAL_L1_EN) {
+ if (lctl & PCI_EXP_LNKCTL_ASPM_L1) {
/* L1-ASPM enabled; disable(!) L0S */
iwl_set_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED);
dev_info(trans->dev, "L1 Enabled; Disabling L0S\n");
@@ -122,7 +118,7 @@ static void iwl_pcie_apm_config(struct iwl_trans *trans)
iwl_clear_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED);
dev_info(trans->dev, "L1 Disabled; Enabling L0S\n");
}
- trans->pm_support = !(lctl & PCI_CFG_LINK_CTRL_VAL_L0S_EN);
+ trans->pm_support = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S);
}
/*
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index ff306d763e3..71ab320fae8 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -1155,7 +1155,7 @@ int zd_chip_init_hw(struct zd_chip *chip)
if (r)
goto out;
/* Currently we support IEEE 802.11g for full and high speed USB.
- * It might be discussed, whether we should suppport pure b mode for
+ * It might be discussed, whether we should support pure b mode for
* full speed USB.
*/
r = set_mandatory_rates(chip, 1);
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index dfba3e64d59..d37bfcf5a3a 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -53,7 +53,7 @@ config OF_DEVICE
config OF_I2C
def_tristate I2C
- depends on I2C && !SPARC
+ depends on I2C
help
OpenFirmware I2C accessors
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 538e3cfad23..be846408dbc 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1025,7 +1025,7 @@ EXPORT_SYMBOL(of_parse_phandle);
* To get a device_node of the `node2' node you may call this:
* of_parse_phandle_with_args(node3, "list", "#list-cells", 1, &args);
*/
-int of_parse_phandle_with_args(struct device_node *np, const char *list_name,
+int of_parse_phandle_with_args(const struct device_node *np, const char *list_name,
const char *cells_name, int index,
struct of_phandle_args *out_args)
{
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index a543746fb35..ad6a8b63569 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -170,6 +170,11 @@ int pci_bus_add_device(struct pci_dev *dev)
int retval;
pci_fixup_device(pci_fixup_final, dev);
+
+ retval = pcibios_add_device(dev);
+ if (retval)
+ return retval;
+
retval = device_add(&dev->dev);
if (retval)
return retval;
diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig
index b0e46dede1a..13e9e63a726 100644
--- a/drivers/pci/hotplug/Kconfig
+++ b/drivers/pci/hotplug/Kconfig
@@ -151,4 +151,15 @@ config HOTPLUG_PCI_SGI
When in doubt, say N.
+config HOTPLUG_PCI_S390
+ tristate "System z PCI Hotplug Support"
+ depends on S390 && 64BIT
+ help
+ Say Y here if you want to use the System z PCI Hotplug
+ driver for PCI devices. Without this driver it is not
+ possible to access stand-by PCI functions nor to deconfigure
+ PCI functions.
+
+ When in doubt, say Y.
+
endif # HOTPLUG_PCI
diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
index c459cd4e39c..47ec8c80e16 100644
--- a/drivers/pci/hotplug/Makefile
+++ b/drivers/pci/hotplug/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_HOTPLUG_PCI_RPA) += rpaphp.o
obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o
obj-$(CONFIG_HOTPLUG_PCI_SGI) += sgi_hotplug.o
obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o
+obj-$(CONFIG_HOTPLUG_PCI_S390) += s390_pci_hpc.o
# acpiphp_ibm extends acpiphp, so should be linked afterwards.
diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c
new file mode 100644
index 00000000000..dee68e0698e
--- /dev/null
+++ b/drivers/pci/hotplug/s390_pci_hpc.c
@@ -0,0 +1,252 @@
+/*
+ * PCI Hot Plug Controller Driver for System z
+ *
+ * Copyright 2012 IBM Corp.
+ *
+ * Author(s):
+ * Jan Glauber <jang@linux.vnet.ibm.com>
+ */
+
+#define COMPONENT "zPCI hpc"
+#define pr_fmt(fmt) COMPONENT ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/pci_hotplug.h>
+#include <linux/init.h>
+#include <asm/sclp.h>
+
+#define SLOT_NAME_SIZE 10
+static LIST_HEAD(s390_hotplug_slot_list);
+
+MODULE_AUTHOR("Jan Glauber <jang@linux.vnet.ibm.com");
+MODULE_DESCRIPTION("Hot Plug PCI Controller for System z");
+MODULE_LICENSE("GPL");
+
+static int zpci_fn_configured(enum zpci_state state)
+{
+ return state == ZPCI_FN_STATE_CONFIGURED ||
+ state == ZPCI_FN_STATE_ONLINE;
+}
+
+/*
+ * struct slot - slot information for each *physical* slot
+ */
+struct slot {
+ struct list_head slot_list;
+ struct hotplug_slot *hotplug_slot;
+ struct zpci_dev *zdev;
+};
+
+static int enable_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = hotplug_slot->private;
+ int rc;
+
+ if (slot->zdev->state != ZPCI_FN_STATE_STANDBY)
+ return -EIO;
+
+ rc = sclp_pci_configure(slot->zdev->fid);
+ if (!rc) {
+ slot->zdev->state = ZPCI_FN_STATE_CONFIGURED;
+ /* automatically scan the device after is was configured */
+ zpci_enable_device(slot->zdev);
+ zpci_scan_device(slot->zdev);
+ }
+ return rc;
+}
+
+static int disable_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = hotplug_slot->private;
+ int rc;
+
+ if (!zpci_fn_configured(slot->zdev->state))
+ return -EIO;
+
+ /* TODO: we rely on the user to unbind/remove the device, is that plausible
+ * or do we need to trigger that here?
+ */
+ rc = sclp_pci_deconfigure(slot->zdev->fid);
+ if (!rc) {
+ /* Fixme: better call List-PCI to find the disabled FH
+ for the FID since the FH should be opaque... */
+ slot->zdev->fh &= 0x7fffffff;
+ slot->zdev->state = ZPCI_FN_STATE_STANDBY;
+ }
+ return rc;
+}
+
+static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ struct slot *slot = hotplug_slot->private;
+
+ switch (slot->zdev->state) {
+ case ZPCI_FN_STATE_STANDBY:
+ *value = 0;
+ break;
+ default:
+ *value = 1;
+ break;
+ }
+ return 0;
+}
+
+static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+ /* if the slot exits it always contains a function */
+ *value = 1;
+ return 0;
+}
+
+static void release_slot(struct hotplug_slot *hotplug_slot)
+{
+ struct slot *slot = hotplug_slot->private;
+
+ pr_debug("%s - physical_slot = %s\n", __func__, hotplug_slot_name(hotplug_slot));
+ kfree(slot->hotplug_slot->info);
+ kfree(slot->hotplug_slot);
+ kfree(slot);
+}
+
+static struct hotplug_slot_ops s390_hotplug_slot_ops = {
+ .enable_slot = enable_slot,
+ .disable_slot = disable_slot,
+ .get_power_status = get_power_status,
+ .get_adapter_status = get_adapter_status,
+};
+
+static int init_pci_slot(struct zpci_dev *zdev)
+{
+ struct hotplug_slot *hotplug_slot;
+ struct hotplug_slot_info *info;
+ char name[SLOT_NAME_SIZE];
+ struct slot *slot;
+ int rc;
+
+ if (!zdev)
+ return 0;
+
+ slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+ if (!slot)
+ goto error;
+
+ hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
+ if (!hotplug_slot)
+ goto error_hp;
+ hotplug_slot->private = slot;
+
+ slot->hotplug_slot = hotplug_slot;
+ slot->zdev = zdev;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ goto error_info;
+ hotplug_slot->info = info;
+
+ hotplug_slot->ops = &s390_hotplug_slot_ops;
+ hotplug_slot->release = &release_slot;
+
+ get_power_status(hotplug_slot, &info->power_status);
+ get_adapter_status(hotplug_slot, &info->adapter_status);
+
+ snprintf(name, SLOT_NAME_SIZE, "%08x", zdev->fid);
+ rc = pci_hp_register(slot->hotplug_slot, zdev->bus,
+ ZPCI_DEVFN, name);
+ if (rc) {
+ pr_err("pci_hp_register failed with error %d\n", rc);
+ goto error_reg;
+ }
+ list_add(&slot->slot_list, &s390_hotplug_slot_list);
+ return 0;
+
+error_reg:
+ kfree(info);
+error_info:
+ kfree(hotplug_slot);
+error_hp:
+ kfree(slot);
+error:
+ return -ENOMEM;
+}
+
+static int __init init_pci_slots(void)
+{
+ struct zpci_dev *zdev;
+ int device = 0;
+
+ /*
+ * Create a structure for each slot, and register that slot
+ * with the pci_hotplug subsystem.
+ */
+ mutex_lock(&zpci_list_lock);
+ list_for_each_entry(zdev, &zpci_list, entry) {
+ init_pci_slot(zdev);
+ device++;
+ }
+
+ mutex_unlock(&zpci_list_lock);
+ return (device) ? 0 : -ENODEV;
+}
+
+static void exit_pci_slot(struct zpci_dev *zdev)
+{
+ struct list_head *tmp, *n;
+ struct slot *slot;
+
+ list_for_each_safe(tmp, n, &s390_hotplug_slot_list) {
+ slot = list_entry(tmp, struct slot, slot_list);
+ if (slot->zdev != zdev)
+ continue;
+ list_del(&slot->slot_list);
+ pci_hp_deregister(slot->hotplug_slot);
+ }
+}
+
+static void __exit exit_pci_slots(void)
+{
+ struct list_head *tmp, *n;
+ struct slot *slot;
+
+ /*
+ * Unregister all of our slots with the pci_hotplug subsystem.
+ * Memory will be freed in release_slot() callback after slot's
+ * lifespan is finished.
+ */
+ list_for_each_safe(tmp, n, &s390_hotplug_slot_list) {
+ slot = list_entry(tmp, struct slot, slot_list);
+ list_del(&slot->slot_list);
+ pci_hp_deregister(slot->hotplug_slot);
+ }
+}
+
+static int __init pci_hotplug_s390_init(void)
+{
+ /*
+ * Do specific initialization stuff for your driver here
+ * like initializing your controller hardware (if any) and
+ * determining the number of slots you have in the system
+ * right now.
+ */
+
+ if (!pci_probe)
+ return -EOPNOTSUPP;
+
+ /* register callbacks for slot handling from arch code */
+ mutex_lock(&zpci_list_lock);
+ hotplug_ops.create_slot = init_pci_slot;
+ hotplug_ops.remove_slot = exit_pci_slot;
+ mutex_unlock(&zpci_list_lock);
+ pr_info("registered hotplug slot callbacks\n");
+ return init_pci_slots();
+}
+
+static void __exit pci_hotplug_s390_exit(void)
+{
+ exit_pci_slots();
+}
+
+module_init(pci_hotplug_s390_init);
+module_exit(pci_hotplug_s390_exit);
diff --git a/drivers/pci/ioapic.c b/drivers/pci/ioapic.c
index 2eca902a428..3c6bbdd059a 100644
--- a/drivers/pci/ioapic.c
+++ b/drivers/pci/ioapic.c
@@ -125,3 +125,5 @@ static void __exit ioapic_exit(void)
module_init(ioapic_init);
module_exit(ioapic_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index aeccc911abb..bafd2bbcaf6 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -106,7 +106,7 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset)
virtfn->resource[i].name = pci_name(virtfn);
virtfn->resource[i].flags = res->flags;
size = resource_size(res);
- do_div(size, iov->total);
+ do_div(size, iov->total_VFs);
virtfn->resource[i].start = res->start + size * id;
virtfn->resource[i].end = virtfn->resource[i].start + size - 1;
rc = request_resource(res, &virtfn->resource[i]);
@@ -194,7 +194,7 @@ static int sriov_migration(struct pci_dev *dev)
u16 status;
struct pci_sriov *iov = dev->sriov;
- if (!iov->nr_virtfn)
+ if (!iov->num_VFs)
return 0;
if (!(iov->cap & PCI_SRIOV_CAP_VFM))
@@ -216,7 +216,7 @@ static void sriov_migration_task(struct work_struct *work)
u16 status;
struct pci_sriov *iov = container_of(work, struct pci_sriov, mtask);
- for (i = iov->initial; i < iov->nr_virtfn; i++) {
+ for (i = iov->initial_VFs; i < iov->num_VFs; i++) {
state = readb(iov->mstate + i);
if (state == PCI_SRIOV_VFM_MI) {
writeb(PCI_SRIOV_VFM_AV, iov->mstate + i);
@@ -244,7 +244,7 @@ static int sriov_enable_migration(struct pci_dev *dev, int nr_virtfn)
resource_size_t pa;
struct pci_sriov *iov = dev->sriov;
- if (nr_virtfn <= iov->initial)
+ if (nr_virtfn <= iov->initial_VFs)
return 0;
pci_read_config_dword(dev, iov->pos + PCI_SRIOV_VFM, &table);
@@ -294,15 +294,15 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
if (!nr_virtfn)
return 0;
- if (iov->nr_virtfn)
+ if (iov->num_VFs)
return -EINVAL;
pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial);
- if (initial > iov->total ||
- (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total)))
+ if (initial > iov->total_VFs ||
+ (!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total_VFs)))
return -EIO;
- if (nr_virtfn < 0 || nr_virtfn > iov->total ||
+ if (nr_virtfn < 0 || nr_virtfn > iov->total_VFs ||
(!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial)))
return -EINVAL;
@@ -359,7 +359,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
msleep(100);
pci_cfg_access_unlock(dev);
- iov->initial = initial;
+ iov->initial_VFs = initial;
if (nr_virtfn < initial)
initial = nr_virtfn;
@@ -376,7 +376,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
}
kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE);
- iov->nr_virtfn = nr_virtfn;
+ iov->num_VFs = nr_virtfn;
return 0;
@@ -401,13 +401,13 @@ static void sriov_disable(struct pci_dev *dev)
int i;
struct pci_sriov *iov = dev->sriov;
- if (!iov->nr_virtfn)
+ if (!iov->num_VFs)
return;
if (iov->cap & PCI_SRIOV_CAP_VFM)
sriov_disable_migration(dev);
- for (i = 0; i < iov->nr_virtfn; i++)
+ for (i = 0; i < iov->num_VFs; i++)
virtfn_remove(dev, i, 0);
iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
@@ -419,7 +419,7 @@ static void sriov_disable(struct pci_dev *dev)
if (iov->link != dev->devfn)
sysfs_remove_link(&dev->dev.kobj, "dep_link");
- iov->nr_virtfn = 0;
+ iov->num_VFs = 0;
}
static int sriov_init(struct pci_dev *dev, int pos)
@@ -496,7 +496,7 @@ found:
iov->pos = pos;
iov->nres = nres;
iov->ctrl = ctrl;
- iov->total = total;
+ iov->total_VFs = total;
iov->offset = offset;
iov->stride = stride;
iov->pgsz = pgsz;
@@ -529,7 +529,7 @@ failed:
static void sriov_release(struct pci_dev *dev)
{
- BUG_ON(dev->sriov->nr_virtfn);
+ BUG_ON(dev->sriov->num_VFs);
if (dev != dev->sriov->dev)
pci_dev_put(dev->sriov->dev);
@@ -554,7 +554,7 @@ static void sriov_restore_state(struct pci_dev *dev)
pci_update_resource(dev, i);
pci_write_config_dword(dev, iov->pos + PCI_SRIOV_SYS_PGSIZE, iov->pgsz);
- pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->nr_virtfn);
+ pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, iov->num_VFs);
pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
if (iov->ctrl & PCI_SRIOV_CTRL_VFE)
msleep(100);
@@ -661,7 +661,7 @@ int pci_iov_bus_range(struct pci_bus *bus)
list_for_each_entry(dev, &bus->devices, bus_list) {
if (!dev->is_physfn)
continue;
- busnr = virtfn_bus(dev, dev->sriov->total - 1);
+ busnr = virtfn_bus(dev, dev->sriov->total_VFs - 1);
if (busnr > max)
max = busnr;
}
@@ -729,9 +729,56 @@ EXPORT_SYMBOL_GPL(pci_sriov_migration);
*/
int pci_num_vf(struct pci_dev *dev)
{
- if (!dev || !dev->is_physfn)
+ if (!dev->is_physfn)
return 0;
- else
- return dev->sriov->nr_virtfn;
+
+ return dev->sriov->num_VFs;
}
EXPORT_SYMBOL_GPL(pci_num_vf);
+
+/**
+ * pci_sriov_set_totalvfs -- reduce the TotalVFs available
+ * @dev: the PCI PF device
+ * numvfs: number that should be used for TotalVFs supported
+ *
+ * Should be called from PF driver's probe routine with
+ * device's mutex held.
+ *
+ * Returns 0 if PF is an SRIOV-capable device and
+ * value of numvfs valid. If not a PF with VFS, return -EINVAL;
+ * if VFs already enabled, return -EBUSY.
+ */
+int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs)
+{
+ if (!dev->is_physfn || (numvfs > dev->sriov->total_VFs))
+ return -EINVAL;
+
+ /* Shouldn't change if VFs already enabled */
+ if (dev->sriov->ctrl & PCI_SRIOV_CTRL_VFE)
+ return -EBUSY;
+ else
+ dev->sriov->driver_max_VFs = numvfs;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs);
+
+/**
+ * pci_sriov_get_totalvfs -- get total VFs supported on this devic3
+ * @dev: the PCI PF device
+ *
+ * For a PCIe device with SRIOV support, return the PCIe
+ * SRIOV capability value of TotalVFs or the value of driver_max_VFs
+ * if the driver reduced it. Otherwise, -EINVAL.
+ */
+int pci_sriov_get_totalvfs(struct pci_dev *dev)
+{
+ if (!dev->is_physfn)
+ return -EINVAL;
+
+ if (dev->sriov->driver_max_VFs)
+ return dev->sriov->driver_max_VFs;
+
+ return dev->sriov->total_VFs;
+}
+EXPORT_SYMBOL_GPL(pci_sriov_get_totalvfs);
diff --git a/drivers/pci/irq.c b/drivers/pci/irq.c
index e5f69a43b1b..b008cf86b9c 100644
--- a/drivers/pci/irq.c
+++ b/drivers/pci/irq.c
@@ -14,11 +14,11 @@ static void pci_note_irq_problem(struct pci_dev *pdev, const char *reason)
{
struct pci_dev *parent = to_pci_dev(pdev->dev.parent);
- dev_printk(KERN_ERR, &pdev->dev,
- "Potentially misrouted IRQ (Bridge %s %04x:%04x)\n",
- dev_name(&parent->dev), parent->vendor, parent->device);
- dev_printk(KERN_ERR, &pdev->dev, "%s\n", reason);
- dev_printk(KERN_ERR, &pdev->dev, "Please report to linux-kernel@vger.kernel.org\n");
+ dev_err(&pdev->dev,
+ "Potentially misrouted IRQ (Bridge %s %04x:%04x)\n",
+ dev_name(&parent->dev), parent->vendor, parent->device);
+ dev_err(&pdev->dev, "%s\n", reason);
+ dev_err(&pdev->dev, "Please report to linux-kernel@vger.kernel.org\n");
WARN_ON(1);
}
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index a825d78fd0a..5099636a6e5 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -207,6 +207,8 @@ static void msix_mask_irq(struct msi_desc *desc, u32 flag)
desc->masked = __msix_mask_irq(desc, flag);
}
+#ifdef CONFIG_GENERIC_HARDIRQS
+
static void msi_set_mask_bit(struct irq_data *data, u32 flag)
{
struct msi_desc *desc = irq_data_get_msi(data);
@@ -230,6 +232,8 @@ void unmask_msi_irq(struct irq_data *data)
msi_set_mask_bit(data, 0);
}
+#endif /* CONFIG_GENERIC_HARDIRQS */
+
void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg)
{
BUG_ON(entry->dev->current_state != PCI_D0);
@@ -337,8 +341,10 @@ static void free_msi_irqs(struct pci_dev *dev)
if (!entry->irq)
continue;
nvec = 1 << entry->msi_attrib.multiple;
+#ifdef CONFIG_GENERIC_HARDIRQS
for (i = 0; i < nvec; i++)
BUG_ON(irq_has_action(entry->irq + i));
+#endif
}
arch_teardown_msi_irqs(dev);
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 1dc78c5cabf..f79cbcd3944 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -248,31 +248,26 @@ struct drv_dev_and_id {
static long local_pci_probe(void *_ddi)
{
struct drv_dev_and_id *ddi = _ddi;
- struct device *dev = &ddi->dev->dev;
- struct device *parent = dev->parent;
+ struct pci_dev *pci_dev = ddi->dev;
+ struct pci_driver *pci_drv = ddi->drv;
+ struct device *dev = &pci_dev->dev;
int rc;
- /* The parent bridge must be in active state when probing */
- if (parent)
- pm_runtime_get_sync(parent);
- /* Unbound PCI devices are always set to disabled and suspended.
- * During probe, the device is set to enabled and active and the
- * usage count is incremented. If the driver supports runtime PM,
- * it should call pm_runtime_put_noidle() in its probe routine and
- * pm_runtime_get_noresume() in its remove routine.
+ /*
+ * Unbound PCI devices are always put in D0, regardless of
+ * runtime PM status. During probe, the device is set to
+ * active and the usage count is incremented. If the driver
+ * supports runtime PM, it should call pm_runtime_put_noidle()
+ * in its probe routine and pm_runtime_get_noresume() in its
+ * remove routine.
*/
- pm_runtime_get_noresume(dev);
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
-
- rc = ddi->drv->probe(ddi->dev, ddi->id);
+ pm_runtime_get_sync(dev);
+ pci_dev->driver = pci_drv;
+ rc = pci_drv->probe(pci_dev, ddi->id);
if (rc) {
- pm_runtime_disable(dev);
- pm_runtime_set_suspended(dev);
- pm_runtime_put_noidle(dev);
+ pci_dev->driver = NULL;
+ pm_runtime_put_sync(dev);
}
- if (parent)
- pm_runtime_put(parent);
return rc;
}
@@ -322,10 +317,8 @@ __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev)
id = pci_match_device(drv, pci_dev);
if (id)
error = pci_call_probe(drv, pci_dev, id);
- if (error >= 0) {
- pci_dev->driver = drv;
+ if (error >= 0)
error = 0;
- }
}
return error;
}
@@ -361,9 +354,7 @@ static int pci_device_remove(struct device * dev)
}
/* Undo the runtime PM settings in local_pci_probe() */
- pm_runtime_disable(dev);
- pm_runtime_set_suspended(dev);
- pm_runtime_put_noidle(dev);
+ pm_runtime_put_sync(dev);
/*
* If the device is still on, set the power state as "unknown",
@@ -986,6 +977,13 @@ static int pci_pm_runtime_suspend(struct device *dev)
pci_power_t prev = pci_dev->current_state;
int error;
+ /*
+ * If pci_dev->driver is not set (unbound), the device should
+ * always remain in D0 regardless of the runtime PM status
+ */
+ if (!pci_dev->driver)
+ return 0;
+
if (!pm || !pm->runtime_suspend)
return -ENOSYS;
@@ -1007,10 +1005,10 @@ static int pci_pm_runtime_suspend(struct device *dev)
return 0;
}
- if (!pci_dev->state_saved)
+ if (!pci_dev->state_saved) {
pci_save_state(pci_dev);
-
- pci_finish_runtime_suspend(pci_dev);
+ pci_finish_runtime_suspend(pci_dev);
+ }
return 0;
}
@@ -1021,6 +1019,13 @@ static int pci_pm_runtime_resume(struct device *dev)
struct pci_dev *pci_dev = to_pci_dev(dev);
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ /*
+ * If pci_dev->driver is not set (unbound), the device should
+ * always remain in D0 regardless of the runtime PM status
+ */
+ if (!pci_dev->driver)
+ return 0;
+
if (!pm || !pm->runtime_resume)
return -ENOSYS;
@@ -1038,8 +1043,16 @@ static int pci_pm_runtime_resume(struct device *dev)
static int pci_pm_runtime_idle(struct device *dev)
{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ /*
+ * If pci_dev->driver is not set (unbound), the device should
+ * always remain in D0 regardless of the runtime PM status
+ */
+ if (!pci_dev->driver)
+ goto out;
+
if (!pm)
return -ENOSYS;
@@ -1049,8 +1062,8 @@ static int pci_pm_runtime_idle(struct device *dev)
return ret;
}
+out:
pm_runtime_suspend(dev);
-
return 0;
}
diff --git a/drivers/pci/pci-stub.c b/drivers/pci/pci-stub.c
index 775e933c222..6e47c519c51 100644
--- a/drivers/pci/pci-stub.c
+++ b/drivers/pci/pci-stub.c
@@ -28,7 +28,7 @@ MODULE_PARM_DESC(ids, "Initial PCI IDs to add to the stub driver, format is "
static int pci_stub_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
- dev_printk(KERN_INFO, &dev->dev, "claimed by stub\n");
+ dev_info(&dev->dev, "claimed by stub\n");
return 0;
}
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 68d56f02e72..05b78b16d20 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -401,6 +401,106 @@ static ssize_t d3cold_allowed_show(struct device *dev,
}
#endif
+#ifdef CONFIG_PCI_IOV
+static ssize_t sriov_totalvfs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ return sprintf(buf, "%u\n", pci_sriov_get_totalvfs(pdev));
+}
+
+
+static ssize_t sriov_numvfs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ return sprintf(buf, "%u\n", pdev->sriov->num_VFs);
+}
+
+/*
+ * num_vfs > 0; number of vfs to enable
+ * num_vfs = 0; disable all vfs
+ *
+ * Note: SRIOV spec doesn't allow partial VF
+ * disable, so its all or none.
+ */
+static ssize_t sriov_numvfs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ int num_vfs_enabled = 0;
+ int num_vfs;
+ int ret = 0;
+ u16 total;
+
+ if (kstrtoint(buf, 0, &num_vfs) < 0)
+ return -EINVAL;
+
+ /* is PF driver loaded w/callback */
+ if (!pdev->driver || !pdev->driver->sriov_configure) {
+ dev_info(&pdev->dev,
+ "Driver doesn't support SRIOV configuration via sysfs\n");
+ return -ENOSYS;
+ }
+
+ /* if enabling vf's ... */
+ total = pci_sriov_get_totalvfs(pdev);
+ /* Requested VFs to enable < totalvfs and none enabled already */
+ if ((num_vfs > 0) && (num_vfs <= total)) {
+ if (pdev->sriov->num_VFs == 0) {
+ num_vfs_enabled =
+ pdev->driver->sriov_configure(pdev, num_vfs);
+ if ((num_vfs_enabled >= 0) &&
+ (num_vfs_enabled != num_vfs)) {
+ dev_warn(&pdev->dev,
+ "Only %d VFs enabled\n",
+ num_vfs_enabled);
+ return count;
+ } else if (num_vfs_enabled < 0)
+ /* error code from driver callback */
+ return num_vfs_enabled;
+ } else if (num_vfs == pdev->sriov->num_VFs) {
+ dev_warn(&pdev->dev,
+ "%d VFs already enabled; no enable action taken\n",
+ num_vfs);
+ return count;
+ } else {
+ dev_warn(&pdev->dev,
+ "%d VFs already enabled. Disable before enabling %d VFs\n",
+ pdev->sriov->num_VFs, num_vfs);
+ return -EINVAL;
+ }
+ }
+
+ /* disable vfs */
+ if (num_vfs == 0) {
+ if (pdev->sriov->num_VFs != 0) {
+ ret = pdev->driver->sriov_configure(pdev, 0);
+ return ret ? ret : count;
+ } else {
+ dev_warn(&pdev->dev,
+ "All VFs disabled; no disable action taken\n");
+ return count;
+ }
+ }
+
+ dev_err(&pdev->dev,
+ "Invalid value for number of VFs to enable: %d\n", num_vfs);
+
+ return -EINVAL;
+}
+
+static struct device_attribute sriov_totalvfs_attr = __ATTR_RO(sriov_totalvfs);
+static struct device_attribute sriov_numvfs_attr =
+ __ATTR(sriov_numvfs, (S_IRUGO|S_IWUSR|S_IWGRP),
+ sriov_numvfs_show, sriov_numvfs_store);
+#endif /* CONFIG_PCI_IOV */
+
struct device_attribute pci_dev_attrs[] = {
__ATTR_RO(resource),
__ATTR_RO(vendor),
@@ -1262,29 +1362,20 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
pdev->rom_attr = attr;
}
- if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
- retval = device_create_file(&pdev->dev, &vga_attr);
- if (retval)
- goto err_rom_file;
- }
-
/* add platform-specific attributes */
retval = pcibios_add_platform_entries(pdev);
if (retval)
- goto err_vga_file;
+ goto err_rom_file;
/* add sysfs entries for various capabilities */
retval = pci_create_capabilities_sysfs(pdev);
if (retval)
- goto err_vga_file;
+ goto err_rom_file;
pci_create_firmware_label_files(pdev);
return 0;
-err_vga_file:
- if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
- device_remove_file(&pdev->dev, &vga_attr);
err_rom_file:
if (rom_size) {
sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
@@ -1370,3 +1461,62 @@ static int __init pci_sysfs_init(void)
}
late_initcall(pci_sysfs_init);
+
+static struct attribute *pci_dev_dev_attrs[] = {
+ &vga_attr.attr,
+ NULL,
+};
+
+static umode_t pci_dev_attrs_are_visible(struct kobject *kobj,
+ struct attribute *a, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ if (a == &vga_attr.attr)
+ if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+ return 0;
+
+ return a->mode;
+}
+
+#ifdef CONFIG_PCI_IOV
+static struct attribute *sriov_dev_attrs[] = {
+ &sriov_totalvfs_attr.attr,
+ &sriov_numvfs_attr.attr,
+ NULL,
+};
+
+static umode_t sriov_attrs_are_visible(struct kobject *kobj,
+ struct attribute *a, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+
+ if (!dev_is_pf(dev))
+ return 0;
+
+ return a->mode;
+}
+
+static struct attribute_group sriov_dev_attr_group = {
+ .attrs = sriov_dev_attrs,
+ .is_visible = sriov_attrs_are_visible,
+};
+#endif /* CONFIG_PCI_IOV */
+
+static struct attribute_group pci_dev_attr_group = {
+ .attrs = pci_dev_dev_attrs,
+ .is_visible = pci_dev_attrs_are_visible,
+};
+
+static const struct attribute_group *pci_dev_attr_groups[] = {
+ &pci_dev_attr_group,
+#ifdef CONFIG_PCI_IOV
+ &sriov_dev_attr_group,
+#endif
+ NULL,
+};
+
+struct device_type pci_dev_type = {
+ .groups = pci_dev_attr_groups,
+};
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index bdf66b500f2..5cb5820fae4 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1333,6 +1333,19 @@ void pcim_pin_device(struct pci_dev *pdev)
dr->pinned = 1;
}
+/*
+ * pcibios_add_device - provide arch specific hooks when adding device dev
+ * @dev: the PCI device being added
+ *
+ * Permits the platform to provide architecture specific functionality when
+ * devices are added. This is the default implementation. Architecture
+ * implementations can override this.
+ */
+int __weak pcibios_add_device (struct pci_dev *dev)
+{
+ return 0;
+}
+
/**
* pcibios_disable_device - disable arch specific PCI resources for device dev
* @dev: the PCI device to disable
@@ -1578,15 +1591,25 @@ void pci_pme_active(struct pci_dev *dev, bool enable)
pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);
- /* PCI (as opposed to PCIe) PME requires that the device have
- its PME# line hooked up correctly. Not all hardware vendors
- do this, so the PME never gets delivered and the device
- remains asleep. The easiest way around this is to
- periodically walk the list of suspended devices and check
- whether any have their PME flag set. The assumption is that
- we'll wake up often enough anyway that this won't be a huge
- hit, and the power savings from the devices will still be a
- win. */
+ /*
+ * PCI (as opposed to PCIe) PME requires that the device have
+ * its PME# line hooked up correctly. Not all hardware vendors
+ * do this, so the PME never gets delivered and the device
+ * remains asleep. The easiest way around this is to
+ * periodically walk the list of suspended devices and check
+ * whether any have their PME flag set. The assumption is that
+ * we'll wake up often enough anyway that this won't be a huge
+ * hit, and the power savings from the devices will still be a
+ * win.
+ *
+ * Although PCIe uses in-band PME message instead of PME# line
+ * to report PME, PME does not work for some PCIe devices in
+ * reality. For example, there are devices that set their PME
+ * status bits, but don't really bother to send a PME message;
+ * there are PCI Express Root Ports that don't bother to
+ * trigger interrupts when they receive PME messages from the
+ * devices below. So PME poll is used for PCIe devices too.
+ */
if (dev->pme_poll) {
struct pci_pme_device *pme_dev;
@@ -1900,6 +1923,8 @@ void pci_pm_init(struct pci_dev *dev)
u16 pmc;
pm_runtime_forbid(&dev->dev);
+ pm_runtime_set_active(&dev->dev);
+ pm_runtime_enable(&dev->dev);
device_enable_async_suspend(&dev->dev);
dev->wakeup_prepared = false;
@@ -3865,14 +3890,13 @@ static void pci_no_domains(void)
}
/**
- * pci_ext_cfg_enabled - can we access extended PCI config space?
- * @dev: The PCI device of the root bridge.
+ * pci_ext_cfg_avail - can we access extended PCI config space?
*
* Returns 1 if we can access PCI extended config space (offsets
* greater than 0xff). This is the default implementation. Architecture
* implementations can override this.
*/
-int __weak pci_ext_cfg_avail(struct pci_dev *dev)
+int __weak pci_ext_cfg_avail(void)
{
return 1;
}
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index e253881c427..e8518292826 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -158,6 +158,7 @@ static inline int pci_no_d1d2(struct pci_dev *dev)
}
extern struct device_attribute pci_dev_attrs[];
extern struct device_attribute pcibus_dev_attrs[];
+extern struct device_type pci_dev_type;
extern struct bus_attribute pci_bus_attrs[];
@@ -229,13 +230,14 @@ struct pci_sriov {
int nres; /* number of resources */
u32 cap; /* SR-IOV Capabilities */
u16 ctrl; /* SR-IOV Control */
- u16 total; /* total VFs associated with the PF */
- u16 initial; /* initial VFs associated with the PF */
- u16 nr_virtfn; /* number of VFs available */
+ u16 total_VFs; /* total VFs associated with the PF */
+ u16 initial_VFs; /* initial VFs associated with the PF */
+ u16 num_VFs; /* number of VFs available */
u16 offset; /* first VF Routing ID offset */
u16 stride; /* following VF stride */
u32 pgsz; /* page size for BAR alignment */
u8 link; /* Function Dependency Link */
+ u16 driver_max_VFs; /* max num VFs driver supports */
struct pci_dev *dev; /* lowest numbered PF */
struct pci_dev *self; /* this PF */
struct mutex lock; /* lock for VF bus */
diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h
index 94a7598eb26..22f840f4dda 100644
--- a/drivers/pci/pcie/aer/aerdrv.h
+++ b/drivers/pci/pcie/aer/aerdrv.h
@@ -87,6 +87,9 @@ struct aer_broadcast_data {
static inline pci_ers_result_t merge_result(enum pci_ers_result orig,
enum pci_ers_result new)
{
+ if (new == PCI_ERS_RESULT_NO_AER_DRIVER)
+ return PCI_ERS_RESULT_NO_AER_DRIVER;
+
if (new == PCI_ERS_RESULT_NONE)
return orig;
@@ -97,7 +100,7 @@ static inline pci_ers_result_t merge_result(enum pci_ers_result orig,
break;
case PCI_ERS_RESULT_DISCONNECT:
if (new == PCI_ERS_RESULT_NEED_RESET)
- orig = new;
+ orig = PCI_ERS_RESULT_NEED_RESET;
break;
default:
break;
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c
index af4e31cd3a3..421bbc5fee3 100644
--- a/drivers/pci/pcie/aer/aerdrv_core.c
+++ b/drivers/pci/pcie/aer/aerdrv_core.c
@@ -232,13 +232,27 @@ static int report_error_detected(struct pci_dev *dev, void *data)
dev->driver ?
"no AER-aware driver" : "no driver");
}
- goto out;
+
+ /*
+ * If there's any device in the subtree that does not
+ * have an error_detected callback, returning
+ * PCI_ERS_RESULT_NO_AER_DRIVER prevents calling of
+ * the subsequent mmio_enabled/slot_reset/resume
+ * callbacks of "any" device in the subtree. All the
+ * devices in the subtree are left in the error state
+ * without recovery.
+ */
+
+ if (!(dev->hdr_type & PCI_HEADER_TYPE_BRIDGE))
+ vote = PCI_ERS_RESULT_NO_AER_DRIVER;
+ else
+ vote = PCI_ERS_RESULT_NONE;
+ } else {
+ err_handler = dev->driver->err_handler;
+ vote = err_handler->error_detected(dev, result_data->state);
}
- err_handler = dev->driver->err_handler;
- vote = err_handler->error_detected(dev, result_data->state);
result_data->result = merge_result(result_data->result, vote);
-out:
device_unlock(&dev->dev);
return 0;
}
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 213753b283a..b52630b8ead 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -242,8 +242,7 @@ static void pcie_aspm_configure_common_clock(struct pcie_link_state *link)
return;
/* Training failed. Restore common clock configurations */
- dev_printk(KERN_ERR, &parent->dev,
- "ASPM: Could not configure common clock\n");
+ dev_err(&parent->dev, "ASPM: Could not configure common clock\n");
list_for_each_entry(child, &linkbus->devices, bus_list)
pcie_capability_write_word(child, PCI_EXP_LNKCTL,
child_reg[PCI_FUNC(child->devfn)]);
@@ -427,7 +426,8 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
{
- pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL, 0x3, val);
+ pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_ASPMC, val);
}
static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
@@ -442,12 +442,12 @@ static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
return;
/* Convert ASPM state to upstream/downstream ASPM register state */
if (state & ASPM_STATE_L0S_UP)
- dwstream |= PCIE_LINK_STATE_L0S;
+ dwstream |= PCI_EXP_LNKCTL_ASPM_L0S;
if (state & ASPM_STATE_L0S_DW)
- upstream |= PCIE_LINK_STATE_L0S;
+ upstream |= PCI_EXP_LNKCTL_ASPM_L0S;
if (state & ASPM_STATE_L1) {
- upstream |= PCIE_LINK_STATE_L1;
- dwstream |= PCIE_LINK_STATE_L1;
+ upstream |= PCI_EXP_LNKCTL_ASPM_L1;
+ dwstream |= PCI_EXP_LNKCTL_ASPM_L1;
}
/*
* Spec 2.0 suggests all functions should be configured the
@@ -507,9 +507,7 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev)
*/
pcie_capability_read_dword(child, PCI_EXP_DEVCAP, &reg32);
if (!(reg32 & PCI_EXP_DEVCAP_RBER) && !aspm_force) {
- dev_printk(KERN_INFO, &child->dev, "disabling ASPM"
- " on pre-1.1 PCIe device. You can enable it"
- " with 'pcie_aspm=force'\n");
+ dev_info(&child->dev, "disabling ASPM on pre-1.1 PCIe device. You can enable it with 'pcie_aspm=force'\n");
return -EINVAL;
}
}
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index ed129b41462..b42133afca9 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -120,8 +120,7 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask)
* the value in this field indicates which MSI-X Table entry is
* used to generate the interrupt message."
*/
- pos = pci_pcie_cap(dev);
- pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &reg16);
+ pcie_capability_read_word(dev, PCI_EXP_FLAGS, &reg16);
entry = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9;
if (entry >= nr_entries)
goto Error;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 3683f6094e3..6186f03d84f 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -521,7 +521,7 @@ static unsigned char pcie_link_speed[] = {
void pcie_update_link_speed(struct pci_bus *bus, u16 linksta)
{
- bus->cur_bus_speed = pcie_link_speed[linksta & 0xf];
+ bus->cur_bus_speed = pcie_link_speed[linksta & PCI_EXP_LNKSTA_CLS];
}
EXPORT_SYMBOL_GPL(pcie_update_link_speed);
@@ -579,14 +579,16 @@ static void pci_set_bus_speed(struct pci_bus *bus)
if (pos) {
u16 status;
enum pci_bus_speed max;
- pci_read_config_word(bridge, pos + 2, &status);
- if (status & 0x8000) {
+ pci_read_config_word(bridge, pos + PCI_X_BRIDGE_SSTATUS,
+ &status);
+
+ if (status & PCI_X_SSTATUS_533MHZ) {
max = PCI_SPEED_133MHz_PCIX_533;
- } else if (status & 0x4000) {
+ } else if (status & PCI_X_SSTATUS_266MHZ) {
max = PCI_SPEED_133MHz_PCIX_266;
- } else if (status & 0x0002) {
- if (((status >> 12) & 0x3) == 2) {
+ } else if (status & PCI_X_SSTATUS_133MHZ) {
+ if ((status & PCI_X_SSTATUS_VERS) == PCI_X_SSTATUS_V2) {
max = PCI_SPEED_133MHz_PCIX_ECC;
} else {
max = PCI_SPEED_133MHz_PCIX;
@@ -596,7 +598,8 @@ static void pci_set_bus_speed(struct pci_bus *bus)
}
bus->max_bus_speed = max;
- bus->cur_bus_speed = pcix_bus_speed[(status >> 6) & 0xf];
+ bus->cur_bus_speed = pcix_bus_speed[
+ (status & PCI_X_SSTATUS_FREQ) >> 6];
return;
}
@@ -607,7 +610,7 @@ static void pci_set_bus_speed(struct pci_bus *bus)
u16 linksta;
pcie_capability_read_dword(bridge, PCI_EXP_LNKCAP, &linkcap);
- bus->max_bus_speed = pcie_link_speed[linkcap & 0xf];
+ bus->max_bus_speed = pcie_link_speed[linkcap & PCI_EXP_LNKCAP_SLS];
pcie_capability_read_word(bridge, PCI_EXP_LNKSTA, &linksta);
pcie_update_link_speed(bus, linksta);
@@ -975,6 +978,7 @@ int pci_setup_device(struct pci_dev *dev)
dev->sysdata = dev->bus->sysdata;
dev->dev.parent = dev->bus->bridge;
dev->dev.bus = &pci_bus_type;
+ dev->dev.type = &pci_dev_type;
dev->hdr_type = hdr_type & 0x7f;
dev->multifunction = !!(hdr_type & 0x80);
dev->error_state = pci_channel_io_normal;
@@ -1889,6 +1893,28 @@ unsigned int __ref pci_rescan_bus_bridge_resize(struct pci_dev *bridge)
return max;
}
+/**
+ * pci_rescan_bus - scan a PCI bus for devices.
+ * @bus: PCI bus to scan
+ *
+ * Scan a PCI bus and child buses for new devices, adds them,
+ * and enables them.
+ *
+ * Returns the max number of subordinate bus discovered.
+ */
+unsigned int __ref pci_rescan_bus(struct pci_bus *bus)
+{
+ unsigned int max;
+
+ max = pci_scan_child_bus(bus);
+ pci_assign_unassigned_bus_resources(bus);
+ pci_enable_bridges(bus);
+ pci_bus_add_devices(bus);
+
+ return max;
+}
+EXPORT_SYMBOL_GPL(pci_rescan_bus);
+
EXPORT_SYMBOL(pci_add_new_bus);
EXPORT_SYMBOL(pci_scan_slot);
EXPORT_SYMBOL(pci_scan_bridge);
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 22ad3ee0cf0..8f7a6344e79 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -1790,6 +1790,45 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TOSHIBA_2,
PCI_DEVICE_ID_TOSHIBA_TC86C001_IDE,
quirk_tc86c001_ide);
+/*
+ * PLX PCI 9050 PCI Target bridge controller has an errata that prevents the
+ * local configuration registers accessible via BAR0 (memory) or BAR1 (i/o)
+ * being read correctly if bit 7 of the base address is set.
+ * The BAR0 or BAR1 region may be disabled (size 0) or enabled (size 128).
+ * Re-allocate the regions to a 256-byte boundary if necessary.
+ */
+static void quirk_plx_pci9050(struct pci_dev *dev)
+{
+ unsigned int bar;
+
+ /* Fixed in revision 2 (PCI 9052). */
+ if (dev->revision >= 2)
+ return;
+ for (bar = 0; bar <= 1; bar++)
+ if (pci_resource_len(dev, bar) == 0x80 &&
+ (pci_resource_start(dev, bar) & 0x80)) {
+ struct resource *r = &dev->resource[bar];
+ dev_info(&dev->dev,
+ "Re-allocating PLX PCI 9050 BAR %u to length 256 to avoid bit 7 bug\n",
+ bar);
+ r->start = 0;
+ r->end = 0xff;
+ }
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+ quirk_plx_pci9050);
+/*
+ * The following Meilhaus (vendor ID 0x1402) device IDs (amongst others)
+ * may be using the PLX PCI 9050: 0x0630, 0x0940, 0x0950, 0x0960, 0x100b,
+ * 0x1400, 0x140a, 0x140b, 0x14e0, 0x14ea, 0x14eb, 0x1604, 0x1608, 0x160c,
+ * 0x168f, 0x2000, 0x2600, 0x3000, 0x810a, 0x810b.
+ *
+ * Currently, device IDs 0x2000 and 0x2600 are used by the Comedi "me_daq"
+ * driver.
+ */
+DECLARE_PCI_FIXUP_HEADER(0x1402, 0x2000, quirk_plx_pci9050);
+DECLARE_PCI_FIXUP_HEADER(0x1402, 0x2600, quirk_plx_pci9050);
+
static void quirk_netmos(struct pci_dev *dev)
{
unsigned int num_parallel = (dev->subsystem_device & 0xf0) >> 4;
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 513972f3ed1..7c0fd9252e6 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -111,3 +111,39 @@ void pci_stop_and_remove_bus_device(struct pci_dev *dev)
pci_remove_bus_device(dev);
}
EXPORT_SYMBOL(pci_stop_and_remove_bus_device);
+
+void pci_stop_root_bus(struct pci_bus *bus)
+{
+ struct pci_dev *child, *tmp;
+ struct pci_host_bridge *host_bridge;
+
+ if (!pci_is_root_bus(bus))
+ return;
+
+ host_bridge = to_pci_host_bridge(bus->bridge);
+ list_for_each_entry_safe_reverse(child, tmp,
+ &bus->devices, bus_list)
+ pci_stop_bus_device(child);
+
+ /* stop the host bridge */
+ device_del(&host_bridge->dev);
+}
+
+void pci_remove_root_bus(struct pci_bus *bus)
+{
+ struct pci_dev *child, *tmp;
+ struct pci_host_bridge *host_bridge;
+
+ if (!pci_is_root_bus(bus))
+ return;
+
+ host_bridge = to_pci_host_bridge(bus->bridge);
+ list_for_each_entry_safe(child, tmp,
+ &bus->devices, bus_list)
+ pci_remove_bus_device(child);
+ pci_remove_bus(bus);
+ host_bridge->bus = NULL;
+
+ /* remove the host bridge */
+ put_device(&host_bridge->dev);
+}
diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c
index 0b3037ab8b9..ab886b7ee32 100644
--- a/drivers/pci/rom.c
+++ b/drivers/pci/rom.c
@@ -118,11 +118,17 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
void __iomem *rom;
/*
+ * Some devices may provide ROMs via a source other than the BAR
+ */
+ if (pdev->rom && pdev->romlen) {
+ *size = pdev->romlen;
+ return phys_to_virt(pdev->rom);
+ /*
* IORESOURCE_ROM_SHADOW set on x86, x86_64 and IA64 supports legacy
* memory map if the VGA enable bit of the Bridge Control register is
* set for embedded VGA.
*/
- if (res->flags & IORESOURCE_ROM_SHADOW) {
+ } else if (res->flags & IORESOURCE_ROM_SHADOW) {
/* primary video rom always starts here */
start = (loff_t)0xC0000;
*size = 0x20000; /* cover C000:0 through E000:0 */
@@ -181,7 +187,8 @@ void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom)
if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_BIOS_COPY))
return;
- iounmap(rom);
+ if (!pdev->rom || !pdev->romlen)
+ iounmap(rom);
/* Disable again before continuing, leave enabled if pci=rom */
if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 1e808ca338f..6d3591d57ea 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -1550,25 +1550,12 @@ enable_all:
}
EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources);
-#ifdef CONFIG_HOTPLUG
-/**
- * pci_rescan_bus - scan a PCI bus for devices.
- * @bus: PCI bus to scan
- *
- * Scan a PCI bus and child buses for new devices, adds them,
- * and enables them.
- *
- * Returns the max number of subordinate bus discovered.
- */
-unsigned int __ref pci_rescan_bus(struct pci_bus *bus)
+void pci_assign_unassigned_bus_resources(struct pci_bus *bus)
{
- unsigned int max;
struct pci_dev *dev;
LIST_HEAD(add_list); /* list of resources that
want additional resources */
- max = pci_scan_child_bus(bus);
-
down_read(&pci_bus_sem);
list_for_each_entry(dev, &bus->devices, bus_list)
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
@@ -1579,11 +1566,4 @@ unsigned int __ref pci_rescan_bus(struct pci_bus *bus)
up_read(&pci_bus_sem);
__pci_bus_assign_resources(bus, &add_list, NULL);
BUG_ON(!list_empty(&add_list));
-
- pci_enable_bridges(bus);
- pci_bus_add_devices(bus);
-
- return max;
}
-EXPORT_SYMBOL_GPL(pci_rescan_bus);
-#endif
diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c
index db542f4196a..966abc6054d 100644
--- a/drivers/pci/xen-pcifront.c
+++ b/drivers/pci/xen-pcifront.c
@@ -1068,13 +1068,16 @@ static void __init_refok pcifront_backend_changed(struct xenbus_device *xdev,
case XenbusStateInitialising:
case XenbusStateInitWait:
case XenbusStateInitialised:
- case XenbusStateClosed:
break;
case XenbusStateConnected:
pcifront_try_connect(pdev);
break;
+ case XenbusStateClosed:
+ if (xdev->state == XenbusStateClosed)
+ break;
+ /* Missed the backend's CLOSING state -- fallthrough */
case XenbusStateClosing:
dev_warn(&xdev->dev, "backend going away!\n");
pcifront_try_disconnect(pdev);
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index b1d956d81f0..9f45e2f77d5 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -245,6 +245,13 @@ config BATTERY_INTEL_MID
Say Y here to enable the battery driver on Intel MID
platforms.
+config BATTERY_RX51
+ tristate "Nokia RX-51 (N900) battery driver"
+ depends on TWL4030_MADC
+ help
+ Say Y here to enable support for battery information on Nokia
+ RX-51, also known as N900 tablet.
+
config CHARGER_ISP1704
tristate "ISP1704 USB Charger Detection"
depends on USB_OTG_UTILS
@@ -315,6 +322,16 @@ config CHARGER_MAX8998
Say Y to enable support for the battery charger control sysfs and
platform data of MAX8998/LP3974 PMICs.
+config CHARGER_BQ2415X
+ tristate "TI BQ2415x battery charger driver"
+ depends on I2C
+ help
+ Say Y to enable support for the TI BQ2415x battery charger
+ PMICs.
+
+ You'll need this driver to charge batteries on e.g. Nokia
+ RX-51/N900.
+
config CHARGER_SMB347
tristate "Summit Microelectronics SMB347 Battery Charger"
depends on I2C
@@ -329,13 +346,6 @@ config AB8500_BM
help
Say Y to include support for AB8500 battery management.
-config AB8500_BATTERY_THERM_ON_BATCTRL
- bool "Thermistor connected on BATCTRL ADC"
- depends on AB8500_BM
- help
- Say Y to enable battery temperature measurements using
- thermistor connected on BATCTRL ADC.
-
source "drivers/power/reset/Kconfig"
endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index f1d99f4a0bc..22c8913382c 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -37,7 +37,8 @@ obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o
-obj-$(CONFIG_AB8500_BM) += ab8500_charger.o ab8500_btemp.o ab8500_fg.o abx500_chargalg.o
+obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o
+obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_btemp.o ab8500_fg.o abx500_chargalg.o
obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o
obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o
@@ -47,6 +48,7 @@ obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
+obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
obj-$(CONFIG_POWER_AVS) += avs/
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
obj-$(CONFIG_POWER_RESET) += reset/
diff --git a/drivers/power/ab8500_bmdata.c b/drivers/power/ab8500_bmdata.c
new file mode 100644
index 00000000000..03cc528425c
--- /dev/null
+++ b/drivers/power/ab8500_bmdata.c
@@ -0,0 +1,521 @@
+#include <linux/export.h>
+#include <linux/power_supply.h>
+#include <linux/of.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+
+/*
+ * These are the defined batteries that uses a NTC and ID resistor placed
+ * inside of the battery pack.
+ * Note that the res_to_temp table must be strictly sorted by falling resistance
+ * values to work.
+ */
+static struct abx500_res_to_temp temp_tbl_A_thermistor[] = {
+ {-5, 53407},
+ { 0, 48594},
+ { 5, 43804},
+ {10, 39188},
+ {15, 34870},
+ {20, 30933},
+ {25, 27422},
+ {30, 24347},
+ {35, 21694},
+ {40, 19431},
+ {45, 17517},
+ {50, 15908},
+ {55, 14561},
+ {60, 13437},
+ {65, 12500},
+};
+
+static struct abx500_res_to_temp temp_tbl_B_thermistor[] = {
+ {-5, 200000},
+ { 0, 159024},
+ { 5, 151921},
+ {10, 144300},
+ {15, 136424},
+ {20, 128565},
+ {25, 120978},
+ {30, 113875},
+ {35, 107397},
+ {40, 101629},
+ {45, 96592},
+ {50, 92253},
+ {55, 88569},
+ {60, 85461},
+ {65, 82869},
+};
+
+static struct abx500_v_to_cap cap_tbl_A_thermistor[] = {
+ {4171, 100},
+ {4114, 95},
+ {4009, 83},
+ {3947, 74},
+ {3907, 67},
+ {3863, 59},
+ {3830, 56},
+ {3813, 53},
+ {3791, 46},
+ {3771, 33},
+ {3754, 25},
+ {3735, 20},
+ {3717, 17},
+ {3681, 13},
+ {3664, 8},
+ {3651, 6},
+ {3635, 5},
+ {3560, 3},
+ {3408, 1},
+ {3247, 0},
+};
+
+static struct abx500_v_to_cap cap_tbl_B_thermistor[] = {
+ {4161, 100},
+ {4124, 98},
+ {4044, 90},
+ {4003, 85},
+ {3966, 80},
+ {3933, 75},
+ {3888, 67},
+ {3849, 60},
+ {3813, 55},
+ {3787, 47},
+ {3772, 30},
+ {3751, 25},
+ {3718, 20},
+ {3681, 16},
+ {3660, 14},
+ {3589, 10},
+ {3546, 7},
+ {3495, 4},
+ {3404, 2},
+ {3250, 0},
+};
+
+static struct abx500_v_to_cap cap_tbl[] = {
+ {4186, 100},
+ {4163, 99},
+ {4114, 95},
+ {4068, 90},
+ {3990, 80},
+ {3926, 70},
+ {3898, 65},
+ {3866, 60},
+ {3833, 55},
+ {3812, 50},
+ {3787, 40},
+ {3768, 30},
+ {3747, 25},
+ {3730, 20},
+ {3705, 15},
+ {3699, 14},
+ {3684, 12},
+ {3672, 9},
+ {3657, 7},
+ {3638, 6},
+ {3556, 4},
+ {3424, 2},
+ {3317, 1},
+ {3094, 0},
+};
+
+/*
+ * Note that the res_to_temp table must be strictly sorted by falling
+ * resistance values to work.
+ */
+static struct abx500_res_to_temp temp_tbl[] = {
+ {-5, 214834},
+ { 0, 162943},
+ { 5, 124820},
+ {10, 96520},
+ {15, 75306},
+ {20, 59254},
+ {25, 47000},
+ {30, 37566},
+ {35, 30245},
+ {40, 24520},
+ {45, 20010},
+ {50, 16432},
+ {55, 13576},
+ {60, 11280},
+ {65, 9425},
+};
+
+/*
+ * Note that the batres_vs_temp table must be strictly sorted by falling
+ * temperature values to work.
+ */
+static struct batres_vs_temp temp_to_batres_tbl_thermistor[] = {
+ { 40, 120},
+ { 30, 135},
+ { 20, 165},
+ { 10, 230},
+ { 00, 325},
+ {-10, 445},
+ {-20, 595},
+};
+
+/*
+ * Note that the batres_vs_temp table must be strictly sorted by falling
+ * temperature values to work.
+ */
+static struct batres_vs_temp temp_to_batres_tbl_ext_thermistor[] = {
+ { 60, 300},
+ { 30, 300},
+ { 20, 300},
+ { 10, 300},
+ { 00, 300},
+ {-10, 300},
+ {-20, 300},
+};
+
+/* battery resistance table for LI ION 9100 battery */
+static struct batres_vs_temp temp_to_batres_tbl_9100[] = {
+ { 60, 180},
+ { 30, 180},
+ { 20, 180},
+ { 10, 180},
+ { 00, 180},
+ {-10, 180},
+ {-20, 180},
+};
+
+static struct abx500_battery_type bat_type_thermistor[] = {
+[BATTERY_UNKNOWN] = {
+ /* First element always represent the UNKNOWN battery */
+ .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
+ .resis_high = 0,
+ .resis_low = 0,
+ .battery_resistance = 300,
+ .charge_full_design = 612,
+ .nominal_voltage = 3700,
+ .termination_vol = 4050,
+ .termination_curr = 200,
+ .recharge_vol = 3990,
+ .normal_cur_lvl = 400,
+ .normal_vol_lvl = 4100,
+ .maint_a_cur_lvl = 400,
+ .maint_a_vol_lvl = 4050,
+ .maint_a_chg_timer_h = 60,
+ .maint_b_cur_lvl = 400,
+ .maint_b_vol_lvl = 4000,
+ .maint_b_chg_timer_h = 200,
+ .low_high_cur_lvl = 300,
+ .low_high_vol_lvl = 4000,
+ .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+ .r_to_t_tbl = temp_tbl,
+ .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+ .v_to_cap_tbl = cap_tbl,
+ .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+ .batres_tbl = temp_to_batres_tbl_thermistor,
+},
+{
+ .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
+ .resis_high = 53407,
+ .resis_low = 12500,
+ .battery_resistance = 300,
+ .charge_full_design = 900,
+ .nominal_voltage = 3600,
+ .termination_vol = 4150,
+ .termination_curr = 80,
+ .recharge_vol = 4130,
+ .normal_cur_lvl = 700,
+ .normal_vol_lvl = 4200,
+ .maint_a_cur_lvl = 600,
+ .maint_a_vol_lvl = 4150,
+ .maint_a_chg_timer_h = 60,
+ .maint_b_cur_lvl = 600,
+ .maint_b_vol_lvl = 4100,
+ .maint_b_chg_timer_h = 200,
+ .low_high_cur_lvl = 300,
+ .low_high_vol_lvl = 4000,
+ .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_A_thermistor),
+ .r_to_t_tbl = temp_tbl_A_thermistor,
+ .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_A_thermistor),
+ .v_to_cap_tbl = cap_tbl_A_thermistor,
+ .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+ .batres_tbl = temp_to_batres_tbl_thermistor,
+
+},
+{
+ .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
+ .resis_high = 200000,
+ .resis_low = 82869,
+ .battery_resistance = 300,
+ .charge_full_design = 900,
+ .nominal_voltage = 3600,
+ .termination_vol = 4150,
+ .termination_curr = 80,
+ .recharge_vol = 4130,
+ .normal_cur_lvl = 700,
+ .normal_vol_lvl = 4200,
+ .maint_a_cur_lvl = 600,
+ .maint_a_vol_lvl = 4150,
+ .maint_a_chg_timer_h = 60,
+ .maint_b_cur_lvl = 600,
+ .maint_b_vol_lvl = 4100,
+ .maint_b_chg_timer_h = 200,
+ .low_high_cur_lvl = 300,
+ .low_high_vol_lvl = 4000,
+ .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_B_thermistor),
+ .r_to_t_tbl = temp_tbl_B_thermistor,
+ .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_B_thermistor),
+ .v_to_cap_tbl = cap_tbl_B_thermistor,
+ .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+ .batres_tbl = temp_to_batres_tbl_thermistor,
+},
+};
+
+static struct abx500_battery_type bat_type_ext_thermistor[] = {
+[BATTERY_UNKNOWN] = {
+ /* First element always represent the UNKNOWN battery */
+ .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
+ .resis_high = 0,
+ .resis_low = 0,
+ .battery_resistance = 300,
+ .charge_full_design = 612,
+ .nominal_voltage = 3700,
+ .termination_vol = 4050,
+ .termination_curr = 200,
+ .recharge_vol = 3990,
+ .normal_cur_lvl = 400,
+ .normal_vol_lvl = 4100,
+ .maint_a_cur_lvl = 400,
+ .maint_a_vol_lvl = 4050,
+ .maint_a_chg_timer_h = 60,
+ .maint_b_cur_lvl = 400,
+ .maint_b_vol_lvl = 4000,
+ .maint_b_chg_timer_h = 200,
+ .low_high_cur_lvl = 300,
+ .low_high_vol_lvl = 4000,
+ .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+ .r_to_t_tbl = temp_tbl,
+ .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+ .v_to_cap_tbl = cap_tbl,
+ .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+ .batres_tbl = temp_to_batres_tbl_thermistor,
+},
+/*
+ * These are the batteries that doesn't have an internal NTC resistor to measure
+ * its temperature. The temperature in this case is measure with a NTC placed
+ * near the battery but on the PCB.
+ */
+{
+ .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
+ .resis_high = 76000,
+ .resis_low = 53000,
+ .battery_resistance = 300,
+ .charge_full_design = 900,
+ .nominal_voltage = 3700,
+ .termination_vol = 4150,
+ .termination_curr = 100,
+ .recharge_vol = 4130,
+ .normal_cur_lvl = 700,
+ .normal_vol_lvl = 4200,
+ .maint_a_cur_lvl = 600,
+ .maint_a_vol_lvl = 4150,
+ .maint_a_chg_timer_h = 60,
+ .maint_b_cur_lvl = 600,
+ .maint_b_vol_lvl = 4100,
+ .maint_b_chg_timer_h = 200,
+ .low_high_cur_lvl = 300,
+ .low_high_vol_lvl = 4000,
+ .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+ .r_to_t_tbl = temp_tbl,
+ .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+ .v_to_cap_tbl = cap_tbl,
+ .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+ .batres_tbl = temp_to_batres_tbl_thermistor,
+},
+{
+ .name = POWER_SUPPLY_TECHNOLOGY_LION,
+ .resis_high = 30000,
+ .resis_low = 10000,
+ .battery_resistance = 300,
+ .charge_full_design = 950,
+ .nominal_voltage = 3700,
+ .termination_vol = 4150,
+ .termination_curr = 100,
+ .recharge_vol = 4130,
+ .normal_cur_lvl = 700,
+ .normal_vol_lvl = 4200,
+ .maint_a_cur_lvl = 600,
+ .maint_a_vol_lvl = 4150,
+ .maint_a_chg_timer_h = 60,
+ .maint_b_cur_lvl = 600,
+ .maint_b_vol_lvl = 4100,
+ .maint_b_chg_timer_h = 200,
+ .low_high_cur_lvl = 300,
+ .low_high_vol_lvl = 4000,
+ .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+ .r_to_t_tbl = temp_tbl,
+ .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+ .v_to_cap_tbl = cap_tbl,
+ .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+ .batres_tbl = temp_to_batres_tbl_thermistor,
+},
+{
+ .name = POWER_SUPPLY_TECHNOLOGY_LION,
+ .resis_high = 95000,
+ .resis_low = 76001,
+ .battery_resistance = 300,
+ .charge_full_design = 950,
+ .nominal_voltage = 3700,
+ .termination_vol = 4150,
+ .termination_curr = 100,
+ .recharge_vol = 4130,
+ .normal_cur_lvl = 700,
+ .normal_vol_lvl = 4200,
+ .maint_a_cur_lvl = 600,
+ .maint_a_vol_lvl = 4150,
+ .maint_a_chg_timer_h = 60,
+ .maint_b_cur_lvl = 600,
+ .maint_b_vol_lvl = 4100,
+ .maint_b_chg_timer_h = 200,
+ .low_high_cur_lvl = 300,
+ .low_high_vol_lvl = 4000,
+ .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+ .r_to_t_tbl = temp_tbl,
+ .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+ .v_to_cap_tbl = cap_tbl,
+ .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
+ .batres_tbl = temp_to_batres_tbl_thermistor,
+},
+};
+
+static const struct abx500_bm_capacity_levels cap_levels = {
+ .critical = 2,
+ .low = 10,
+ .normal = 70,
+ .high = 95,
+ .full = 100,
+};
+
+static const struct abx500_fg_parameters fg = {
+ .recovery_sleep_timer = 10,
+ .recovery_total_time = 100,
+ .init_timer = 1,
+ .init_discard_time = 5,
+ .init_total_time = 40,
+ .high_curr_time = 60,
+ .accu_charging = 30,
+ .accu_high_curr = 30,
+ .high_curr_threshold = 50,
+ .lowbat_threshold = 3100,
+ .battok_falling_th_sel0 = 2860,
+ .battok_raising_th_sel1 = 2860,
+ .user_cap_limit = 15,
+ .maint_thres = 97,
+};
+
+static const struct abx500_maxim_parameters maxi_params = {
+ .ena_maxi = true,
+ .chg_curr = 910,
+ .wait_cycles = 10,
+ .charger_curr_step = 100,
+};
+
+static const struct abx500_bm_charger_parameters chg = {
+ .usb_volt_max = 5500,
+ .usb_curr_max = 1500,
+ .ac_volt_max = 7500,
+ .ac_curr_max = 1500,
+};
+
+struct abx500_bm_data ab8500_bm_data = {
+ .temp_under = 3,
+ .temp_low = 8,
+ .temp_high = 43,
+ .temp_over = 48,
+ .main_safety_tmr_h = 4,
+ .temp_interval_chg = 20,
+ .temp_interval_nochg = 120,
+ .usb_safety_tmr_h = 4,
+ .bkup_bat_v = BUP_VCH_SEL_2P6V,
+ .bkup_bat_i = BUP_ICH_SEL_150UA,
+ .no_maintenance = false,
+ .adc_therm = ABx500_ADC_THERM_BATCTRL,
+ .chg_unknown_bat = false,
+ .enable_overshoot = false,
+ .fg_res = 100,
+ .cap_levels = &cap_levels,
+ .bat_type = bat_type_thermistor,
+ .n_btypes = 3,
+ .batt_id = 0,
+ .interval_charging = 5,
+ .interval_not_charging = 120,
+ .temp_hysteresis = 3,
+ .gnd_lift_resistance = 34,
+ .maxi = &maxi_params,
+ .chg_params = &chg,
+ .fg_params = &fg,
+};
+
+int __devinit
+bmdevs_of_probe(struct device *dev,
+ struct device_node *np,
+ struct abx500_bm_data **battery)
+{
+ struct abx500_battery_type *btype;
+ struct device_node *np_bat_supply;
+ struct abx500_bm_data *bat;
+ const char *btech;
+ char bat_tech[8];
+ int i, thermistor;
+
+ *battery = &ab8500_bm_data;
+
+ /* get phandle to 'battery-info' node */
+ np_bat_supply = of_parse_phandle(np, "battery", 0);
+ if (!np_bat_supply) {
+ dev_err(dev, "missing property battery\n");
+ return -EINVAL;
+ }
+ if (of_property_read_bool(np_bat_supply,
+ "thermistor-on-batctrl"))
+ thermistor = NTC_INTERNAL;
+ else
+ thermistor = NTC_EXTERNAL;
+
+ bat = *battery;
+ if (thermistor == NTC_EXTERNAL) {
+ bat->n_btypes = 4;
+ bat->bat_type = bat_type_ext_thermistor;
+ bat->adc_therm = ABx500_ADC_THERM_BATTEMP;
+ }
+ btech = of_get_property(np_bat_supply,
+ "stericsson,battery-type", NULL);
+ if (!btech) {
+ dev_warn(dev, "missing property battery-name/type\n");
+ strcpy(bat_tech, "UNKNOWN");
+ } else {
+ strcpy(bat_tech, btech);
+ }
+
+ if (strncmp(bat_tech, "LION", 4) == 0) {
+ bat->no_maintenance = true;
+ bat->chg_unknown_bat = true;
+ bat->bat_type[BATTERY_UNKNOWN].charge_full_design = 2600;
+ bat->bat_type[BATTERY_UNKNOWN].termination_vol = 4150;
+ bat->bat_type[BATTERY_UNKNOWN].recharge_vol = 4130;
+ bat->bat_type[BATTERY_UNKNOWN].normal_cur_lvl = 520;
+ bat->bat_type[BATTERY_UNKNOWN].normal_vol_lvl = 4200;
+ }
+ /* select the battery resolution table */
+ for (i = 0; i < bat->n_btypes; ++i) {
+ btype = (bat->bat_type + i);
+ if (thermistor == NTC_EXTERNAL) {
+ btype->batres_tbl =
+ temp_to_batres_tbl_ext_thermistor;
+ } else if (strncmp(bat_tech, "LION", 4) == 0) {
+ btype->batres_tbl =
+ temp_to_batres_tbl_9100;
+ } else {
+ btype->batres_tbl =
+ temp_to_batres_tbl_thermistor;
+ }
+ }
+ of_node_put(np_bat_supply);
+ return 0;
+}
diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
index 989b09950af..20e2a7d3ef4 100644
--- a/drivers/power/ab8500_btemp.c
+++ b/drivers/power/ab8500_btemp.c
@@ -20,11 +20,13 @@
#include <linux/power_supply.h>
#include <linux/completion.h>
#include <linux/workqueue.h>
-#include <linux/mfd/abx500/ab8500.h>
+#include <linux/jiffies.h>
+#include <linux/of.h>
+#include <linux/mfd/core.h>
#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500/ab8500-bm.h>
#include <linux/mfd/abx500/ab8500-gpadc.h>
-#include <linux/jiffies.h>
#define VTVOUT_V 1800
@@ -76,7 +78,6 @@ struct ab8500_btemp_ranges {
* @parent: Pointer to the struct ab8500
* @gpadc: Pointer to the struct gpadc
* @fg: Pointer to the struct fg
- * @pdata: Pointer to the abx500_btemp platform data
* @bat: Pointer to the abx500_bm platform data
* @btemp_psy: Structure for BTEMP specific battery properties
* @events: Structure for information about events triggered
@@ -93,7 +94,6 @@ struct ab8500_btemp {
struct ab8500 *parent;
struct ab8500_gpadc *gpadc;
struct ab8500_fg *fg;
- struct abx500_btemp_platform_data *pdata;
struct abx500_bm_data *bat;
struct power_supply btemp_psy;
struct ab8500_btemp_events events;
@@ -955,56 +955,57 @@ static int ab8500_btemp_remove(struct platform_device *pdev)
flush_scheduled_work();
power_supply_unregister(&di->btemp_psy);
platform_set_drvdata(pdev, NULL);
- kfree(di);
return 0;
}
+static char *supply_interface[] = {
+ "ab8500_chargalg",
+ "ab8500_fg",
+};
+
static int ab8500_btemp_probe(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
+ struct ab8500_btemp *di;
int irq, i, ret = 0;
u8 val;
- struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
- struct ab8500_btemp *di;
-
- if (!plat_data) {
- dev_err(&pdev->dev, "No platform data\n");
- return -EINVAL;
- }
- di = kzalloc(sizeof(*di), GFP_KERNEL);
- if (!di)
+ di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
+ if (!di) {
+ dev_err(&pdev->dev, "%s no mem for ab8500_btemp\n", __func__);
return -ENOMEM;
+ }
+ di->bat = pdev->mfd_cell->platform_data;
+ if (!di->bat) {
+ if (np) {
+ ret = bmdevs_of_probe(&pdev->dev, np, &di->bat);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to get battery information\n");
+ return ret;
+ }
+ } else {
+ dev_err(&pdev->dev, "missing dt node for ab8500_btemp\n");
+ return -EINVAL;
+ }
+ } else {
+ dev_info(&pdev->dev, "falling back to legacy platform data\n");
+ }
/* get parent data */
di->dev = &pdev->dev;
di->parent = dev_get_drvdata(pdev->dev.parent);
di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
- /* get btemp specific platform data */
- di->pdata = plat_data->btemp;
- if (!di->pdata) {
- dev_err(di->dev, "no btemp platform data supplied\n");
- ret = -EINVAL;
- goto free_device_info;
- }
-
- /* get battery specific platform data */
- di->bat = plat_data->battery;
- if (!di->bat) {
- dev_err(di->dev, "no battery platform data supplied\n");
- ret = -EINVAL;
- goto free_device_info;
- }
-
/* BTEMP supply */
di->btemp_psy.name = "ab8500_btemp";
di->btemp_psy.type = POWER_SUPPLY_TYPE_BATTERY;
di->btemp_psy.properties = ab8500_btemp_props;
di->btemp_psy.num_properties = ARRAY_SIZE(ab8500_btemp_props);
di->btemp_psy.get_property = ab8500_btemp_get_property;
- di->btemp_psy.supplied_to = di->pdata->supplied_to;
- di->btemp_psy.num_supplicants = di->pdata->num_supplicants;
+ di->btemp_psy.supplied_to = supply_interface;
+ di->btemp_psy.num_supplicants = ARRAY_SIZE(supply_interface);
di->btemp_psy.external_power_changed =
ab8500_btemp_external_power_changed;
@@ -1014,8 +1015,7 @@ static int ab8500_btemp_probe(struct platform_device *pdev)
create_singlethread_workqueue("ab8500_btemp_wq");
if (di->btemp_wq == NULL) {
dev_err(di->dev, "failed to create work queue\n");
- ret = -ENOMEM;
- goto free_device_info;
+ return -ENOMEM;
}
/* Init work for measuring temperature periodically */
@@ -1093,12 +1093,14 @@ free_irq:
}
free_btemp_wq:
destroy_workqueue(di->btemp_wq);
-free_device_info:
- kfree(di);
-
return ret;
}
+static const struct of_device_id ab8500_btemp_match[] = {
+ { .compatible = "stericsson,ab8500-btemp", },
+ { },
+};
+
static struct platform_driver ab8500_btemp_driver = {
.probe = ab8500_btemp_probe,
.remove = ab8500_btemp_remove,
@@ -1107,6 +1109,7 @@ static struct platform_driver ab8500_btemp_driver = {
.driver = {
.name = "ab8500-btemp",
.owner = THIS_MODULE,
+ .of_match_table = ab8500_btemp_match,
},
};
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 7ecb8abe20b..3be9c0ee3fc 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -23,6 +23,8 @@
#include <linux/err.h>
#include <linux/workqueue.h>
#include <linux/kobject.h>
+#include <linux/of.h>
+#include <linux/mfd/core.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500-bm.h>
@@ -181,9 +183,9 @@ struct ab8500_charger_usb_state {
* @vbat Battery voltage
* @old_vbat Previously measured battery voltage
* @autopower Indicate if we should have automatic pwron after pwrloss
+ * @autopower_cfg platform specific power config support for "pwron after pwrloss"
* @parent: Pointer to the struct ab8500
* @gpadc: Pointer to the struct gpadc
- * @pdata: Pointer to the abx500_charger platform data
* @bat: Pointer to the abx500_bm platform data
* @flags: Structure for information about events triggered
* @usb_state: Structure for usb stack information
@@ -218,9 +220,9 @@ struct ab8500_charger {
int vbat;
int old_vbat;
bool autopower;
+ bool autopower_cfg;
struct ab8500 *parent;
struct ab8500_gpadc *gpadc;
- struct abx500_charger_platform_data *pdata;
struct abx500_bm_data *bat;
struct ab8500_charger_event_flags flags;
struct ab8500_charger_usb_state usb_state;
@@ -322,7 +324,7 @@ static void ab8500_power_loss_handling(struct ab8500_charger *di)
static void ab8500_power_supply_changed(struct ab8500_charger *di,
struct power_supply *psy)
{
- if (di->pdata->autopower_cfg) {
+ if (di->autopower_cfg) {
if (!di->usb.charger_connected &&
!di->ac.charger_connected &&
di->autopower) {
@@ -2526,25 +2528,45 @@ static int ab8500_charger_remove(struct platform_device *pdev)
power_supply_unregister(&di->usb_chg.psy);
power_supply_unregister(&di->ac_chg.psy);
platform_set_drvdata(pdev, NULL);
- kfree(di);
return 0;
}
+static char *supply_interface[] = {
+ "ab8500_chargalg",
+ "ab8500_fg",
+ "ab8500_btemp",
+};
+
static int ab8500_charger_probe(struct platform_device *pdev)
{
- int irq, i, charger_status, ret = 0;
- struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
+ struct device_node *np = pdev->dev.of_node;
struct ab8500_charger *di;
+ int irq, i, charger_status, ret = 0;
- if (!plat_data) {
- dev_err(&pdev->dev, "No platform data\n");
- return -EINVAL;
- }
-
- di = kzalloc(sizeof(*di), GFP_KERNEL);
- if (!di)
+ di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
+ if (!di) {
+ dev_err(&pdev->dev, "%s no mem for ab8500_charger\n", __func__);
return -ENOMEM;
+ }
+ di->bat = pdev->mfd_cell->platform_data;
+ if (!di->bat) {
+ if (np) {
+ ret = bmdevs_of_probe(&pdev->dev, np, &di->bat);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to get battery information\n");
+ return ret;
+ }
+ di->autopower_cfg = of_property_read_bool(np, "autopower_cfg");
+ } else {
+ dev_err(&pdev->dev, "missing dt node for ab8500_charger\n");
+ return -EINVAL;
+ }
+ } else {
+ dev_info(&pdev->dev, "falling back to legacy platform data\n");
+ di->autopower_cfg = false;
+ }
/* get parent data */
di->dev = &pdev->dev;
@@ -2554,22 +2576,6 @@ static int ab8500_charger_probe(struct platform_device *pdev)
/* initialize lock */
spin_lock_init(&di->usb_state.usb_lock);
- /* get charger specific platform data */
- di->pdata = plat_data->charger;
- if (!di->pdata) {
- dev_err(di->dev, "no charger platform data supplied\n");
- ret = -EINVAL;
- goto free_device_info;
- }
-
- /* get battery specific platform data */
- di->bat = plat_data->battery;
- if (!di->bat) {
- dev_err(di->dev, "no battery platform data supplied\n");
- ret = -EINVAL;
- goto free_device_info;
- }
-
di->autopower = false;
/* AC supply */
@@ -2579,8 +2585,8 @@ static int ab8500_charger_probe(struct platform_device *pdev)
di->ac_chg.psy.properties = ab8500_charger_ac_props;
di->ac_chg.psy.num_properties = ARRAY_SIZE(ab8500_charger_ac_props);
di->ac_chg.psy.get_property = ab8500_charger_ac_get_property;
- di->ac_chg.psy.supplied_to = di->pdata->supplied_to;
- di->ac_chg.psy.num_supplicants = di->pdata->num_supplicants;
+ di->ac_chg.psy.supplied_to = supply_interface;
+ di->ac_chg.psy.num_supplicants = ARRAY_SIZE(supply_interface),
/* ux500_charger sub-class */
di->ac_chg.ops.enable = &ab8500_charger_ac_en;
di->ac_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
@@ -2597,8 +2603,8 @@ static int ab8500_charger_probe(struct platform_device *pdev)
di->usb_chg.psy.properties = ab8500_charger_usb_props;
di->usb_chg.psy.num_properties = ARRAY_SIZE(ab8500_charger_usb_props);
di->usb_chg.psy.get_property = ab8500_charger_usb_get_property;
- di->usb_chg.psy.supplied_to = di->pdata->supplied_to;
- di->usb_chg.psy.num_supplicants = di->pdata->num_supplicants;
+ di->usb_chg.psy.supplied_to = supply_interface;
+ di->usb_chg.psy.num_supplicants = ARRAY_SIZE(supply_interface),
/* ux500_charger sub-class */
di->usb_chg.ops.enable = &ab8500_charger_usb_en;
di->usb_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
@@ -2614,8 +2620,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
create_singlethread_workqueue("ab8500_charger_wq");
if (di->charger_wq == NULL) {
dev_err(di->dev, "failed to create work queue\n");
- ret = -ENOMEM;
- goto free_device_info;
+ return -ENOMEM;
}
/* Init work for HW failure check */
@@ -2757,12 +2762,14 @@ free_regulator:
regulator_put(di->regu);
free_charger_wq:
destroy_workqueue(di->charger_wq);
-free_device_info:
- kfree(di);
-
return ret;
}
+static const struct of_device_id ab8500_charger_match[] = {
+ { .compatible = "stericsson,ab8500-charger", },
+ { },
+};
+
static struct platform_driver ab8500_charger_driver = {
.probe = ab8500_charger_probe,
.remove = ab8500_charger_remove,
@@ -2771,6 +2778,7 @@ static struct platform_driver ab8500_charger_driver = {
.driver = {
.name = "ab8500-charger",
.owner = THIS_MODULE,
+ .of_match_table = ab8500_charger_match,
},
};
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index 331dc43ded4..b3bf178c346 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -22,15 +22,16 @@
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/kobject.h>
-#include <linux/mfd/abx500/ab8500.h>
-#include <linux/mfd/abx500.h>
#include <linux/slab.h>
-#include <linux/mfd/abx500/ab8500-bm.h>
#include <linux/delay.h>
-#include <linux/mfd/abx500/ab8500-gpadc.h>
-#include <linux/mfd/abx500.h>
#include <linux/time.h>
+#include <linux/of.h>
#include <linux/completion.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
#define MILLI_TO_MICRO 1000
#define FG_LSB_IN_MA 1627
@@ -172,7 +173,6 @@ struct inst_curr_result_list {
* @avg_cap: Average capacity filter
* @parent: Pointer to the struct ab8500
* @gpadc: Pointer to the struct gpadc
- * @pdata: Pointer to the abx500_fg platform data
* @bat: Pointer to the abx500_bm platform data
* @fg_psy: Structure that holds the FG specific battery properties
* @fg_wq: Work queue for running the FG algorithm
@@ -212,7 +212,6 @@ struct ab8500_fg {
struct ab8500_fg_avg_cap avg_cap;
struct ab8500 *parent;
struct ab8500_gpadc *gpadc;
- struct abx500_fg_platform_data *pdata;
struct abx500_bm_data *bat;
struct power_supply fg_psy;
struct workqueue_struct *fg_wq;
@@ -2429,7 +2428,6 @@ static int ab8500_fg_remove(struct platform_device *pdev)
flush_scheduled_work();
power_supply_unregister(&di->fg_psy);
platform_set_drvdata(pdev, NULL);
- kfree(di);
return ret;
}
@@ -2442,21 +2440,39 @@ static struct ab8500_fg_interrupts ab8500_fg_irq[] = {
{"CCEOC", ab8500_fg_cc_data_end_handler},
};
+static char *supply_interface[] = {
+ "ab8500_chargalg",
+ "ab8500_usb",
+};
+
static int ab8500_fg_probe(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
+ struct ab8500_fg *di;
int i, irq;
int ret = 0;
- struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
- struct ab8500_fg *di;
-
- if (!plat_data) {
- dev_err(&pdev->dev, "No platform data\n");
- return -EINVAL;
- }
- di = kzalloc(sizeof(*di), GFP_KERNEL);
- if (!di)
+ di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
+ if (!di) {
+ dev_err(&pdev->dev, "%s no mem for ab8500_fg\n", __func__);
return -ENOMEM;
+ }
+ di->bat = pdev->mfd_cell->platform_data;
+ if (!di->bat) {
+ if (np) {
+ ret = bmdevs_of_probe(&pdev->dev, np, &di->bat);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to get battery information\n");
+ return ret;
+ }
+ } else {
+ dev_err(&pdev->dev, "missing dt node for ab8500_fg\n");
+ return -EINVAL;
+ }
+ } else {
+ dev_info(&pdev->dev, "falling back to legacy platform data\n");
+ }
mutex_init(&di->cc_lock);
@@ -2465,29 +2481,13 @@ static int ab8500_fg_probe(struct platform_device *pdev)
di->parent = dev_get_drvdata(pdev->dev.parent);
di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
- /* get fg specific platform data */
- di->pdata = plat_data->fg;
- if (!di->pdata) {
- dev_err(di->dev, "no fg platform data supplied\n");
- ret = -EINVAL;
- goto free_device_info;
- }
-
- /* get battery specific platform data */
- di->bat = plat_data->battery;
- if (!di->bat) {
- dev_err(di->dev, "no battery platform data supplied\n");
- ret = -EINVAL;
- goto free_device_info;
- }
-
di->fg_psy.name = "ab8500_fg";
di->fg_psy.type = POWER_SUPPLY_TYPE_BATTERY;
di->fg_psy.properties = ab8500_fg_props;
di->fg_psy.num_properties = ARRAY_SIZE(ab8500_fg_props);
di->fg_psy.get_property = ab8500_fg_get_property;
- di->fg_psy.supplied_to = di->pdata->supplied_to;
- di->fg_psy.num_supplicants = di->pdata->num_supplicants;
+ di->fg_psy.supplied_to = supply_interface;
+ di->fg_psy.num_supplicants = ARRAY_SIZE(supply_interface),
di->fg_psy.external_power_changed = ab8500_fg_external_power_changed;
di->bat_cap.max_mah_design = MILLI_TO_MICRO *
@@ -2506,8 +2506,7 @@ static int ab8500_fg_probe(struct platform_device *pdev)
di->fg_wq = create_singlethread_workqueue("ab8500_fg_wq");
if (di->fg_wq == NULL) {
dev_err(di->dev, "failed to create work queue\n");
- ret = -ENOMEM;
- goto free_device_info;
+ return -ENOMEM;
}
/* Init work for running the fg algorithm instantly */
@@ -2606,12 +2605,14 @@ free_irq:
}
free_inst_curr_wq:
destroy_workqueue(di->fg_wq);
-free_device_info:
- kfree(di);
-
return ret;
}
+static const struct of_device_id ab8500_fg_match[] = {
+ { .compatible = "stericsson,ab8500-fg", },
+ { },
+};
+
static struct platform_driver ab8500_fg_driver = {
.probe = ab8500_fg_probe,
.remove = ab8500_fg_remove,
@@ -2620,6 +2621,7 @@ static struct platform_driver ab8500_fg_driver = {
.driver = {
.name = "ab8500-fg",
.owner = THIS_MODULE,
+ .of_match_table = ab8500_fg_match,
},
};
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index 19f25419079..29708914606 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -21,6 +21,8 @@
#include <linux/completion.h>
#include <linux/workqueue.h>
#include <linux/kobject.h>
+#include <linux/of.h>
+#include <linux/mfd/core.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ux500_chargalg.h>
#include <linux/mfd/abx500/ab8500-bm.h>
@@ -205,7 +207,6 @@ enum maxim_ret {
* @chg_info: information about connected charger types
* @batt_data: data of the battery
* @susp_status: current charger suspension status
- * @pdata: pointer to the abx500_chargalg platform data
* @bat: pointer to the abx500_bm platform data
* @chargalg_psy: structure that holds the battery properties exposed by
* the charging algorithm
@@ -231,7 +232,6 @@ struct abx500_chargalg {
struct abx500_chargalg_charger_info chg_info;
struct abx500_chargalg_battery_data batt_data;
struct abx500_chargalg_suspension_status susp_status;
- struct abx500_chargalg_platform_data *pdata;
struct abx500_bm_data *bat;
struct power_supply chargalg_psy;
struct ux500_charger *ac_chg;
@@ -1795,36 +1795,53 @@ static int abx500_chargalg_remove(struct platform_device *pdev)
flush_scheduled_work();
power_supply_unregister(&di->chargalg_psy);
platform_set_drvdata(pdev, NULL);
- kfree(di);
return 0;
}
+static char *supply_interface[] = {
+ "ab8500_fg",
+};
+
static int abx500_chargalg_probe(struct platform_device *pdev)
{
- struct abx500_bm_plat_data *plat_data;
+ struct device_node *np = pdev->dev.of_node;
+ struct abx500_chargalg *di;
int ret = 0;
- struct abx500_chargalg *di =
- kzalloc(sizeof(struct abx500_chargalg), GFP_KERNEL);
- if (!di)
+ di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
+ if (!di) {
+ dev_err(&pdev->dev, "%s no mem for ab8500_chargalg\n", __func__);
return -ENOMEM;
+ }
+ di->bat = pdev->mfd_cell->platform_data;
+ if (!di->bat) {
+ if (np) {
+ ret = bmdevs_of_probe(&pdev->dev, np, &di->bat);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to get battery information\n");
+ return ret;
+ }
+ } else {
+ dev_err(&pdev->dev, "missing dt node for ab8500_chargalg\n");
+ return -EINVAL;
+ }
+ } else {
+ dev_info(&pdev->dev, "falling back to legacy platform data\n");
+ }
/* get device struct */
di->dev = &pdev->dev;
- plat_data = pdev->dev.platform_data;
- di->pdata = plat_data->chargalg;
- di->bat = plat_data->battery;
-
/* chargalg supply */
di->chargalg_psy.name = "abx500_chargalg";
di->chargalg_psy.type = POWER_SUPPLY_TYPE_BATTERY;
di->chargalg_psy.properties = abx500_chargalg_props;
di->chargalg_psy.num_properties = ARRAY_SIZE(abx500_chargalg_props);
di->chargalg_psy.get_property = abx500_chargalg_get_property;
- di->chargalg_psy.supplied_to = di->pdata->supplied_to;
- di->chargalg_psy.num_supplicants = di->pdata->num_supplicants;
+ di->chargalg_psy.supplied_to = supply_interface;
+ di->chargalg_psy.num_supplicants = ARRAY_SIZE(supply_interface),
di->chargalg_psy.external_power_changed =
abx500_chargalg_external_power_changed;
@@ -1844,7 +1861,7 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
create_singlethread_workqueue("abx500_chargalg_wq");
if (di->chargalg_wq == NULL) {
dev_err(di->dev, "failed to create work queue\n");
- goto free_device_info;
+ return -ENOMEM;
}
/* Init work for chargalg */
@@ -1885,20 +1902,23 @@ free_psy:
power_supply_unregister(&di->chargalg_psy);
free_chargalg_wq:
destroy_workqueue(di->chargalg_wq);
-free_device_info:
- kfree(di);
-
return ret;
}
+static const struct of_device_id ab8500_chargalg_match[] = {
+ { .compatible = "stericsson,ab8500-chargalg", },
+ { },
+};
+
static struct platform_driver abx500_chargalg_driver = {
.probe = abx500_chargalg_probe,
.remove = abx500_chargalg_remove,
.suspend = abx500_chargalg_suspend,
.resume = abx500_chargalg_resume,
.driver = {
- .name = "abx500-chargalg",
+ .name = "ab8500-chargalg",
.owner = THIS_MODULE,
+ .of_match_table = ab8500_chargalg_match,
},
};
diff --git a/drivers/power/bq2415x_charger.c b/drivers/power/bq2415x_charger.c
new file mode 100644
index 00000000000..ee842b37f46
--- /dev/null
+++ b/drivers/power/bq2415x_charger.c
@@ -0,0 +1,1670 @@
+/*
+ * bq2415x charger driver
+ *
+ * Copyright (C) 2011-2012 Pali Rohár <pali.rohar@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * Datasheets:
+ * http://www.ti.com/product/bq24150
+ * http://www.ti.com/product/bq24150a
+ * http://www.ti.com/product/bq24152
+ * http://www.ti.com/product/bq24153
+ * http://www.ti.com/product/bq24153a
+ * http://www.ti.com/product/bq24155
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/idr.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+
+#include <linux/power/bq2415x_charger.h>
+
+/* timeout for resetting chip timer */
+#define BQ2415X_TIMER_TIMEOUT 10
+
+#define BQ2415X_REG_STATUS 0x00
+#define BQ2415X_REG_CONTROL 0x01
+#define BQ2415X_REG_VOLTAGE 0x02
+#define BQ2415X_REG_VENDER 0x03
+#define BQ2415X_REG_CURRENT 0x04
+
+/* reset state for all registers */
+#define BQ2415X_RESET_STATUS BIT(6)
+#define BQ2415X_RESET_CONTROL (BIT(4)|BIT(5))
+#define BQ2415X_RESET_VOLTAGE (BIT(1)|BIT(3))
+#define BQ2415X_RESET_CURRENT (BIT(0)|BIT(3)|BIT(7))
+
+/* status register */
+#define BQ2415X_BIT_TMR_RST 7
+#define BQ2415X_BIT_OTG 7
+#define BQ2415X_BIT_EN_STAT 6
+#define BQ2415X_MASK_STAT (BIT(4)|BIT(5))
+#define BQ2415X_SHIFT_STAT 4
+#define BQ2415X_BIT_BOOST 3
+#define BQ2415X_MASK_FAULT (BIT(0)|BIT(1)|BIT(2))
+#define BQ2415X_SHIFT_FAULT 0
+
+/* control register */
+#define BQ2415X_MASK_LIMIT (BIT(6)|BIT(7))
+#define BQ2415X_SHIFT_LIMIT 6
+#define BQ2415X_MASK_VLOWV (BIT(4)|BIT(5))
+#define BQ2415X_SHIFT_VLOWV 4
+#define BQ2415X_BIT_TE 3
+#define BQ2415X_BIT_CE 2
+#define BQ2415X_BIT_HZ_MODE 1
+#define BQ2415X_BIT_OPA_MODE 0
+
+/* voltage register */
+#define BQ2415X_MASK_VO (BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7))
+#define BQ2415X_SHIFT_VO 2
+#define BQ2415X_BIT_OTG_PL 1
+#define BQ2415X_BIT_OTG_EN 0
+
+/* vender register */
+#define BQ2415X_MASK_VENDER (BIT(5)|BIT(6)|BIT(7))
+#define BQ2415X_SHIFT_VENDER 5
+#define BQ2415X_MASK_PN (BIT(3)|BIT(4))
+#define BQ2415X_SHIFT_PN 3
+#define BQ2415X_MASK_REVISION (BIT(0)|BIT(1)|BIT(2))
+#define BQ2415X_SHIFT_REVISION 0
+
+/* current register */
+#define BQ2415X_MASK_RESET BIT(7)
+#define BQ2415X_MASK_VI_CHRG (BIT(4)|BIT(5)|BIT(6))
+#define BQ2415X_SHIFT_VI_CHRG 4
+/* N/A BIT(3) */
+#define BQ2415X_MASK_VI_TERM (BIT(0)|BIT(1)|BIT(2))
+#define BQ2415X_SHIFT_VI_TERM 0
+
+
+enum bq2415x_command {
+ BQ2415X_TIMER_RESET,
+ BQ2415X_OTG_STATUS,
+ BQ2415X_STAT_PIN_STATUS,
+ BQ2415X_STAT_PIN_ENABLE,
+ BQ2415X_STAT_PIN_DISABLE,
+ BQ2415X_CHARGE_STATUS,
+ BQ2415X_BOOST_STATUS,
+ BQ2415X_FAULT_STATUS,
+
+ BQ2415X_CHARGE_TERMINATION_STATUS,
+ BQ2415X_CHARGE_TERMINATION_ENABLE,
+ BQ2415X_CHARGE_TERMINATION_DISABLE,
+ BQ2415X_CHARGER_STATUS,
+ BQ2415X_CHARGER_ENABLE,
+ BQ2415X_CHARGER_DISABLE,
+ BQ2415X_HIGH_IMPEDANCE_STATUS,
+ BQ2415X_HIGH_IMPEDANCE_ENABLE,
+ BQ2415X_HIGH_IMPEDANCE_DISABLE,
+ BQ2415X_BOOST_MODE_STATUS,
+ BQ2415X_BOOST_MODE_ENABLE,
+ BQ2415X_BOOST_MODE_DISABLE,
+
+ BQ2415X_OTG_LEVEL,
+ BQ2415X_OTG_ACTIVATE_HIGH,
+ BQ2415X_OTG_ACTIVATE_LOW,
+ BQ2415X_OTG_PIN_STATUS,
+ BQ2415X_OTG_PIN_ENABLE,
+ BQ2415X_OTG_PIN_DISABLE,
+
+ BQ2415X_VENDER_CODE,
+ BQ2415X_PART_NUMBER,
+ BQ2415X_REVISION,
+};
+
+enum bq2415x_chip {
+ BQUNKNOWN,
+ BQ24150,
+ BQ24150A,
+ BQ24151,
+ BQ24151A,
+ BQ24152,
+ BQ24153,
+ BQ24153A,
+ BQ24155,
+ BQ24156,
+ BQ24156A,
+ BQ24158,
+};
+
+static char *bq2415x_chip_name[] = {
+ "unknown",
+ "bq24150",
+ "bq24150a",
+ "bq24151",
+ "bq24151a",
+ "bq24152",
+ "bq24153",
+ "bq24153a",
+ "bq24155",
+ "bq24156",
+ "bq24156a",
+ "bq24158",
+};
+
+struct bq2415x_device {
+ struct device *dev;
+ struct bq2415x_platform_data init_data;
+ struct power_supply charger;
+ struct delayed_work work;
+ enum bq2415x_mode reported_mode;/* mode reported by hook function */
+ enum bq2415x_mode mode; /* current configured mode */
+ enum bq2415x_chip chip;
+ const char *timer_error;
+ char *model;
+ char *name;
+ int autotimer; /* 1 - if driver automatically reset timer, 0 - not */
+ int automode; /* 1 - enabled, 0 - disabled; -1 - not supported */
+ int id;
+};
+
+/* each registered chip must have unique id */
+static DEFINE_IDR(bq2415x_id);
+
+static DEFINE_MUTEX(bq2415x_id_mutex);
+static DEFINE_MUTEX(bq2415x_timer_mutex);
+static DEFINE_MUTEX(bq2415x_i2c_mutex);
+
+/**** i2c read functions ****/
+
+/* read value from register */
+static int bq2415x_i2c_read(struct bq2415x_device *bq, u8 reg)
+{
+ struct i2c_client *client = to_i2c_client(bq->dev);
+ struct i2c_msg msg[2];
+ u8 val;
+ int ret;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].buf = &reg;
+ msg[0].len = sizeof(reg);
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].buf = &val;
+ msg[1].len = sizeof(val);
+
+ mutex_lock(&bq2415x_i2c_mutex);
+ ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+ mutex_unlock(&bq2415x_i2c_mutex);
+
+ if (ret < 0)
+ return ret;
+
+ return val;
+}
+
+/* read value from register, apply mask and right shift it */
+static int bq2415x_i2c_read_mask(struct bq2415x_device *bq, u8 reg,
+ u8 mask, u8 shift)
+{
+ int ret;
+
+ if (shift > 8)
+ return -EINVAL;
+
+ ret = bq2415x_i2c_read(bq, reg);
+ if (ret < 0)
+ return ret;
+ return (ret & mask) >> shift;
+}
+
+/* read value from register and return one specified bit */
+static int bq2415x_i2c_read_bit(struct bq2415x_device *bq, u8 reg, u8 bit)
+{
+ if (bit > 8)
+ return -EINVAL;
+ return bq2415x_i2c_read_mask(bq, reg, BIT(bit), bit);
+}
+
+/**** i2c write functions ****/
+
+/* write value to register */
+static int bq2415x_i2c_write(struct bq2415x_device *bq, u8 reg, u8 val)
+{
+ struct i2c_client *client = to_i2c_client(bq->dev);
+ struct i2c_msg msg[1];
+ u8 data[2];
+ int ret;
+
+ data[0] = reg;
+ data[1] = val;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].buf = data;
+ msg[0].len = ARRAY_SIZE(data);
+
+ mutex_lock(&bq2415x_i2c_mutex);
+ ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+ mutex_unlock(&bq2415x_i2c_mutex);
+
+ /* i2c_transfer returns number of messages transferred */
+ if (ret < 0)
+ return ret;
+ else if (ret != 1)
+ return -EIO;
+
+ return 0;
+}
+
+/* read value from register, change it with mask left shifted and write back */
+static int bq2415x_i2c_write_mask(struct bq2415x_device *bq, u8 reg, u8 val,
+ u8 mask, u8 shift)
+{
+ int ret;
+
+ if (shift > 8)
+ return -EINVAL;
+
+ ret = bq2415x_i2c_read(bq, reg);
+ if (ret < 0)
+ return ret;
+
+ ret &= ~mask;
+ ret |= val << shift;
+
+ return bq2415x_i2c_write(bq, reg, ret);
+}
+
+/* change only one bit in register */
+static int bq2415x_i2c_write_bit(struct bq2415x_device *bq, u8 reg,
+ bool val, u8 bit)
+{
+ if (bit > 8)
+ return -EINVAL;
+ return bq2415x_i2c_write_mask(bq, reg, val, BIT(bit), bit);
+}
+
+/**** global functions ****/
+
+/* exec command function */
+static int bq2415x_exec_command(struct bq2415x_device *bq,
+ enum bq2415x_command command)
+{
+ int ret;
+
+ switch (command) {
+ case BQ2415X_TIMER_RESET:
+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS,
+ 1, BQ2415X_BIT_TMR_RST);
+ case BQ2415X_OTG_STATUS:
+ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS,
+ BQ2415X_BIT_OTG);
+ case BQ2415X_STAT_PIN_STATUS:
+ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS,
+ BQ2415X_BIT_EN_STAT);
+ case BQ2415X_STAT_PIN_ENABLE:
+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, 1,
+ BQ2415X_BIT_EN_STAT);
+ case BQ2415X_STAT_PIN_DISABLE:
+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, 0,
+ BQ2415X_BIT_EN_STAT);
+ case BQ2415X_CHARGE_STATUS:
+ return bq2415x_i2c_read_mask(bq, BQ2415X_REG_STATUS,
+ BQ2415X_MASK_STAT, BQ2415X_SHIFT_STAT);
+ case BQ2415X_BOOST_STATUS:
+ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS,
+ BQ2415X_BIT_BOOST);
+ case BQ2415X_FAULT_STATUS:
+ return bq2415x_i2c_read_mask(bq, BQ2415X_REG_STATUS,
+ BQ2415X_MASK_FAULT, BQ2415X_SHIFT_FAULT);
+
+ case BQ2415X_CHARGE_TERMINATION_STATUS:
+ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
+ BQ2415X_BIT_TE);
+ case BQ2415X_CHARGE_TERMINATION_ENABLE:
+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
+ 1, BQ2415X_BIT_TE);
+ case BQ2415X_CHARGE_TERMINATION_DISABLE:
+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
+ 0, BQ2415X_BIT_TE);
+ case BQ2415X_CHARGER_STATUS:
+ ret = bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
+ BQ2415X_BIT_CE);
+ if (ret < 0)
+ return ret;
+ else
+ return ret > 0 ? 0 : 1;
+ case BQ2415X_CHARGER_ENABLE:
+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
+ 0, BQ2415X_BIT_CE);
+ case BQ2415X_CHARGER_DISABLE:
+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
+ 1, BQ2415X_BIT_CE);
+ case BQ2415X_HIGH_IMPEDANCE_STATUS:
+ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
+ BQ2415X_BIT_HZ_MODE);
+ case BQ2415X_HIGH_IMPEDANCE_ENABLE:
+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
+ 1, BQ2415X_BIT_HZ_MODE);
+ case BQ2415X_HIGH_IMPEDANCE_DISABLE:
+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
+ 0, BQ2415X_BIT_HZ_MODE);
+ case BQ2415X_BOOST_MODE_STATUS:
+ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
+ BQ2415X_BIT_OPA_MODE);
+ case BQ2415X_BOOST_MODE_ENABLE:
+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
+ 1, BQ2415X_BIT_OPA_MODE);
+ case BQ2415X_BOOST_MODE_DISABLE:
+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
+ 0, BQ2415X_BIT_OPA_MODE);
+
+ case BQ2415X_OTG_LEVEL:
+ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_VOLTAGE,
+ BQ2415X_BIT_OTG_PL);
+ case BQ2415X_OTG_ACTIVATE_HIGH:
+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
+ 1, BQ2415X_BIT_OTG_PL);
+ case BQ2415X_OTG_ACTIVATE_LOW:
+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
+ 0, BQ2415X_BIT_OTG_PL);
+ case BQ2415X_OTG_PIN_STATUS:
+ return bq2415x_i2c_read_bit(bq, BQ2415X_REG_VOLTAGE,
+ BQ2415X_BIT_OTG_EN);
+ case BQ2415X_OTG_PIN_ENABLE:
+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
+ 1, BQ2415X_BIT_OTG_EN);
+ case BQ2415X_OTG_PIN_DISABLE:
+ return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
+ 0, BQ2415X_BIT_OTG_EN);
+
+ case BQ2415X_VENDER_CODE:
+ return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER,
+ BQ2415X_MASK_VENDER, BQ2415X_SHIFT_VENDER);
+ case BQ2415X_PART_NUMBER:
+ return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER,
+ BQ2415X_MASK_PN, BQ2415X_SHIFT_PN);
+ case BQ2415X_REVISION:
+ return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER,
+ BQ2415X_MASK_REVISION, BQ2415X_SHIFT_REVISION);
+ }
+ return -EINVAL;
+}
+
+/* detect chip type */
+static enum bq2415x_chip bq2415x_detect_chip(struct bq2415x_device *bq)
+{
+ struct i2c_client *client = to_i2c_client(bq->dev);
+ int ret = bq2415x_exec_command(bq, BQ2415X_PART_NUMBER);
+
+ if (ret < 0)
+ return ret;
+
+ switch (client->addr) {
+ case 0x6b:
+ switch (ret) {
+ case 0:
+ if (bq->chip == BQ24151A)
+ return bq->chip;
+ else
+ return BQ24151;
+ case 1:
+ if (bq->chip == BQ24150A ||
+ bq->chip == BQ24152 ||
+ bq->chip == BQ24155)
+ return bq->chip;
+ else
+ return BQ24150;
+ case 2:
+ if (bq->chip == BQ24153A)
+ return bq->chip;
+ else
+ return BQ24153;
+ default:
+ return BQUNKNOWN;
+ }
+ break;
+
+ case 0x6a:
+ switch (ret) {
+ case 0:
+ if (bq->chip == BQ24156A)
+ return bq->chip;
+ else
+ return BQ24156;
+ case 2:
+ return BQ24158;
+ default:
+ return BQUNKNOWN;
+ }
+ break;
+ }
+
+ return BQUNKNOWN;
+}
+
+/* detect chip revision */
+static int bq2415x_detect_revision(struct bq2415x_device *bq)
+{
+ int ret = bq2415x_exec_command(bq, BQ2415X_REVISION);
+ int chip = bq2415x_detect_chip(bq);
+
+ if (ret < 0 || chip < 0)
+ return -1;
+
+ switch (chip) {
+ case BQ24150:
+ case BQ24150A:
+ case BQ24151:
+ case BQ24151A:
+ case BQ24152:
+ if (ret >= 0 && ret <= 3)
+ return ret;
+ else
+ return -1;
+ case BQ24153:
+ case BQ24153A:
+ case BQ24156:
+ case BQ24156A:
+ case BQ24158:
+ if (ret == 3)
+ return 0;
+ else if (ret == 1)
+ return 1;
+ else
+ return -1;
+ case BQ24155:
+ if (ret == 3)
+ return 3;
+ else
+ return -1;
+ case BQUNKNOWN:
+ return -1;
+ }
+
+ return -1;
+}
+
+/* return chip vender code */
+static int bq2415x_get_vender_code(struct bq2415x_device *bq)
+{
+ int ret;
+
+ ret = bq2415x_exec_command(bq, BQ2415X_VENDER_CODE);
+ if (ret < 0)
+ return 0;
+
+ /* convert to binary */
+ return (ret & 0x1) +
+ ((ret >> 1) & 0x1) * 10 +
+ ((ret >> 2) & 0x1) * 100;
+}
+
+/* reset all chip registers to default state */
+static void bq2415x_reset_chip(struct bq2415x_device *bq)
+{
+ bq2415x_i2c_write(bq, BQ2415X_REG_CURRENT, BQ2415X_RESET_CURRENT);
+ bq2415x_i2c_write(bq, BQ2415X_REG_VOLTAGE, BQ2415X_RESET_VOLTAGE);
+ bq2415x_i2c_write(bq, BQ2415X_REG_CONTROL, BQ2415X_RESET_CONTROL);
+ bq2415x_i2c_write(bq, BQ2415X_REG_STATUS, BQ2415X_RESET_STATUS);
+ bq->timer_error = NULL;
+}
+
+/**** properties functions ****/
+
+/* set current limit in mA */
+static int bq2415x_set_current_limit(struct bq2415x_device *bq, int mA)
+{
+ int val;
+
+ if (mA <= 100)
+ val = 0;
+ else if (mA <= 500)
+ val = 1;
+ else if (mA <= 800)
+ val = 2;
+ else
+ val = 3;
+
+ return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CONTROL, val,
+ BQ2415X_MASK_LIMIT, BQ2415X_SHIFT_LIMIT);
+}
+
+/* get current limit in mA */
+static int bq2415x_get_current_limit(struct bq2415x_device *bq)
+{
+ int ret;
+
+ ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL,
+ BQ2415X_MASK_LIMIT, BQ2415X_SHIFT_LIMIT);
+ if (ret < 0)
+ return ret;
+ else if (ret == 0)
+ return 100;
+ else if (ret == 1)
+ return 500;
+ else if (ret == 2)
+ return 800;
+ else if (ret == 3)
+ return 1800;
+ return -EINVAL;
+}
+
+/* set weak battery voltage in mV */
+static int bq2415x_set_weak_battery_voltage(struct bq2415x_device *bq, int mV)
+{
+ int val;
+
+ /* round to 100mV */
+ if (mV <= 3400 + 50)
+ val = 0;
+ else if (mV <= 3500 + 50)
+ val = 1;
+ else if (mV <= 3600 + 50)
+ val = 2;
+ else
+ val = 3;
+
+ return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CONTROL, val,
+ BQ2415X_MASK_VLOWV, BQ2415X_SHIFT_VLOWV);
+}
+
+/* get weak battery voltage in mV */
+static int bq2415x_get_weak_battery_voltage(struct bq2415x_device *bq)
+{
+ int ret;
+
+ ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL,
+ BQ2415X_MASK_VLOWV, BQ2415X_SHIFT_VLOWV);
+ if (ret < 0)
+ return ret;
+ return 100 * (34 + ret);
+}
+
+/* set battery regulation voltage in mV */
+static int bq2415x_set_battery_regulation_voltage(struct bq2415x_device *bq,
+ int mV)
+{
+ int val = (mV/10 - 350) / 2;
+
+ if (val < 0)
+ val = 0;
+ else if (val > 94) /* FIXME: Max is 94 or 122 ? Set max value ? */
+ return -EINVAL;
+
+ return bq2415x_i2c_write_mask(bq, BQ2415X_REG_VOLTAGE, val,
+ BQ2415X_MASK_VO, BQ2415X_SHIFT_VO);
+}
+
+/* get battery regulation voltage in mV */
+static int bq2415x_get_battery_regulation_voltage(struct bq2415x_device *bq)
+{
+ int ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_VOLTAGE,
+ BQ2415X_MASK_VO, BQ2415X_SHIFT_VO);
+
+ if (ret < 0)
+ return ret;
+ return 10 * (350 + 2*ret);
+}
+
+/* set charge current in mA (platform data must provide resistor sense) */
+static int bq2415x_set_charge_current(struct bq2415x_device *bq, int mA)
+{
+ int val;
+
+ if (bq->init_data.resistor_sense <= 0)
+ return -ENOSYS;
+
+ val = (mA * bq->init_data.resistor_sense - 37400) / 6800;
+ if (val < 0)
+ val = 0;
+ else if (val > 7)
+ val = 7;
+
+ return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CURRENT, val,
+ BQ2415X_MASK_VI_CHRG | BQ2415X_MASK_RESET,
+ BQ2415X_SHIFT_VI_CHRG);
+}
+
+/* get charge current in mA (platform data must provide resistor sense) */
+static int bq2415x_get_charge_current(struct bq2415x_device *bq)
+{
+ int ret;
+
+ if (bq->init_data.resistor_sense <= 0)
+ return -ENOSYS;
+
+ ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT,
+ BQ2415X_MASK_VI_CHRG, BQ2415X_SHIFT_VI_CHRG);
+ if (ret < 0)
+ return ret;
+ return (37400 + 6800*ret) / bq->init_data.resistor_sense;
+}
+
+/* set termination current in mA (platform data must provide resistor sense) */
+static int bq2415x_set_termination_current(struct bq2415x_device *bq, int mA)
+{
+ int val;
+
+ if (bq->init_data.resistor_sense <= 0)
+ return -ENOSYS;
+
+ val = (mA * bq->init_data.resistor_sense - 3400) / 3400;
+ if (val < 0)
+ val = 0;
+ else if (val > 7)
+ val = 7;
+
+ return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CURRENT, val,
+ BQ2415X_MASK_VI_TERM | BQ2415X_MASK_RESET,
+ BQ2415X_SHIFT_VI_TERM);
+}
+
+/* get termination current in mA (platform data must provide resistor sense) */
+static int bq2415x_get_termination_current(struct bq2415x_device *bq)
+{
+ int ret;
+
+ if (bq->init_data.resistor_sense <= 0)
+ return -ENOSYS;
+
+ ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT,
+ BQ2415X_MASK_VI_TERM, BQ2415X_SHIFT_VI_TERM);
+ if (ret < 0)
+ return ret;
+ return (3400 + 3400*ret) / bq->init_data.resistor_sense;
+}
+
+/* set default value of property */
+#define bq2415x_set_default_value(bq, prop) \
+ do { \
+ int ret = 0; \
+ if (bq->init_data.prop != -1) \
+ ret = bq2415x_set_##prop(bq, bq->init_data.prop); \
+ if (ret < 0) \
+ return ret; \
+ } while (0)
+
+/* set default values of all properties */
+static int bq2415x_set_defaults(struct bq2415x_device *bq)
+{
+ bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_DISABLE);
+ bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE);
+ bq2415x_exec_command(bq, BQ2415X_CHARGE_TERMINATION_DISABLE);
+
+ bq2415x_set_default_value(bq, current_limit);
+ bq2415x_set_default_value(bq, weak_battery_voltage);
+ bq2415x_set_default_value(bq, battery_regulation_voltage);
+
+ if (bq->init_data.resistor_sense > 0) {
+ bq2415x_set_default_value(bq, charge_current);
+ bq2415x_set_default_value(bq, termination_current);
+ bq2415x_exec_command(bq, BQ2415X_CHARGE_TERMINATION_ENABLE);
+ }
+
+ bq2415x_exec_command(bq, BQ2415X_CHARGER_ENABLE);
+ return 0;
+}
+
+/**** charger mode functions ****/
+
+/* set charger mode */
+static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode)
+{
+ int ret = 0;
+ int charger = 0;
+ int boost = 0;
+
+ if (mode == BQ2415X_MODE_HOST_CHARGER ||
+ mode == BQ2415X_MODE_DEDICATED_CHARGER)
+ charger = 1;
+
+ if (mode == BQ2415X_MODE_BOOST)
+ boost = 1;
+
+ if (!charger)
+ ret = bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE);
+
+ if (!boost)
+ ret = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_DISABLE);
+
+ if (ret < 0)
+ return ret;
+
+ switch (mode) {
+ case BQ2415X_MODE_NONE:
+ dev_dbg(bq->dev, "changing mode to: N/A\n");
+ ret = bq2415x_set_current_limit(bq, 100);
+ break;
+ case BQ2415X_MODE_HOST_CHARGER:
+ dev_dbg(bq->dev, "changing mode to: Host/HUB charger\n");
+ ret = bq2415x_set_current_limit(bq, 500);
+ break;
+ case BQ2415X_MODE_DEDICATED_CHARGER:
+ dev_dbg(bq->dev, "changing mode to: Dedicated charger\n");
+ ret = bq2415x_set_current_limit(bq, 1800);
+ break;
+ case BQ2415X_MODE_BOOST: /* Boost mode */
+ dev_dbg(bq->dev, "changing mode to: Boost\n");
+ ret = bq2415x_set_current_limit(bq, 100);
+ break;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ if (charger)
+ ret = bq2415x_exec_command(bq, BQ2415X_CHARGER_ENABLE);
+ else if (boost)
+ ret = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_ENABLE);
+
+ if (ret < 0)
+ return ret;
+
+ bq2415x_set_default_value(bq, weak_battery_voltage);
+ bq2415x_set_default_value(bq, battery_regulation_voltage);
+
+ bq->mode = mode;
+ sysfs_notify(&bq->charger.dev->kobj, NULL, "mode");
+
+ return 0;
+
+}
+
+/* hook function called by other driver which set reported mode */
+static void bq2415x_hook_function(enum bq2415x_mode mode, void *data)
+{
+ struct bq2415x_device *bq = data;
+
+ if (!bq)
+ return;
+
+ dev_dbg(bq->dev, "hook function was called\n");
+ bq->reported_mode = mode;
+
+ /* if automode is not enabled do not tell about reported_mode */
+ if (bq->automode < 1)
+ return;
+
+ sysfs_notify(&bq->charger.dev->kobj, NULL, "reported_mode");
+ bq2415x_set_mode(bq, bq->reported_mode);
+
+}
+
+/**** timer functions ****/
+
+/* enable/disable auto resetting chip timer */
+static void bq2415x_set_autotimer(struct bq2415x_device *bq, int state)
+{
+ mutex_lock(&bq2415x_timer_mutex);
+
+ if (bq->autotimer == state) {
+ mutex_unlock(&bq2415x_timer_mutex);
+ return;
+ }
+
+ bq->autotimer = state;
+
+ if (state) {
+ schedule_delayed_work(&bq->work, BQ2415X_TIMER_TIMEOUT * HZ);
+ bq2415x_exec_command(bq, BQ2415X_TIMER_RESET);
+ bq->timer_error = NULL;
+ } else {
+ cancel_delayed_work_sync(&bq->work);
+ }
+
+ mutex_unlock(&bq2415x_timer_mutex);
+}
+
+/* called by bq2415x_timer_work on timer error */
+static void bq2415x_timer_error(struct bq2415x_device *bq, const char *msg)
+{
+ bq->timer_error = msg;
+ sysfs_notify(&bq->charger.dev->kobj, NULL, "timer");
+ dev_err(bq->dev, "%s\n", msg);
+ if (bq->automode > 0)
+ bq->automode = 0;
+ bq2415x_set_mode(bq, BQ2415X_MODE_NONE);
+ bq2415x_set_autotimer(bq, 0);
+}
+
+/* delayed work function for auto resetting chip timer */
+static void bq2415x_timer_work(struct work_struct *work)
+{
+ struct bq2415x_device *bq = container_of(work, struct bq2415x_device,
+ work.work);
+ int ret;
+ int error;
+ int boost;
+
+ if (!bq->autotimer)
+ return;
+
+ ret = bq2415x_exec_command(bq, BQ2415X_TIMER_RESET);
+ if (ret < 0) {
+ bq2415x_timer_error(bq, "Resetting timer failed");
+ return;
+ }
+
+ boost = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_STATUS);
+ if (boost < 0) {
+ bq2415x_timer_error(bq, "Unknown error");
+ return;
+ }
+
+ error = bq2415x_exec_command(bq, BQ2415X_FAULT_STATUS);
+ if (error < 0) {
+ bq2415x_timer_error(bq, "Unknown error");
+ return;
+ }
+
+ if (boost) {
+ switch (error) {
+ /* Non fatal errors, chip is OK */
+ case 0: /* No error */
+ break;
+ case 6: /* Timer expired */
+ dev_err(bq->dev, "Timer expired\n");
+ break;
+ case 3: /* Battery voltage too low */
+ dev_err(bq->dev, "Battery voltage to low\n");
+ break;
+
+ /* Fatal errors, disable and reset chip */
+ case 1: /* Overvoltage protection (chip fried) */
+ bq2415x_timer_error(bq,
+ "Overvoltage protection (chip fried)");
+ return;
+ case 2: /* Overload */
+ bq2415x_timer_error(bq, "Overload");
+ return;
+ case 4: /* Battery overvoltage protection */
+ bq2415x_timer_error(bq,
+ "Battery overvoltage protection");
+ return;
+ case 5: /* Thermal shutdown (too hot) */
+ bq2415x_timer_error(bq,
+ "Thermal shutdown (too hot)");
+ return;
+ case 7: /* N/A */
+ bq2415x_timer_error(bq, "Unknown error");
+ return;
+ }
+ } else {
+ switch (error) {
+ /* Non fatal errors, chip is OK */
+ case 0: /* No error */
+ break;
+ case 2: /* Sleep mode */
+ dev_err(bq->dev, "Sleep mode\n");
+ break;
+ case 3: /* Poor input source */
+ dev_err(bq->dev, "Poor input source\n");
+ break;
+ case 6: /* Timer expired */
+ dev_err(bq->dev, "Timer expired\n");
+ break;
+ case 7: /* No battery */
+ dev_err(bq->dev, "No battery\n");
+ break;
+
+ /* Fatal errors, disable and reset chip */
+ case 1: /* Overvoltage protection (chip fried) */
+ bq2415x_timer_error(bq,
+ "Overvoltage protection (chip fried)");
+ return;
+ case 4: /* Battery overvoltage protection */
+ bq2415x_timer_error(bq,
+ "Battery overvoltage protection");
+ return;
+ case 5: /* Thermal shutdown (too hot) */
+ bq2415x_timer_error(bq,
+ "Thermal shutdown (too hot)");
+ return;
+ }
+ }
+
+ schedule_delayed_work(&bq->work, BQ2415X_TIMER_TIMEOUT * HZ);
+}
+
+/**** power supply interface code ****/
+
+static enum power_supply_property bq2415x_power_supply_props[] = {
+ /* TODO: maybe add more power supply properties */
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+};
+
+static int bq2415x_power_supply_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
+ charger);
+ int ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ ret = bq2415x_exec_command(bq, BQ2415X_CHARGE_STATUS);
+ if (ret < 0)
+ return ret;
+ else if (ret == 0) /* Ready */
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ else if (ret == 1) /* Charge in progress */
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ else if (ret == 2) /* Charge done */
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ else
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = bq->model;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int bq2415x_power_supply_init(struct bq2415x_device *bq)
+{
+ int ret;
+ int chip;
+ char revstr[8];
+
+ bq->charger.name = bq->name;
+ bq->charger.type = POWER_SUPPLY_TYPE_USB;
+ bq->charger.properties = bq2415x_power_supply_props;
+ bq->charger.num_properties = ARRAY_SIZE(bq2415x_power_supply_props);
+ bq->charger.get_property = bq2415x_power_supply_get_property;
+
+ ret = bq2415x_detect_chip(bq);
+ if (ret < 0)
+ chip = BQUNKNOWN;
+ else
+ chip = ret;
+
+ ret = bq2415x_detect_revision(bq);
+ if (ret < 0)
+ strcpy(revstr, "unknown");
+ else
+ sprintf(revstr, "1.%d", ret);
+
+ bq->model = kasprintf(GFP_KERNEL,
+ "chip %s, revision %s, vender code %.3d",
+ bq2415x_chip_name[chip], revstr,
+ bq2415x_get_vender_code(bq));
+ if (!bq->model) {
+ dev_err(bq->dev, "failed to allocate model name\n");
+ return -ENOMEM;
+ }
+
+ ret = power_supply_register(bq->dev, &bq->charger);
+ if (ret) {
+ kfree(bq->model);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void bq2415x_power_supply_exit(struct bq2415x_device *bq)
+{
+ bq->autotimer = 0;
+ if (bq->automode > 0)
+ bq->automode = 0;
+ cancel_delayed_work_sync(&bq->work);
+ power_supply_unregister(&bq->charger);
+ kfree(bq->model);
+}
+
+/**** additional sysfs entries for power supply interface ****/
+
+/* show *_status entries */
+static ssize_t bq2415x_sysfs_show_status(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
+ charger);
+ enum bq2415x_command command;
+ int ret;
+
+ if (strcmp(attr->attr.name, "otg_status") == 0)
+ command = BQ2415X_OTG_STATUS;
+ else if (strcmp(attr->attr.name, "charge_status") == 0)
+ command = BQ2415X_CHARGE_STATUS;
+ else if (strcmp(attr->attr.name, "boost_status") == 0)
+ command = BQ2415X_BOOST_STATUS;
+ else if (strcmp(attr->attr.name, "fault_status") == 0)
+ command = BQ2415X_FAULT_STATUS;
+ else
+ return -EINVAL;
+
+ ret = bq2415x_exec_command(bq, command);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%d\n", ret);
+}
+
+/*
+ * set timer entry:
+ * auto - enable auto mode
+ * off - disable auto mode
+ * (other values) - reset chip timer
+ */
+static ssize_t bq2415x_sysfs_set_timer(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
+ charger);
+ int ret = 0;
+
+ if (strncmp(buf, "auto", 4) == 0)
+ bq2415x_set_autotimer(bq, 1);
+ else if (strncmp(buf, "off", 3) == 0)
+ bq2415x_set_autotimer(bq, 0);
+ else
+ ret = bq2415x_exec_command(bq, BQ2415X_TIMER_RESET);
+
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+/* show timer entry (auto or off) */
+static ssize_t bq2415x_sysfs_show_timer(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
+ charger);
+
+ if (bq->timer_error)
+ return sprintf(buf, "%s\n", bq->timer_error);
+
+ if (bq->autotimer)
+ return sprintf(buf, "auto\n");
+ return sprintf(buf, "off\n");
+}
+
+/*
+ * set mode entry:
+ * auto - if automode is supported, enable it and set mode to reported
+ * none - disable charger and boost mode
+ * host - charging mode for host/hub chargers (current limit 500mA)
+ * dedicated - charging mode for dedicated chargers (unlimited current limit)
+ * boost - disable charger and enable boost mode
+ */
+static ssize_t bq2415x_sysfs_set_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
+ charger);
+ enum bq2415x_mode mode;
+ int ret = 0;
+
+ if (strncmp(buf, "auto", 4) == 0) {
+ if (bq->automode < 0)
+ return -ENOSYS;
+ bq->automode = 1;
+ mode = bq->reported_mode;
+ } else if (strncmp(buf, "none", 4) == 0) {
+ if (bq->automode > 0)
+ bq->automode = 0;
+ mode = BQ2415X_MODE_NONE;
+ } else if (strncmp(buf, "host", 4) == 0) {
+ if (bq->automode > 0)
+ bq->automode = 0;
+ mode = BQ2415X_MODE_HOST_CHARGER;
+ } else if (strncmp(buf, "dedicated", 9) == 0) {
+ if (bq->automode > 0)
+ bq->automode = 0;
+ mode = BQ2415X_MODE_DEDICATED_CHARGER;
+ } else if (strncmp(buf, "boost", 5) == 0) {
+ if (bq->automode > 0)
+ bq->automode = 0;
+ mode = BQ2415X_MODE_BOOST;
+ } else if (strncmp(buf, "reset", 5) == 0) {
+ bq2415x_reset_chip(bq);
+ bq2415x_set_defaults(bq);
+ if (bq->automode <= 0)
+ return count;
+ bq->automode = 1;
+ mode = bq->reported_mode;
+ } else {
+ return -EINVAL;
+ }
+
+ ret = bq2415x_set_mode(bq, mode);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+/* show mode entry (auto, none, host, dedicated or boost) */
+static ssize_t bq2415x_sysfs_show_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
+ charger);
+ ssize_t ret = 0;
+
+ if (bq->automode > 0)
+ ret += sprintf(buf+ret, "auto (");
+
+ switch (bq->mode) {
+ case BQ2415X_MODE_NONE:
+ ret += sprintf(buf+ret, "none");
+ break;
+ case BQ2415X_MODE_HOST_CHARGER:
+ ret += sprintf(buf+ret, "host");
+ break;
+ case BQ2415X_MODE_DEDICATED_CHARGER:
+ ret += sprintf(buf+ret, "dedicated");
+ break;
+ case BQ2415X_MODE_BOOST:
+ ret += sprintf(buf+ret, "boost");
+ break;
+ }
+
+ if (bq->automode > 0)
+ ret += sprintf(buf+ret, ")");
+
+ ret += sprintf(buf+ret, "\n");
+ return ret;
+}
+
+/* show reported_mode entry (none, host, dedicated or boost) */
+static ssize_t bq2415x_sysfs_show_reported_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
+ charger);
+
+ if (bq->automode < 0)
+ return -EINVAL;
+
+ switch (bq->reported_mode) {
+ case BQ2415X_MODE_NONE:
+ return sprintf(buf, "none\n");
+ case BQ2415X_MODE_HOST_CHARGER:
+ return sprintf(buf, "host\n");
+ case BQ2415X_MODE_DEDICATED_CHARGER:
+ return sprintf(buf, "dedicated\n");
+ case BQ2415X_MODE_BOOST:
+ return sprintf(buf, "boost\n");
+ }
+
+ return -EINVAL;
+}
+
+/* directly set raw value to chip register, format: 'register value' */
+static ssize_t bq2415x_sysfs_set_registers(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
+ charger);
+ ssize_t ret = 0;
+ unsigned int reg;
+ unsigned int val;
+
+ if (sscanf(buf, "%x %x", &reg, &val) != 2)
+ return -EINVAL;
+
+ if (reg > 4 || val > 255)
+ return -EINVAL;
+
+ ret = bq2415x_i2c_write(bq, reg, val);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+/* print value of chip register, format: 'register=value' */
+static ssize_t bq2415x_sysfs_print_reg(struct bq2415x_device *bq,
+ u8 reg,
+ char *buf)
+{
+ int ret = bq2415x_i2c_read(bq, reg);
+
+ if (ret < 0)
+ return sprintf(buf, "%#.2x=error %d\n", reg, ret);
+ return sprintf(buf, "%#.2x=%#.2x\n", reg, ret);
+}
+
+/* show all raw values of chip register, format per line: 'register=value' */
+static ssize_t bq2415x_sysfs_show_registers(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
+ charger);
+ ssize_t ret = 0;
+
+ ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_STATUS, buf+ret);
+ ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_CONTROL, buf+ret);
+ ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_VOLTAGE, buf+ret);
+ ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_VENDER, buf+ret);
+ ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_CURRENT, buf+ret);
+ return ret;
+}
+
+/* set current and voltage limit entries (in mA or mV) */
+static ssize_t bq2415x_sysfs_set_limit(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
+ charger);
+ long val;
+ int ret;
+
+ if (kstrtol(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ if (strcmp(attr->attr.name, "current_limit") == 0)
+ ret = bq2415x_set_current_limit(bq, val);
+ else if (strcmp(attr->attr.name, "weak_battery_voltage") == 0)
+ ret = bq2415x_set_weak_battery_voltage(bq, val);
+ else if (strcmp(attr->attr.name, "battery_regulation_voltage") == 0)
+ ret = bq2415x_set_battery_regulation_voltage(bq, val);
+ else if (strcmp(attr->attr.name, "charge_current") == 0)
+ ret = bq2415x_set_charge_current(bq, val);
+ else if (strcmp(attr->attr.name, "termination_current") == 0)
+ ret = bq2415x_set_termination_current(bq, val);
+ else
+ return -EINVAL;
+
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+/* show current and voltage limit entries (in mA or mV) */
+static ssize_t bq2415x_sysfs_show_limit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
+ charger);
+ int ret;
+
+ if (strcmp(attr->attr.name, "current_limit") == 0)
+ ret = bq2415x_get_current_limit(bq);
+ else if (strcmp(attr->attr.name, "weak_battery_voltage") == 0)
+ ret = bq2415x_get_weak_battery_voltage(bq);
+ else if (strcmp(attr->attr.name, "battery_regulation_voltage") == 0)
+ ret = bq2415x_get_battery_regulation_voltage(bq);
+ else if (strcmp(attr->attr.name, "charge_current") == 0)
+ ret = bq2415x_get_charge_current(bq);
+ else if (strcmp(attr->attr.name, "termination_current") == 0)
+ ret = bq2415x_get_termination_current(bq);
+ else
+ return -EINVAL;
+
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%d\n", ret);
+}
+
+/* set *_enable entries */
+static ssize_t bq2415x_sysfs_set_enable(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
+ charger);
+ enum bq2415x_command command;
+ long val;
+ int ret;
+
+ if (kstrtol(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ if (strcmp(attr->attr.name, "charge_termination_enable") == 0)
+ command = val ? BQ2415X_CHARGE_TERMINATION_ENABLE :
+ BQ2415X_CHARGE_TERMINATION_DISABLE;
+ else if (strcmp(attr->attr.name, "high_impedance_enable") == 0)
+ command = val ? BQ2415X_HIGH_IMPEDANCE_ENABLE :
+ BQ2415X_HIGH_IMPEDANCE_DISABLE;
+ else if (strcmp(attr->attr.name, "otg_pin_enable") == 0)
+ command = val ? BQ2415X_OTG_PIN_ENABLE :
+ BQ2415X_OTG_PIN_DISABLE;
+ else if (strcmp(attr->attr.name, "stat_pin_enable") == 0)
+ command = val ? BQ2415X_STAT_PIN_ENABLE :
+ BQ2415X_STAT_PIN_DISABLE;
+ else
+ return -EINVAL;
+
+ ret = bq2415x_exec_command(bq, command);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+/* show *_enable entries */
+static ssize_t bq2415x_sysfs_show_enable(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
+ charger);
+ enum bq2415x_command command;
+ int ret;
+
+ if (strcmp(attr->attr.name, "charge_termination_enable") == 0)
+ command = BQ2415X_CHARGE_TERMINATION_STATUS;
+ else if (strcmp(attr->attr.name, "high_impedance_enable") == 0)
+ command = BQ2415X_HIGH_IMPEDANCE_STATUS;
+ else if (strcmp(attr->attr.name, "otg_pin_enable") == 0)
+ command = BQ2415X_OTG_PIN_STATUS;
+ else if (strcmp(attr->attr.name, "stat_pin_enable") == 0)
+ command = BQ2415X_STAT_PIN_STATUS;
+ else
+ return -EINVAL;
+
+ ret = bq2415x_exec_command(bq, command);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%d\n", ret);
+}
+
+static DEVICE_ATTR(current_limit, S_IWUSR | S_IRUGO,
+ bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
+static DEVICE_ATTR(weak_battery_voltage, S_IWUSR | S_IRUGO,
+ bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
+static DEVICE_ATTR(battery_regulation_voltage, S_IWUSR | S_IRUGO,
+ bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
+static DEVICE_ATTR(charge_current, S_IWUSR | S_IRUGO,
+ bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
+static DEVICE_ATTR(termination_current, S_IWUSR | S_IRUGO,
+ bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
+
+static DEVICE_ATTR(charge_termination_enable, S_IWUSR | S_IRUGO,
+ bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
+static DEVICE_ATTR(high_impedance_enable, S_IWUSR | S_IRUGO,
+ bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
+static DEVICE_ATTR(otg_pin_enable, S_IWUSR | S_IRUGO,
+ bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
+static DEVICE_ATTR(stat_pin_enable, S_IWUSR | S_IRUGO,
+ bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
+
+static DEVICE_ATTR(reported_mode, S_IRUGO,
+ bq2415x_sysfs_show_reported_mode, NULL);
+static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO,
+ bq2415x_sysfs_show_mode, bq2415x_sysfs_set_mode);
+static DEVICE_ATTR(timer, S_IWUSR | S_IRUGO,
+ bq2415x_sysfs_show_timer, bq2415x_sysfs_set_timer);
+
+static DEVICE_ATTR(registers, S_IWUSR | S_IRUGO,
+ bq2415x_sysfs_show_registers, bq2415x_sysfs_set_registers);
+
+static DEVICE_ATTR(otg_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
+static DEVICE_ATTR(charge_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
+static DEVICE_ATTR(boost_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
+static DEVICE_ATTR(fault_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
+
+static struct attribute *bq2415x_sysfs_attributes[] = {
+ /*
+ * TODO: some (appropriate) of these attrs should be switched to
+ * use power supply class props.
+ */
+ &dev_attr_current_limit.attr,
+ &dev_attr_weak_battery_voltage.attr,
+ &dev_attr_battery_regulation_voltage.attr,
+ &dev_attr_charge_current.attr,
+ &dev_attr_termination_current.attr,
+
+ &dev_attr_charge_termination_enable.attr,
+ &dev_attr_high_impedance_enable.attr,
+ &dev_attr_otg_pin_enable.attr,
+ &dev_attr_stat_pin_enable.attr,
+
+ &dev_attr_reported_mode.attr,
+ &dev_attr_mode.attr,
+ &dev_attr_timer.attr,
+
+ &dev_attr_registers.attr,
+
+ &dev_attr_otg_status.attr,
+ &dev_attr_charge_status.attr,
+ &dev_attr_boost_status.attr,
+ &dev_attr_fault_status.attr,
+ NULL,
+};
+
+static const struct attribute_group bq2415x_sysfs_attr_group = {
+ .attrs = bq2415x_sysfs_attributes,
+};
+
+static int bq2415x_sysfs_init(struct bq2415x_device *bq)
+{
+ return sysfs_create_group(&bq->charger.dev->kobj,
+ &bq2415x_sysfs_attr_group);
+}
+
+static void bq2415x_sysfs_exit(struct bq2415x_device *bq)
+{
+ sysfs_remove_group(&bq->charger.dev->kobj, &bq2415x_sysfs_attr_group);
+}
+
+/* main bq2415x probe function */
+static int bq2415x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ int num;
+ char *name;
+ struct bq2415x_device *bq;
+
+ if (!client->dev.platform_data) {
+ dev_err(&client->dev, "platform data not set\n");
+ return -ENODEV;
+ }
+
+ /* Get new ID for the new device */
+ ret = idr_pre_get(&bq2415x_id, GFP_KERNEL);
+ if (ret == 0)
+ return -ENOMEM;
+
+ mutex_lock(&bq2415x_id_mutex);
+ ret = idr_get_new(&bq2415x_id, client, &num);
+ mutex_unlock(&bq2415x_id_mutex);
+
+ if (ret < 0)
+ return ret;
+
+ name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num);
+ if (!name) {
+ dev_err(&client->dev, "failed to allocate device name\n");
+ ret = -ENOMEM;
+ goto error_1;
+ }
+
+ bq = kzalloc(sizeof(*bq), GFP_KERNEL);
+ if (!bq) {
+ dev_err(&client->dev, "failed to allocate device data\n");
+ ret = -ENOMEM;
+ goto error_2;
+ }
+
+ i2c_set_clientdata(client, bq);
+
+ bq->id = num;
+ bq->dev = &client->dev;
+ bq->chip = id->driver_data;
+ bq->name = name;
+ bq->mode = BQ2415X_MODE_NONE;
+ bq->reported_mode = BQ2415X_MODE_NONE;
+ bq->autotimer = 0;
+ bq->automode = 0;
+
+ memcpy(&bq->init_data, client->dev.platform_data,
+ sizeof(bq->init_data));
+
+ bq2415x_reset_chip(bq);
+
+ ret = bq2415x_power_supply_init(bq);
+ if (ret) {
+ dev_err(bq->dev, "failed to register power supply: %d\n", ret);
+ goto error_3;
+ }
+
+ ret = bq2415x_sysfs_init(bq);
+ if (ret) {
+ dev_err(bq->dev, "failed to create sysfs entries: %d\n", ret);
+ goto error_4;
+ }
+
+ ret = bq2415x_set_defaults(bq);
+ if (ret) {
+ dev_err(bq->dev, "failed to set default values: %d\n", ret);
+ goto error_5;
+ }
+
+ if (bq->init_data.set_mode_hook) {
+ if (bq->init_data.set_mode_hook(
+ bq2415x_hook_function, bq)) {
+ bq->automode = 1;
+ bq2415x_set_mode(bq, bq->reported_mode);
+ dev_info(bq->dev, "automode enabled\n");
+ } else {
+ bq->automode = -1;
+ dev_info(bq->dev, "automode failed\n");
+ }
+ } else {
+ bq->automode = -1;
+ dev_info(bq->dev, "automode not supported\n");
+ }
+
+ INIT_DELAYED_WORK(&bq->work, bq2415x_timer_work);
+ bq2415x_set_autotimer(bq, 1);
+
+ dev_info(bq->dev, "driver registered\n");
+ return 0;
+
+error_5:
+ bq2415x_sysfs_exit(bq);
+error_4:
+ bq2415x_power_supply_exit(bq);
+error_3:
+ kfree(bq);
+error_2:
+ kfree(name);
+error_1:
+ mutex_lock(&bq2415x_id_mutex);
+ idr_remove(&bq2415x_id, num);
+ mutex_unlock(&bq2415x_id_mutex);
+
+ return ret;
+}
+
+/* main bq2415x remove function */
+
+static int bq2415x_remove(struct i2c_client *client)
+{
+ struct bq2415x_device *bq = i2c_get_clientdata(client);
+
+ if (bq->init_data.set_mode_hook)
+ bq->init_data.set_mode_hook(NULL, NULL);
+
+ bq2415x_sysfs_exit(bq);
+ bq2415x_power_supply_exit(bq);
+
+ bq2415x_reset_chip(bq);
+
+ mutex_lock(&bq2415x_id_mutex);
+ idr_remove(&bq2415x_id, bq->id);
+ mutex_unlock(&bq2415x_id_mutex);
+
+ dev_info(bq->dev, "driver unregistered\n");
+
+ kfree(bq->name);
+ kfree(bq);
+
+ return 0;
+}
+
+static const struct i2c_device_id bq2415x_i2c_id_table[] = {
+ { "bq2415x", BQUNKNOWN },
+ { "bq24150", BQ24150 },
+ { "bq24150a", BQ24150A },
+ { "bq24151", BQ24151 },
+ { "bq24151a", BQ24151A },
+ { "bq24152", BQ24152 },
+ { "bq24153", BQ24153 },
+ { "bq24153a", BQ24153A },
+ { "bq24155", BQ24155 },
+ { "bq24156", BQ24156 },
+ { "bq24156a", BQ24156A },
+ { "bq24158", BQ24158 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, bq2415x_i2c_id_table);
+
+static struct i2c_driver bq2415x_driver = {
+ .driver = {
+ .name = "bq2415x-charger",
+ },
+ .probe = bq2415x_probe,
+ .remove = bq2415x_remove,
+ .id_table = bq2415x_i2c_id_table,
+};
+
+static int __init bq2415x_init(void)
+{
+ return i2c_add_driver(&bq2415x_driver);
+}
+module_init(bq2415x_init);
+
+static void __exit bq2415x_exit(void)
+{
+ i2c_del_driver(&bq2415x_driver);
+}
+module_exit(bq2415x_exit);
+
+MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
+MODULE_DESCRIPTION("bq2415x charger driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c
index e0edaf7de54..36b34efdafc 100644
--- a/drivers/power/bq27x00_battery.c
+++ b/drivers/power/bq27x00_battery.c
@@ -230,6 +230,14 @@ static int bq27x00_battery_read_charge(struct bq27x00_device_info *di, u8 reg)
*/
static inline int bq27x00_battery_read_nac(struct bq27x00_device_info *di)
{
+ int flags;
+ bool is_bq27500 = di->chip == BQ27500;
+ bool is_higher = bq27xxx_is_chip_version_higher(di);
+
+ flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500);
+ if (flags >= 0 && !is_higher && (flags & BQ27000_FLAG_CI))
+ return -ENODATA;
+
return bq27x00_battery_read_charge(di, BQ27x00_REG_NAC);
}
diff --git a/drivers/power/ds2782_battery.c b/drivers/power/ds2782_battery.c
index 6bb6e2f5ea8..2fa9b6bf1f3 100644
--- a/drivers/power/ds2782_battery.c
+++ b/drivers/power/ds2782_battery.c
@@ -80,13 +80,13 @@ static inline int ds278x_read_reg16(struct ds278x_info *info, int reg_msb,
{
int ret;
- ret = swab16(i2c_smbus_read_word_data(info->client, reg_msb));
+ ret = i2c_smbus_read_word_data(info->client, reg_msb);
if (ret < 0) {
dev_err(&info->client->dev, "register read failed\n");
return ret;
}
- *val = ret;
+ *val = swab16(ret);
return 0;
}
diff --git a/drivers/power/generic-adc-battery.c b/drivers/power/generic-adc-battery.c
index e902b088d52..32ce17e235c 100644
--- a/drivers/power/generic-adc-battery.c
+++ b/drivers/power/generic-adc-battery.c
@@ -279,7 +279,8 @@ static int gab_probe(struct platform_device *pdev)
}
memcpy(psy->properties, gab_props, sizeof(gab_props));
- properties = psy->properties + sizeof(gab_props);
+ properties = (enum power_supply_property *)
+ ((char *)psy->properties + sizeof(gab_props));
/*
* getting channel from iio and copying the battery properties
@@ -327,7 +328,7 @@ static int gab_probe(struct platform_device *pdev)
ret = request_any_context_irq(irq, gab_charged,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
"battery charged", adc_bat);
- if (ret)
+ if (ret < 0)
goto err_gpio;
}
diff --git a/drivers/power/jz4740-battery.c b/drivers/power/jz4740-battery.c
index 74ac69e0687..bf914893c6f 100644
--- a/drivers/power/jz4740-battery.c
+++ b/drivers/power/jz4740-battery.c
@@ -33,7 +33,6 @@ struct jz_battery {
struct jz_battery_platform_data *pdata;
struct platform_device *pdev;
- struct resource *mem;
void __iomem *base;
int irq;
@@ -244,13 +243,14 @@ static int jz_battery_probe(struct platform_device *pdev)
struct jz_battery_platform_data *pdata = pdev->dev.parent->platform_data;
struct jz_battery *jz_battery;
struct power_supply *battery;
+ struct resource *mem;
if (!pdata) {
dev_err(&pdev->dev, "No platform_data supplied\n");
return -ENXIO;
}
- jz_battery = kzalloc(sizeof(*jz_battery), GFP_KERNEL);
+ jz_battery = devm_kzalloc(&pdev->dev, sizeof(*jz_battery), GFP_KERNEL);
if (!jz_battery) {
dev_err(&pdev->dev, "Failed to allocate driver structure\n");
return -ENOMEM;
@@ -260,33 +260,15 @@ static int jz_battery_probe(struct platform_device *pdev)
jz_battery->irq = platform_get_irq(pdev, 0);
if (jz_battery->irq < 0) {
- ret = jz_battery->irq;
dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
- goto err_free;
+ return jz_battery->irq;
}
- jz_battery->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!jz_battery->mem) {
- ret = -ENOENT;
- dev_err(&pdev->dev, "Failed to get platform mmio resource\n");
- goto err_free;
- }
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- jz_battery->mem = request_mem_region(jz_battery->mem->start,
- resource_size(jz_battery->mem), pdev->name);
- if (!jz_battery->mem) {
- ret = -EBUSY;
- dev_err(&pdev->dev, "Failed to request mmio memory region\n");
- goto err_free;
- }
-
- jz_battery->base = ioremap_nocache(jz_battery->mem->start,
- resource_size(jz_battery->mem));
- if (!jz_battery->base) {
- ret = -EBUSY;
- dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
- goto err_release_mem_region;
- }
+ jz_battery->base = devm_request_and_ioremap(&pdev->dev, mem);
+ if (!jz_battery->base)
+ return -EBUSY;
battery = &jz_battery->battery;
battery->name = pdata->info.name;
@@ -309,7 +291,7 @@ static int jz_battery_probe(struct platform_device *pdev)
jz_battery);
if (ret) {
dev_err(&pdev->dev, "Failed to request irq %d\n", ret);
- goto err_iounmap;
+ goto err;
}
disable_irq(jz_battery->irq);
@@ -366,13 +348,8 @@ err_free_gpio:
gpio_free(jz_battery->pdata->gpio_charge);
err_free_irq:
free_irq(jz_battery->irq, jz_battery);
-err_iounmap:
+err:
platform_set_drvdata(pdev, NULL);
- iounmap(jz_battery->base);
-err_release_mem_region:
- release_mem_region(jz_battery->mem->start, resource_size(jz_battery->mem));
-err_free:
- kfree(jz_battery);
return ret;
}
@@ -392,10 +369,6 @@ static int jz_battery_remove(struct platform_device *pdev)
free_irq(jz_battery->irq, jz_battery);
- iounmap(jz_battery->base);
- release_mem_region(jz_battery->mem->start, resource_size(jz_battery->mem));
- kfree(jz_battery);
-
return 0;
}
diff --git a/drivers/power/lp8788-charger.c b/drivers/power/lp8788-charger.c
index a1c51ac117f..22b6407c9ca 100644
--- a/drivers/power/lp8788-charger.c
+++ b/drivers/power/lp8788-charger.c
@@ -235,25 +235,14 @@ static int lp8788_get_battery_present(struct lp8788_charger *pchg,
return 0;
}
-static int lp8788_get_vbatt_adc(struct lp8788_charger *pchg,
- unsigned int *result)
+static int lp8788_get_vbatt_adc(struct lp8788_charger *pchg, int *result)
{
struct iio_channel *channel = pchg->chan[LP8788_VBATT];
- int scaleint;
- int scalepart;
- int ret;
if (!channel)
return -EINVAL;
- ret = iio_read_channel_scale(channel, &scaleint, &scalepart);
- if (ret != IIO_VAL_INT_PLUS_MICRO)
- return -EINVAL;
-
- /* unit: mV */
- *result = (scaleint + scalepart * 1000000) / 1000;
-
- return 0;
+ return iio_read_channel_processed(channel, result);
}
static int lp8788_get_battery_voltage(struct lp8788_charger *pchg,
@@ -268,7 +257,7 @@ static int lp8788_get_battery_capacity(struct lp8788_charger *pchg,
struct lp8788 *lp = pchg->lp;
struct lp8788_charger_platform_data *pdata = pchg->pdata;
unsigned int max_vbatt;
- unsigned int vbatt;
+ int vbatt;
enum lp8788_charging_state state;
u8 data;
int ret;
@@ -304,19 +293,18 @@ static int lp8788_get_battery_temperature(struct lp8788_charger *pchg,
union power_supply_propval *val)
{
struct iio_channel *channel = pchg->chan[LP8788_BATT_TEMP];
- int scaleint;
- int scalepart;
+ int result;
int ret;
if (!channel)
return -EINVAL;
- ret = iio_read_channel_scale(channel, &scaleint, &scalepart);
- if (ret != IIO_VAL_INT_PLUS_MICRO)
+ ret = iio_read_channel_processed(channel, &result);
+ if (ret < 0)
return -EINVAL;
/* unit: 0.1 'C */
- val->intval = (scaleint + scalepart * 1000000) / 100;
+ val->intval = result * 10;
return 0;
}
@@ -592,53 +580,22 @@ static void lp8788_irq_unregister(struct platform_device *pdev,
}
}
-static void lp8788_setup_adc_channel(struct lp8788_charger *pchg)
+static void lp8788_setup_adc_channel(const char *consumer_name,
+ struct lp8788_charger *pchg)
{
struct lp8788_charger_platform_data *pdata = pchg->pdata;
- struct device *dev = pchg->lp->dev;
struct iio_channel *chan;
- enum lp8788_adc_id id;
- const char *chan_name[LPADC_MAX] = {
- [LPADC_VBATT_5P5] = "vbatt-5p5",
- [LPADC_VBATT_6P0] = "vbatt-6p0",
- [LPADC_VBATT_5P0] = "vbatt-5p0",
- [LPADC_ADC1] = "adc1",
- [LPADC_ADC2] = "adc2",
- [LPADC_ADC3] = "adc3",
- [LPADC_ADC4] = "adc4",
- };
if (!pdata)
return;
- id = pdata->vbatt_adc;
- switch (id) {
- case LPADC_VBATT_5P5:
- case LPADC_VBATT_6P0:
- case LPADC_VBATT_5P0:
- chan = iio_channel_get(NULL, chan_name[id]);
- pchg->chan[LP8788_VBATT] = IS_ERR(chan) ? NULL : chan;
- break;
- default:
- dev_err(dev, "invalid ADC id for VBATT: %d\n", id);
- pchg->chan[LP8788_VBATT] = NULL;
- break;
- }
+ /* ADC channel for battery voltage */
+ chan = iio_channel_get(consumer_name, pdata->adc_vbatt);
+ pchg->chan[LP8788_VBATT] = IS_ERR(chan) ? NULL : chan;
- id = pdata->batt_temp_adc;
- switch (id) {
- case LPADC_ADC1:
- case LPADC_ADC2:
- case LPADC_ADC3:
- case LPADC_ADC4:
- chan = iio_channel_get(NULL, chan_name[id]);
- pchg->chan[LP8788_BATT_TEMP] = IS_ERR(chan) ? NULL : chan;
- break;
- default:
- dev_err(dev, "invalid ADC id for BATT_TEMP : %d\n", id);
- pchg->chan[LP8788_BATT_TEMP] = NULL;
- break;
- }
+ /* ADC channel for battery temperature */
+ chan = iio_channel_get(consumer_name, pdata->adc_batt_temp);
+ pchg->chan[LP8788_BATT_TEMP] = IS_ERR(chan) ? NULL : chan;
}
static void lp8788_release_adc_channel(struct lp8788_charger *pchg)
@@ -747,7 +704,7 @@ static int lp8788_charger_probe(struct platform_device *pdev)
if (ret)
return ret;
- lp8788_setup_adc_channel(pchg);
+ lp8788_setup_adc_channel(pdev->name, pchg);
ret = lp8788_psy_register(pdev, pchg);
if (ret)
diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c
index 5ffe46916f0..d664ef58afa 100644
--- a/drivers/power/max17042_battery.c
+++ b/drivers/power/max17042_battery.c
@@ -572,7 +572,8 @@ static int max17042_init_chip(struct max17042_chip *chip)
__func__);
return -EIO;
}
- max17042_verify_model_lock(chip);
+
+ ret = max17042_verify_model_lock(chip);
if (ret) {
dev_err(&chip->client->dev, "%s lock verify failed\n",
__func__);
diff --git a/drivers/power/max8925_power.c b/drivers/power/max8925_power.c
index 1a075f1f1b6..665cdc76c26 100644
--- a/drivers/power/max8925_power.c
+++ b/drivers/power/max8925_power.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/err.h>
#include <linux/slab.h>
+#include <linux/of.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
@@ -426,6 +427,54 @@ static int max8925_deinit_charger(struct max8925_power_info *info)
return 0;
}
+#ifdef CONFIG_OF
+static struct max8925_power_pdata *
+max8925_power_dt_init(struct platform_device *pdev)
+{
+ struct device_node *nproot = pdev->dev.parent->of_node;
+ struct device_node *np;
+ int batt_detect;
+ int topoff_threshold;
+ int fast_charge;
+ int no_temp_support;
+ int no_insert_detect;
+ struct max8925_power_pdata *pdata;
+
+ if (!nproot)
+ return pdev->dev.platform_data;
+
+ np = of_find_node_by_name(nproot, "charger");
+ if (!np) {
+ dev_err(&pdev->dev, "failed to find charger node\n");
+ return NULL;
+ }
+
+ pdata = devm_kzalloc(&pdev->dev,
+ sizeof(struct max8925_power_pdata),
+ GFP_KERNEL);
+
+ of_property_read_u32(np, "topoff-threshold", &topoff_threshold);
+ of_property_read_u32(np, "batt-detect", &batt_detect);
+ of_property_read_u32(np, "fast-charge", &fast_charge);
+ of_property_read_u32(np, "no-insert-detect", &no_insert_detect);
+ of_property_read_u32(np, "no-temp-support", &no_temp_support);
+
+ pdata->batt_detect = batt_detect;
+ pdata->fast_charge = fast_charge;
+ pdata->topoff_threshold = topoff_threshold;
+ pdata->no_insert_detect = no_insert_detect;
+ pdata->no_temp_support = no_temp_support;
+
+ return pdata;
+}
+#else
+static struct max8925_power_pdata *
+max8925_power_dt_init(struct platform_device *pdev)
+{
+ return pdev->dev.platform_data;
+}
+#endif
+
static int max8925_power_probe(struct platform_device *pdev)
{
struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
@@ -433,7 +482,7 @@ static int max8925_power_probe(struct platform_device *pdev)
struct max8925_power_info *info;
int ret;
- pdata = pdev->dev.platform_data;
+ pdata = max8925_power_dt_init(pdev);
if (!pdata) {
dev_err(&pdev->dev, "platform data isn't assigned to "
"power supply\n");
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index f77a41272e5..8a7cfb3cc16 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -216,6 +216,86 @@ static void psy_unregister_thermal(struct power_supply *psy)
return;
thermal_zone_device_unregister(psy->tzd);
}
+
+/* thermal cooling device callbacks */
+static int ps_get_max_charge_cntl_limit(struct thermal_cooling_device *tcd,
+ unsigned long *state)
+{
+ struct power_supply *psy;
+ union power_supply_propval val;
+ int ret;
+
+ psy = tcd->devdata;
+ ret = psy->get_property(psy,
+ POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, &val);
+ if (!ret)
+ *state = val.intval;
+
+ return ret;
+}
+
+static int ps_get_cur_chrage_cntl_limit(struct thermal_cooling_device *tcd,
+ unsigned long *state)
+{
+ struct power_supply *psy;
+ union power_supply_propval val;
+ int ret;
+
+ psy = tcd->devdata;
+ ret = psy->get_property(psy,
+ POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val);
+ if (!ret)
+ *state = val.intval;
+
+ return ret;
+}
+
+static int ps_set_cur_charge_cntl_limit(struct thermal_cooling_device *tcd,
+ unsigned long state)
+{
+ struct power_supply *psy;
+ union power_supply_propval val;
+ int ret;
+
+ psy = tcd->devdata;
+ val.intval = state;
+ ret = psy->set_property(psy,
+ POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val);
+
+ return ret;
+}
+
+static struct thermal_cooling_device_ops psy_tcd_ops = {
+ .get_max_state = ps_get_max_charge_cntl_limit,
+ .get_cur_state = ps_get_cur_chrage_cntl_limit,
+ .set_cur_state = ps_set_cur_charge_cntl_limit,
+};
+
+static int psy_register_cooler(struct power_supply *psy)
+{
+ int i;
+
+ /* Register for cooling device if psy can control charging */
+ for (i = 0; i < psy->num_properties; i++) {
+ if (psy->properties[i] ==
+ POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT) {
+ psy->tcd = thermal_cooling_device_register(
+ (char *)psy->name,
+ psy, &psy_tcd_ops);
+ if (IS_ERR(psy->tcd))
+ return PTR_ERR(psy->tcd);
+ break;
+ }
+ }
+ return 0;
+}
+
+static void psy_unregister_cooler(struct power_supply *psy)
+{
+ if (IS_ERR_OR_NULL(psy->tcd))
+ return;
+ thermal_cooling_device_unregister(psy->tcd);
+}
#else
static int psy_register_thermal(struct power_supply *psy)
{
@@ -225,6 +305,15 @@ static int psy_register_thermal(struct power_supply *psy)
static void psy_unregister_thermal(struct power_supply *psy)
{
}
+
+static int psy_register_cooler(struct power_supply *psy)
+{
+ return 0;
+}
+
+static void psy_unregister_cooler(struct power_supply *psy)
+{
+}
#endif
int power_supply_register(struct device *parent, struct power_supply *psy)
@@ -259,6 +348,10 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
if (rc)
goto register_thermal_failed;
+ rc = psy_register_cooler(psy);
+ if (rc)
+ goto register_cooler_failed;
+
rc = power_supply_create_triggers(psy);
if (rc)
goto create_triggers_failed;
@@ -268,6 +361,8 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
goto success;
create_triggers_failed:
+ psy_unregister_cooler(psy);
+register_cooler_failed:
psy_unregister_thermal(psy);
register_thermal_failed:
device_del(dev);
@@ -284,6 +379,7 @@ void power_supply_unregister(struct power_supply *psy)
cancel_work_sync(&psy->changed_work);
sysfs_remove_link(&psy->dev->kobj, "powers");
power_supply_remove_triggers(psy);
+ psy_unregister_cooler(psy);
psy_unregister_thermal(psy);
device_unregister(psy->dev);
}
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 395c2cfa16c..40fa3b7cae5 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -164,6 +164,8 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(constant_charge_current_max),
POWER_SUPPLY_ATTR(constant_charge_voltage),
POWER_SUPPLY_ATTR(constant_charge_voltage_max),
+ POWER_SUPPLY_ATTR(charge_control_limit),
+ POWER_SUPPLY_ATTR(charge_control_limit_max),
POWER_SUPPLY_ATTR(energy_full_design),
POWER_SUPPLY_ATTR(energy_empty_design),
POWER_SUPPLY_ATTR(energy_full),
diff --git a/drivers/power/rx51_battery.c b/drivers/power/rx51_battery.c
new file mode 100644
index 00000000000..ca49d6c0ee9
--- /dev/null
+++ b/drivers/power/rx51_battery.c
@@ -0,0 +1,251 @@
+/*
+ * Nokia RX-51 battery driver
+ *
+ * Copyright (C) 2012 Pali Rohár <pali.rohar@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/i2c/twl4030-madc.h>
+
+struct rx51_device_info {
+ struct device *dev;
+ struct power_supply bat;
+};
+
+/*
+ * Read ADCIN channel value, code copied from maemo kernel
+ */
+static int rx51_battery_read_adc(int channel)
+{
+ struct twl4030_madc_request req;
+
+ req.channels = 1 << channel;
+ req.do_avg = 1;
+ req.method = TWL4030_MADC_SW1;
+ req.func_cb = NULL;
+ req.type = TWL4030_MADC_WAIT;
+
+ if (twl4030_madc_conversion(&req) <= 0)
+ return -ENODATA;
+
+ return req.rbuf[channel];
+}
+
+/*
+ * Read ADCIN channel 12 (voltage) and convert RAW value to micro voltage
+ * This conversion formula was extracted from maemo program bsi-read
+ */
+static int rx51_battery_read_voltage(struct rx51_device_info *di)
+{
+ int voltage = rx51_battery_read_adc(12);
+
+ if (voltage < 0)
+ return voltage;
+
+ return 1000 * (10000 * voltage / 1705);
+}
+
+/*
+ * Temperature look-up tables
+ * TEMP = (1/(t1 + 1/298) - 273.15)
+ * Where t1 = (1/B) * ln((RAW_ADC_U * 2.5)/(R * I * 255))
+ * Formula is based on experimental data, RX-51 CAL data, maemo program bme
+ * and formula from da9052 driver with values R = 100, B = 3380, I = 0.00671
+ */
+
+/*
+ * Table1 (temperature for first 25 RAW values)
+ * Usage: TEMP = rx51_temp_table1[RAW]
+ * RAW is between 1 and 24
+ * TEMP is between 201 C and 55 C
+ */
+static u8 rx51_temp_table1[] = {
+ 255, 201, 159, 138, 124, 114, 106, 99, 94, 89, 85, 82, 78, 75,
+ 73, 70, 68, 66, 64, 62, 61, 59, 57, 56, 55
+};
+
+/*
+ * Table2 (lowest RAW value for temperature)
+ * Usage: RAW = rx51_temp_table2[TEMP-rx51_temp_table2_first]
+ * TEMP is between 53 C and -32 C
+ * RAW is between 25 and 993
+ */
+#define rx51_temp_table2_first 53
+static u16 rx51_temp_table2[] = {
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 39,
+ 40, 41, 43, 44, 46, 48, 49, 51, 53, 55, 57, 59, 61, 64,
+ 66, 69, 71, 74, 77, 80, 83, 86, 90, 94, 97, 101, 106, 110,
+ 115, 119, 125, 130, 136, 141, 148, 154, 161, 168, 176, 184, 202, 211,
+ 221, 231, 242, 254, 266, 279, 293, 308, 323, 340, 357, 375, 395, 415,
+ 437, 460, 485, 511, 539, 568, 600, 633, 669, 706, 747, 790, 836, 885,
+ 937, 993, 1024
+};
+
+/*
+ * Read ADCIN channel 0 (battery temp) and convert value to tenths of Celsius
+ * Use Temperature look-up tables for conversation
+ */
+static int rx51_battery_read_temperature(struct rx51_device_info *di)
+{
+ int min = 0;
+ int max = ARRAY_SIZE(rx51_temp_table2) - 1;
+ int raw = rx51_battery_read_adc(0);
+
+ /* Zero and negative values are undefined */
+ if (raw <= 0)
+ return INT_MAX;
+
+ /* ADC channels are 10 bit, higher value are undefined */
+ if (raw >= (1 << 10))
+ return INT_MIN;
+
+ /* First check for temperature in first direct table */
+ if (raw < ARRAY_SIZE(rx51_temp_table1))
+ return rx51_temp_table1[raw] * 100;
+
+ /* Binary search RAW value in second inverse table */
+ while (max - min > 1) {
+ int mid = (max + min) / 2;
+ if (rx51_temp_table2[mid] <= raw)
+ min = mid;
+ else if (rx51_temp_table2[mid] > raw)
+ max = mid;
+ if (rx51_temp_table2[mid] == raw)
+ break;
+ }
+
+ return (rx51_temp_table2_first - min) * 100;
+}
+
+/*
+ * Read ADCIN channel 4 (BSI) and convert RAW value to micro Ah
+ * This conversion formula was extracted from maemo program bsi-read
+ */
+static int rx51_battery_read_capacity(struct rx51_device_info *di)
+{
+ int capacity = rx51_battery_read_adc(4);
+
+ if (capacity < 0)
+ return capacity;
+
+ return 1280 * (1200 * capacity)/(1024 - capacity);
+}
+
+/*
+ * Return power_supply property
+ */
+static int rx51_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct rx51_device_info *di = container_of((psy),
+ struct rx51_device_info, bat);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = 4200000;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = rx51_battery_read_voltage(di) ? 1 : 0;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = rx51_battery_read_voltage(di);
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = rx51_battery_read_temperature(di);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ val->intval = rx51_battery_read_capacity(di);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (val->intval == INT_MAX || val->intval == INT_MIN)
+ return -EINVAL;
+
+ return 0;
+}
+
+static enum power_supply_property rx51_battery_props[] = {
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+};
+
+static int __devinit rx51_battery_probe(struct platform_device *pdev)
+{
+ struct rx51_device_info *di;
+ int ret;
+
+ di = kzalloc(sizeof(*di), GFP_KERNEL);
+ if (!di)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, di);
+
+ di->bat.name = dev_name(&pdev->dev);
+ di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
+ di->bat.properties = rx51_battery_props;
+ di->bat.num_properties = ARRAY_SIZE(rx51_battery_props);
+ di->bat.get_property = rx51_battery_get_property;
+
+ ret = power_supply_register(di->dev, &di->bat);
+ if (ret) {
+ platform_set_drvdata(pdev, NULL);
+ kfree(di);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devexit rx51_battery_remove(struct platform_device *pdev)
+{
+ struct rx51_device_info *di = platform_get_drvdata(pdev);
+
+ power_supply_unregister(&di->bat);
+ platform_set_drvdata(pdev, NULL);
+ kfree(di);
+
+ return 0;
+}
+
+static struct platform_driver rx51_battery_driver = {
+ .probe = rx51_battery_probe,
+ .remove = __devexit_p(rx51_battery_remove),
+ .driver = {
+ .name = "rx51-battery",
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(rx51_battery_driver);
+
+MODULE_ALIAS("platform:rx51-battery");
+MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
+MODULE_DESCRIPTION("Nokia RX-51 battery driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c
index f9e70cf0819..a69d0d11b54 100644
--- a/drivers/power/twl4030_charger.c
+++ b/drivers/power/twl4030_charger.c
@@ -114,12 +114,12 @@ static int twl4030_clear_set(u8 mod_no, u8 clear, u8 set, u8 reg)
static int twl4030_bci_read(u8 reg, u8 *val)
{
- return twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, val, reg);
+ return twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, val, reg);
}
static int twl4030_clear_set_boot_bci(u8 clear, u8 set)
{
- return twl4030_clear_set(TWL4030_MODULE_PM_MASTER, clear,
+ return twl4030_clear_set(TWL_MODULE_PM_MASTER, clear,
TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set,
TWL4030_PM_MASTER_BOOT_BCI);
}
@@ -152,7 +152,7 @@ static int twl4030_bci_have_vbus(struct twl4030_bci *bci)
int ret;
u8 hwsts;
- ret = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &hwsts,
+ ret = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &hwsts,
TWL4030_PM_MASTER_STS_HW_CONDITIONS);
if (ret < 0)
return 0;
@@ -199,7 +199,7 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
return ret;
/* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */
- ret = twl4030_clear_set(TWL4030_MODULE_MAIN_CHARGE, 0,
+ ret = twl4030_clear_set(TWL_MODULE_MAIN_CHARGE, 0,
TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4);
} else {
ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0);
@@ -238,7 +238,7 @@ static int twl4030_charger_enable_backup(int uvolt, int uamp)
if (uvolt < 2500000 ||
uamp < 25) {
/* disable charging of backup battery */
- ret = twl4030_clear_set(TWL4030_MODULE_PM_RECEIVER,
+ ret = twl4030_clear_set(TWL_MODULE_PM_RECEIVER,
TWL4030_BBCHEN, 0, TWL4030_BB_CFG);
return ret;
}
@@ -262,7 +262,7 @@ static int twl4030_charger_enable_backup(int uvolt, int uamp)
else
flags |= TWL4030_BBISEL_25uA;
- ret = twl4030_clear_set(TWL4030_MODULE_PM_RECEIVER,
+ ret = twl4030_clear_set(TWL_MODULE_PM_RECEIVER,
TWL4030_BBSEL_MASK | TWL4030_BBISEL_MASK,
flags,
TWL4030_BB_CFG);
diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c
index 1c5ab0172ea..2b557119ada 100644
--- a/drivers/regulator/88pm8607.c
+++ b/drivers/regulator/88pm8607.c
@@ -394,7 +394,7 @@ static int pm8607_regulator_dt_init(struct platform_device *pdev,
#define pm8607_regulator_dt_init(x, y, z) (-1)
#endif
-static int __devinit pm8607_regulator_probe(struct platform_device *pdev)
+static int pm8607_regulator_probe(struct platform_device *pdev)
{
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct pm8607_regulator_info *info = NULL;
@@ -454,7 +454,7 @@ static int __devinit pm8607_regulator_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit pm8607_regulator_remove(struct platform_device *pdev)
+static int pm8607_regulator_remove(struct platform_device *pdev)
{
struct pm8607_regulator_info *info = platform_get_drvdata(pdev);
@@ -481,7 +481,7 @@ static struct platform_driver pm8607_regulator_driver = {
.owner = THIS_MODULE,
},
.probe = pm8607_regulator_probe,
- .remove = __devexit_p(pm8607_regulator_remove),
+ .remove = pm8607_regulator_remove,
.id_table = pm8607_regulator_driver_ids,
};
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 67d47b59a66..551a22b0753 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -109,6 +109,16 @@ config REGULATOR_DA9052
This driver supports the voltage regulators of DA9052-BC and
DA9053-AA/Bx PMIC.
+config REGULATOR_DA9055
+ tristate "Dialog Semiconductor DA9055 regulators"
+ depends on MFD_DA9055
+ help
+ Say y here to support the BUCKs and LDOs regulators found on
+ Dialog Semiconductor DA9055 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called da9055-regulator.
+
config REGULATOR_FAN53555
tristate "Fairchild FAN53555 Regulator"
depends on I2C
@@ -204,6 +214,16 @@ config REGULATOR_MAX8952
via I2C bus. Maxim 8952 has one voltage output and supports 4 DVS
modes ranging from 0.77V to 1.40V by 0.01V steps.
+config REGULATOR_MAX8973
+ tristate "Maxim MAX8973 voltage regulator "
+ depends on I2C
+ select REGMAP_I2C
+ help
+ The MAXIM MAX8973 high-efficiency. three phase, DC-DC step-down
+ switching regulator delievers up to 9A of output current. Each
+ phase operates at a 2MHz fixed frequency with a 120 deg shift
+ from the adjacent phase, allowing the use of small magnetic component.
+
config REGULATOR_MAX8997
tristate "Maxim 8997/8966 regulator"
depends on MFD_MAX8997
@@ -335,6 +355,17 @@ config REGULATOR_PALMAS
on the muxing. This is handled automatically in the driver by
reading the mux info from OTP.
+config REGULATOR_TPS51632
+ tristate "TI TPS51632 Power Regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ This driver supports TPS51632 voltage regulator chip.
+ The TPS51632 is 3-2-1 Phase D-Cap+ Step Down Driverless Controller
+ with Serial VID control and DVFS.
+ The voltage output can be configure through I2C interface or PWM
+ interface.
+
config REGULATOR_TPS6105X
tristate "TI TPS6105X Power regulators"
depends on TPS6105X
@@ -415,6 +446,15 @@ config REGULATOR_TPS65912
help
This driver supports TPS65912 voltage regulator chip.
+config REGULATOR_TPS80031
+ tristate "TI TPS80031/TPS80032 power regualtor driver"
+ depends on MFD_TPS80031
+ help
+ TPS80031/ TPS80032 Fully Integrated Power Management with Power
+ Path and Battery Charger. It has 5 configurable step-down
+ converters, 11 general purpose LDOs, VBUS generator and digital
+ output to control regulators.
+
config REGULATOR_TWL4030
bool "TI TWL4030/TWL5030/TWL6030/TPS659x0 PMIC"
depends on TWL4030_CORE
@@ -422,6 +462,13 @@ config REGULATOR_TWL4030
This driver supports the voltage regulators provided by
this family of companion chips.
+config REGULATOR_VEXPRESS
+ tristate "Versatile Express regulators"
+ depends on VEXPRESS_CONFIG
+ help
+ This driver provides support for voltage regulators available
+ on the ARM Ltd's Versatile Express platform.
+
config REGULATOR_WM831X
tristate "Wolfson Microelectronics WM831x PMIC regulators"
depends on MFD_WM831X
@@ -450,5 +497,12 @@ config REGULATOR_WM8994
This driver provides support for the voltage regulators on the
WM8994 CODEC.
+config REGULATOR_AS3711
+ tristate "AS3711 PMIC"
+ depends on MFD_AS3711
+ help
+ This driver provides support for the voltage regulators on the
+ AS3711 PMIC
+
endif
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index e431eed8a87..b802b0c7fb0 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -16,8 +16,10 @@ obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o
obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o
obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o
obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o
+obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
+obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o
obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o
obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o
obj-$(CONFIG_REGULATOR_FAN53555) += fan53555.o
@@ -34,6 +36,7 @@ obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o
obj-$(CONFIG_REGULATOR_MAX8907) += max8907-regulator.o
obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o
obj-$(CONFIG_REGULATOR_MAX8952) += max8952.o
+obj-$(CONFIG_REGULATOR_MAX8973) += max8973-regulator.o
obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o
obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o
obj-$(CONFIG_REGULATOR_MAX77686) += max77686.o
@@ -41,6 +44,7 @@ obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
+obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o
obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o
@@ -56,7 +60,9 @@ obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o
obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o
obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o
obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o
+obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o
obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o
+obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o
diff --git a/drivers/regulator/aat2870-regulator.c b/drivers/regulator/aat2870-regulator.c
index 167c93f2198..8b5876356db 100644
--- a/drivers/regulator/aat2870-regulator.c
+++ b/drivers/regulator/aat2870-regulator.c
@@ -187,7 +187,7 @@ static int aat2870_regulator_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit aat2870_regulator_remove(struct platform_device *pdev)
+static int aat2870_regulator_remove(struct platform_device *pdev)
{
struct regulator_dev *rdev = platform_get_drvdata(pdev);
@@ -201,7 +201,7 @@ static struct platform_driver aat2870_regulator_driver = {
.owner = THIS_MODULE,
},
.probe = aat2870_regulator_probe,
- .remove = __devexit_p(aat2870_regulator_remove),
+ .remove = aat2870_regulator_remove,
};
static int __init aat2870_regulator_init(void)
diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c
index df4ad8927f0..111ec69a3e9 100644
--- a/drivers/regulator/ab3100.c
+++ b/drivers/regulator/ab3100.c
@@ -494,7 +494,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = {
* for all the different regulators.
*/
-static int __devinit ab3100_regulators_probe(struct platform_device *pdev)
+static int ab3100_regulators_probe(struct platform_device *pdev)
{
struct ab3100_platform_data *plfdata = pdev->dev.platform_data;
struct regulator_config config = { };
@@ -571,7 +571,7 @@ static int __devinit ab3100_regulators_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit ab3100_regulators_remove(struct platform_device *pdev)
+static int ab3100_regulators_remove(struct platform_device *pdev)
{
int i;
@@ -589,7 +589,7 @@ static struct platform_driver ab3100_regulators_driver = {
.owner = THIS_MODULE,
},
.probe = ab3100_regulators_probe,
- .remove = __devexit_p(ab3100_regulators_remove),
+ .remove = ab3100_regulators_remove,
};
static __init int ab3100_regulators_init(void)
diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
index e3d1d063025..09014f38a94 100644
--- a/drivers/regulator/ab8500.c
+++ b/drivers/regulator/ab8500.c
@@ -641,7 +641,7 @@ static struct ab8500_reg_init ab8500_reg_init[] = {
REG_INIT(AB8500_REGUCTRLDISCH2, 0x04, 0x44, 0x16),
};
-static __devinit int
+static int
ab8500_regulator_init_registers(struct platform_device *pdev, int id, int value)
{
int err;
@@ -676,7 +676,7 @@ ab8500_regulator_init_registers(struct platform_device *pdev, int id, int value)
return 0;
}
-static __devinit int ab8500_regulator_register(struct platform_device *pdev,
+static int ab8500_regulator_register(struct platform_device *pdev,
struct regulator_init_data *init_data,
int id,
struct device_node *np)
@@ -735,7 +735,7 @@ static struct of_regulator_match ab8500_regulator_matches[] = {
{ .name = "ab8500_ldo_ana", .driver_data = (void *) AB8500_LDO_ANA, },
};
-static __devinit int
+static int
ab8500_regulator_of_probe(struct platform_device *pdev, struct device_node *np)
{
int err, i;
@@ -751,7 +751,7 @@ ab8500_regulator_of_probe(struct platform_device *pdev, struct device_node *np)
return 0;
}
-static __devinit int ab8500_regulator_probe(struct platform_device *pdev)
+static int ab8500_regulator_probe(struct platform_device *pdev)
{
struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
struct ab8500_platform_data *pdata;
@@ -817,7 +817,7 @@ static __devinit int ab8500_regulator_probe(struct platform_device *pdev)
return 0;
}
-static __devexit int ab8500_regulator_remove(struct platform_device *pdev)
+static int ab8500_regulator_remove(struct platform_device *pdev)
{
int i;
@@ -836,7 +836,7 @@ static __devexit int ab8500_regulator_remove(struct platform_device *pdev)
static struct platform_driver ab8500_regulator_driver = {
.probe = ab8500_regulator_probe,
- .remove = __devexit_p(ab8500_regulator_remove),
+ .remove = ab8500_regulator_remove,
.driver = {
.name = "ab8500-regulator",
.owner = THIS_MODULE,
diff --git a/drivers/regulator/ad5398.c b/drivers/regulator/ad5398.c
index f123f7e3b75..6b981b5faa7 100644
--- a/drivers/regulator/ad5398.c
+++ b/drivers/regulator/ad5398.c
@@ -211,7 +211,7 @@ static const struct i2c_device_id ad5398_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ad5398_id);
-static int __devinit ad5398_probe(struct i2c_client *client,
+static int ad5398_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regulator_init_data *init_data = client->dev.platform_data;
@@ -256,7 +256,7 @@ err:
return ret;
}
-static int __devexit ad5398_remove(struct i2c_client *client)
+static int ad5398_remove(struct i2c_client *client)
{
struct ad5398_chip_info *chip = i2c_get_clientdata(client);
@@ -266,7 +266,7 @@ static int __devexit ad5398_remove(struct i2c_client *client)
static struct i2c_driver ad5398_driver = {
.probe = ad5398_probe,
- .remove = __devexit_p(ad5398_remove),
+ .remove = ad5398_remove,
.driver = {
.name = "ad5398",
},
diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c
index 1af97686f44..0199eeea63b 100644
--- a/drivers/regulator/anatop-regulator.c
+++ b/drivers/regulator/anatop-regulator.c
@@ -48,36 +48,21 @@ static int anatop_regmap_set_voltage_sel(struct regulator_dev *reg,
unsigned selector)
{
struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg);
- u32 val, mask;
if (!anatop_reg->control_reg)
return -ENOTSUPP;
- val = anatop_reg->min_bit_val + selector;
- dev_dbg(&reg->dev, "%s: calculated val %d\n", __func__, val);
- mask = ((1 << anatop_reg->vol_bit_width) - 1) <<
- anatop_reg->vol_bit_shift;
- val <<= anatop_reg->vol_bit_shift;
- regmap_update_bits(anatop_reg->anatop, anatop_reg->control_reg,
- mask, val);
-
- return 0;
+ return regulator_set_voltage_sel_regmap(reg, selector);
}
static int anatop_regmap_get_voltage_sel(struct regulator_dev *reg)
{
struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg);
- u32 val, mask;
if (!anatop_reg->control_reg)
return -ENOTSUPP;
- regmap_read(anatop_reg->anatop, anatop_reg->control_reg, &val);
- mask = ((1 << anatop_reg->vol_bit_width) - 1) <<
- anatop_reg->vol_bit_shift;
- val = (val & mask) >> anatop_reg->vol_bit_shift;
-
- return val - anatop_reg->min_bit_val;
+ return regulator_get_voltage_sel_regmap(reg);
}
static struct regulator_ops anatop_rops = {
@@ -87,7 +72,7 @@ static struct regulator_ops anatop_rops = {
.map_voltage = regulator_map_voltage_linear,
};
-static int __devinit anatop_regulator_probe(struct platform_device *pdev)
+static int anatop_regulator_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
@@ -158,15 +143,20 @@ static int __devinit anatop_regulator_probe(struct platform_device *pdev)
goto anatop_probe_end;
}
- rdesc->n_voltages = (sreg->max_voltage - sreg->min_voltage)
- / 25000 + 1;
+ rdesc->n_voltages = (sreg->max_voltage - sreg->min_voltage) / 25000 + 1
+ + sreg->min_bit_val;
rdesc->min_uV = sreg->min_voltage;
rdesc->uV_step = 25000;
+ rdesc->linear_min_sel = sreg->min_bit_val;
+ rdesc->vsel_reg = sreg->control_reg;
+ rdesc->vsel_mask = ((1 << sreg->vol_bit_width) - 1) <<
+ sreg->vol_bit_shift;
config.dev = &pdev->dev;
config.init_data = initdata;
config.driver_data = sreg;
config.of_node = pdev->dev.of_node;
+ config.regmap = sreg->anatop;
/* register regulator */
rdev = regulator_register(rdesc, &config);
@@ -186,7 +176,7 @@ anatop_probe_end:
return ret;
}
-static int __devexit anatop_regulator_remove(struct platform_device *pdev)
+static int anatop_regulator_remove(struct platform_device *pdev)
{
struct regulator_dev *rdev = platform_get_drvdata(pdev);
struct anatop_regulator *sreg = rdev_get_drvdata(rdev);
@@ -210,7 +200,7 @@ static struct platform_driver anatop_regulator_driver = {
.of_match_table = of_anatop_regulator_match_tbl,
},
.probe = anatop_regulator_probe,
- .remove = __devexit_p(anatop_regulator_remove),
+ .remove = anatop_regulator_remove,
};
static int __init anatop_regulator_init(void)
diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c
index d184aa35abc..ed7beec53af 100644
--- a/drivers/regulator/arizona-ldo1.c
+++ b/drivers/regulator/arizona-ldo1.c
@@ -34,6 +34,108 @@ struct arizona_ldo1 {
struct regulator_init_data init_data;
};
+static int arizona_ldo1_hc_list_voltage(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ if (selector >= rdev->desc->n_voltages)
+ return -EINVAL;
+
+ if (selector == rdev->desc->n_voltages - 1)
+ return 1800000;
+ else
+ return rdev->desc->min_uV + (rdev->desc->uV_step * selector);
+}
+
+static int arizona_ldo1_hc_map_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ int sel;
+
+ sel = DIV_ROUND_UP(min_uV - rdev->desc->min_uV, rdev->desc->uV_step);
+ if (sel >= rdev->desc->n_voltages)
+ sel = rdev->desc->n_voltages - 1;
+
+ return sel;
+}
+
+static int arizona_ldo1_hc_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned sel)
+{
+ struct arizona_ldo1 *ldo = rdev_get_drvdata(rdev);
+ struct regmap *regmap = ldo->arizona->regmap;
+ unsigned int val;
+ int ret;
+
+ if (sel == rdev->desc->n_voltages - 1)
+ val = ARIZONA_LDO1_HI_PWR;
+ else
+ val = 0;
+
+ ret = regmap_update_bits(regmap, ARIZONA_LDO1_CONTROL_2,
+ ARIZONA_LDO1_HI_PWR, val);
+ if (ret != 0)
+ return ret;
+
+ ret = regmap_update_bits(regmap, ARIZONA_DYNAMIC_FREQUENCY_SCALING_1,
+ ARIZONA_SUBSYS_MAX_FREQ, val);
+ if (ret != 0)
+ return ret;
+
+ if (val)
+ return 0;
+
+ val = sel << ARIZONA_LDO1_VSEL_SHIFT;
+
+ return regmap_update_bits(regmap, ARIZONA_LDO1_CONTROL_1,
+ ARIZONA_LDO1_VSEL_MASK, val);
+}
+
+static int arizona_ldo1_hc_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct arizona_ldo1 *ldo = rdev_get_drvdata(rdev);
+ struct regmap *regmap = ldo->arizona->regmap;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(regmap, ARIZONA_LDO1_CONTROL_2, &val);
+ if (ret != 0)
+ return ret;
+
+ if (val & ARIZONA_LDO1_HI_PWR)
+ return rdev->desc->n_voltages - 1;
+
+ ret = regmap_read(regmap, ARIZONA_LDO1_CONTROL_1, &val);
+ if (ret != 0)
+ return ret;
+
+ return (val & ARIZONA_LDO1_VSEL_MASK) >> ARIZONA_LDO1_VSEL_SHIFT;
+}
+
+static struct regulator_ops arizona_ldo1_hc_ops = {
+ .list_voltage = arizona_ldo1_hc_list_voltage,
+ .map_voltage = arizona_ldo1_hc_map_voltage,
+ .get_voltage_sel = arizona_ldo1_hc_get_voltage_sel,
+ .set_voltage_sel = arizona_ldo1_hc_set_voltage_sel,
+ .get_bypass = regulator_get_bypass_regmap,
+ .set_bypass = regulator_set_bypass_regmap,
+};
+
+static const struct regulator_desc arizona_ldo1_hc = {
+ .name = "LDO1",
+ .supply_name = "LDOVDD",
+ .type = REGULATOR_VOLTAGE,
+ .ops = &arizona_ldo1_hc_ops,
+
+ .bypass_reg = ARIZONA_LDO1_CONTROL_1,
+ .bypass_mask = ARIZONA_LDO1_BYPASS,
+ .min_uV = 900000,
+ .uV_step = 50000,
+ .n_voltages = 8,
+ .enable_time = 500,
+
+ .owner = THIS_MODULE,
+};
+
static struct regulator_ops arizona_ldo1_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
@@ -55,11 +157,22 @@ static const struct regulator_desc arizona_ldo1 = {
.bypass_mask = ARIZONA_LDO1_BYPASS,
.min_uV = 900000,
.uV_step = 50000,
- .n_voltages = 6,
+ .n_voltages = 7,
+ .enable_time = 500,
.owner = THIS_MODULE,
};
+static const struct regulator_init_data arizona_ldo1_dvfs = {
+ .constraints = {
+ .min_uV = 1200000,
+ .max_uV = 1800000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_VOLTAGE,
+ },
+ .num_consumer_supplies = 1,
+};
+
static const struct regulator_init_data arizona_ldo1_default = {
.constraints = {
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
@@ -67,9 +180,10 @@ static const struct regulator_init_data arizona_ldo1_default = {
.num_consumer_supplies = 1,
};
-static __devinit int arizona_ldo1_probe(struct platform_device *pdev)
+static int arizona_ldo1_probe(struct platform_device *pdev)
{
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ const struct regulator_desc *desc;
struct regulator_config config = { };
struct arizona_ldo1 *ldo1;
int ret;
@@ -87,7 +201,17 @@ static __devinit int arizona_ldo1_probe(struct platform_device *pdev)
* default init_data for it. This will be overridden with
* platform data if provided.
*/
- ldo1->init_data = arizona_ldo1_default;
+ switch (arizona->type) {
+ case WM5102:
+ desc = &arizona_ldo1_hc;
+ ldo1->init_data = arizona_ldo1_dvfs;
+ break;
+ default:
+ desc = &arizona_ldo1;
+ ldo1->init_data = arizona_ldo1_default;
+ break;
+ }
+
ldo1->init_data.consumer_supplies = &ldo1->supply;
ldo1->supply.supply = "DCVDD";
ldo1->supply.dev_name = dev_name(arizona->dev);
@@ -102,7 +226,7 @@ static __devinit int arizona_ldo1_probe(struct platform_device *pdev)
else
config.init_data = &ldo1->init_data;
- ldo1->regulator = regulator_register(&arizona_ldo1, &config);
+ ldo1->regulator = regulator_register(desc, &config);
if (IS_ERR(ldo1->regulator)) {
ret = PTR_ERR(ldo1->regulator);
dev_err(arizona->dev, "Failed to register LDO1 supply: %d\n",
@@ -115,7 +239,7 @@ static __devinit int arizona_ldo1_probe(struct platform_device *pdev)
return 0;
}
-static __devexit int arizona_ldo1_remove(struct platform_device *pdev)
+static int arizona_ldo1_remove(struct platform_device *pdev)
{
struct arizona_ldo1 *ldo1 = platform_get_drvdata(pdev);
@@ -126,7 +250,7 @@ static __devexit int arizona_ldo1_remove(struct platform_device *pdev)
static struct platform_driver arizona_ldo1_driver = {
.probe = arizona_ldo1_probe,
- .remove = __devexit_p(arizona_ldo1_remove),
+ .remove = arizona_ldo1_remove,
.driver = {
.name = "arizona-ldo1",
.owner = THIS_MODULE,
diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c
index d9b1f82cc5b..a6d040cbf8a 100644
--- a/drivers/regulator/arizona-micsupp.c
+++ b/drivers/regulator/arizona-micsupp.c
@@ -101,6 +101,8 @@ static const struct regulator_desc arizona_micsupp = {
.bypass_reg = ARIZONA_MIC_CHARGE_PUMP_1,
.bypass_mask = ARIZONA_CPMIC_BYPASS,
+ .enable_time = 3000,
+
.owner = THIS_MODULE,
};
@@ -115,7 +117,7 @@ static const struct regulator_init_data arizona_micsupp_default = {
.num_consumer_supplies = 1,
};
-static __devinit int arizona_micsupp_probe(struct platform_device *pdev)
+static int arizona_micsupp_probe(struct platform_device *pdev)
{
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
struct regulator_config config = { };
@@ -166,7 +168,7 @@ static __devinit int arizona_micsupp_probe(struct platform_device *pdev)
return 0;
}
-static __devexit int arizona_micsupp_remove(struct platform_device *pdev)
+static int arizona_micsupp_remove(struct platform_device *pdev)
{
struct arizona_micsupp *micsupp = platform_get_drvdata(pdev);
@@ -177,7 +179,7 @@ static __devexit int arizona_micsupp_remove(struct platform_device *pdev)
static struct platform_driver arizona_micsupp_driver = {
.probe = arizona_micsupp_probe,
- .remove = __devexit_p(arizona_micsupp_remove),
+ .remove = arizona_micsupp_remove,
.driver = {
.name = "arizona-micsupp",
.owner = THIS_MODULE,
diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c
new file mode 100644
index 00000000000..2f1341db38a
--- /dev/null
+++ b/drivers/regulator/as3711-regulator.c
@@ -0,0 +1,369 @@
+/*
+ * AS3711 PMIC regulator driver, using DCDC Step Down and LDO supplies
+ *
+ * Copyright (C) 2012 Renesas Electronics Corporation
+ * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License as
+ * published by the Free Software Foundation
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/mfd/as3711.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/slab.h>
+
+struct as3711_regulator_info {
+ struct regulator_desc desc;
+ unsigned int max_uV;
+};
+
+struct as3711_regulator {
+ struct as3711_regulator_info *reg_info;
+ struct regulator_dev *rdev;
+};
+
+static int as3711_list_voltage_sd(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ if (selector >= rdev->desc->n_voltages)
+ return -EINVAL;
+
+ if (!selector)
+ return 0;
+ if (selector < 0x41)
+ return 600000 + selector * 12500;
+ if (selector < 0x71)
+ return 1400000 + (selector - 0x40) * 25000;
+ return 2600000 + (selector - 0x70) * 50000;
+}
+
+static int as3711_list_voltage_aldo(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ if (selector >= rdev->desc->n_voltages)
+ return -EINVAL;
+
+ if (selector < 0x10)
+ return 1200000 + selector * 50000;
+ return 1800000 + (selector - 0x10) * 100000;
+}
+
+static int as3711_list_voltage_dldo(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ if (selector >= rdev->desc->n_voltages ||
+ (selector > 0x10 && selector < 0x20))
+ return -EINVAL;
+
+ if (selector < 0x11)
+ return 900000 + selector * 50000;
+ return 1750000 + (selector - 0x20) * 50000;
+}
+
+static int as3711_bound_check(struct regulator_dev *rdev,
+ int *min_uV, int *max_uV)
+{
+ struct as3711_regulator *reg = rdev_get_drvdata(rdev);
+ struct as3711_regulator_info *info = reg->reg_info;
+
+ dev_dbg(&rdev->dev, "%s(), %d, %d, %d\n", __func__,
+ *min_uV, rdev->desc->min_uV, info->max_uV);
+
+ if (*max_uV < *min_uV ||
+ *min_uV > info->max_uV || rdev->desc->min_uV > *max_uV)
+ return -EINVAL;
+
+ if (rdev->desc->n_voltages == 1)
+ return 0;
+
+ if (*max_uV > info->max_uV)
+ *max_uV = info->max_uV;
+
+ if (*min_uV < rdev->desc->min_uV)
+ *min_uV = rdev->desc->min_uV;
+
+ return *min_uV;
+}
+
+static int as3711_sel_check(int min, int max, int bottom, int step)
+{
+ int sel, voltage;
+
+ /* Round up min, when dividing: keeps us within the range */
+ sel = DIV_ROUND_UP(min - bottom, step);
+ voltage = sel * step + bottom;
+ pr_debug("%s(): select %d..%d in %d+N*%d: %d\n", __func__,
+ min, max, bottom, step, sel);
+ if (voltage > max)
+ return -EINVAL;
+
+ return sel;
+}
+
+static int as3711_map_voltage_sd(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ int ret;
+
+ ret = as3711_bound_check(rdev, &min_uV, &max_uV);
+ if (ret <= 0)
+ return ret;
+
+ if (min_uV <= 1400000)
+ return as3711_sel_check(min_uV, max_uV, 600000, 12500);
+
+ if (min_uV <= 2600000)
+ return as3711_sel_check(min_uV, max_uV, 1400000, 25000) + 0x40;
+
+ return as3711_sel_check(min_uV, max_uV, 2600000, 50000) + 0x70;
+}
+
+/*
+ * The regulator API supports 4 modes of operataion: FAST, NORMAL, IDLE and
+ * STANDBY. We map them in the following way to AS3711 SD1-4 DCDC modes:
+ * FAST: sdX_fast=1
+ * NORMAL: low_noise=1
+ * IDLE: low_noise=0
+ */
+
+static int as3711_set_mode_sd(struct regulator_dev *rdev, unsigned int mode)
+{
+ unsigned int fast_bit = rdev->desc->enable_mask,
+ low_noise_bit = fast_bit << 4;
+ u8 val;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = fast_bit | low_noise_bit;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = low_noise_bit;
+ break;
+ case REGULATOR_MODE_IDLE:
+ val = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(rdev->regmap, AS3711_SD_CONTROL_1,
+ low_noise_bit | fast_bit, val);
+}
+
+static unsigned int as3711_get_mode_sd(struct regulator_dev *rdev)
+{
+ unsigned int fast_bit = rdev->desc->enable_mask,
+ low_noise_bit = fast_bit << 4, mask = fast_bit | low_noise_bit;
+ unsigned int val;
+ int ret = regmap_read(rdev->regmap, AS3711_SD_CONTROL_1, &val);
+
+ if (ret < 0)
+ return ret;
+
+ if ((val & mask) == mask)
+ return REGULATOR_MODE_FAST;
+
+ if ((val & mask) == low_noise_bit)
+ return REGULATOR_MODE_NORMAL;
+
+ if (!(val & mask))
+ return REGULATOR_MODE_IDLE;
+
+ return -EINVAL;
+}
+
+static int as3711_map_voltage_aldo(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ int ret;
+
+ ret = as3711_bound_check(rdev, &min_uV, &max_uV);
+ if (ret <= 0)
+ return ret;
+
+ if (min_uV <= 1800000)
+ return as3711_sel_check(min_uV, max_uV, 1200000, 50000);
+
+ return as3711_sel_check(min_uV, max_uV, 1800000, 100000) + 0x10;
+}
+
+static int as3711_map_voltage_dldo(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ int ret;
+
+ ret = as3711_bound_check(rdev, &min_uV, &max_uV);
+ if (ret <= 0)
+ return ret;
+
+ if (min_uV <= 1700000)
+ return as3711_sel_check(min_uV, max_uV, 900000, 50000);
+
+ return as3711_sel_check(min_uV, max_uV, 1750000, 50000) + 0x20;
+}
+
+static struct regulator_ops as3711_sd_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = as3711_list_voltage_sd,
+ .map_voltage = as3711_map_voltage_sd,
+ .get_mode = as3711_get_mode_sd,
+ .set_mode = as3711_set_mode_sd,
+};
+
+static struct regulator_ops as3711_aldo_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = as3711_list_voltage_aldo,
+ .map_voltage = as3711_map_voltage_aldo,
+};
+
+static struct regulator_ops as3711_dldo_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = as3711_list_voltage_dldo,
+ .map_voltage = as3711_map_voltage_dldo,
+};
+
+#define AS3711_REG(_id, _en_reg, _en_bit, _vmask, _vshift, _min_uV, _max_uV, _sfx) \
+ [AS3711_REGULATOR_ ## _id] = { \
+ .desc = { \
+ .name = "as3711-regulator-" # _id, \
+ .id = AS3711_REGULATOR_ ## _id, \
+ .n_voltages = (_vmask + 1), \
+ .ops = &as3711_ ## _sfx ## _ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .vsel_reg = AS3711_ ## _id ## _VOLTAGE, \
+ .vsel_mask = _vmask << _vshift, \
+ .enable_reg = AS3711_ ## _en_reg, \
+ .enable_mask = BIT(_en_bit), \
+ .min_uV = _min_uV, \
+ }, \
+ .max_uV = _max_uV, \
+}
+
+static struct as3711_regulator_info as3711_reg_info[] = {
+ AS3711_REG(SD_1, SD_CONTROL, 0, 0x7f, 0, 612500, 3350000, sd),
+ AS3711_REG(SD_2, SD_CONTROL, 1, 0x7f, 0, 612500, 3350000, sd),
+ AS3711_REG(SD_3, SD_CONTROL, 2, 0x7f, 0, 612500, 3350000, sd),
+ AS3711_REG(SD_4, SD_CONTROL, 3, 0x7f, 0, 612500, 3350000, sd),
+ AS3711_REG(LDO_1, LDO_1_VOLTAGE, 7, 0x1f, 0, 1200000, 3300000, aldo),
+ AS3711_REG(LDO_2, LDO_2_VOLTAGE, 7, 0x1f, 0, 1200000, 3300000, aldo),
+ AS3711_REG(LDO_3, LDO_3_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo),
+ AS3711_REG(LDO_4, LDO_4_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo),
+ AS3711_REG(LDO_5, LDO_5_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo),
+ AS3711_REG(LDO_6, LDO_6_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo),
+ AS3711_REG(LDO_7, LDO_7_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo),
+ AS3711_REG(LDO_8, LDO_8_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo),
+ /* StepUp output voltage depends on supplying regulator */
+};
+
+#define AS3711_REGULATOR_NUM ARRAY_SIZE(as3711_reg_info)
+
+static int as3711_regulator_probe(struct platform_device *pdev)
+{
+ struct as3711_regulator_pdata *pdata = dev_get_platdata(&pdev->dev);
+ struct as3711 *as3711 = dev_get_drvdata(pdev->dev.parent);
+ struct regulator_init_data *reg_data;
+ struct regulator_config config = {.dev = &pdev->dev,};
+ struct as3711_regulator *reg = NULL;
+ struct as3711_regulator *regs;
+ struct regulator_dev *rdev;
+ struct as3711_regulator_info *ri;
+ int ret;
+ int id;
+
+ if (!pdata)
+ dev_dbg(&pdev->dev, "No platform data...\n");
+
+ regs = devm_kzalloc(&pdev->dev, AS3711_REGULATOR_NUM *
+ sizeof(struct as3711_regulator), GFP_KERNEL);
+ if (!regs) {
+ dev_err(&pdev->dev, "Memory allocation failed exiting..\n");
+ return -ENOMEM;
+ }
+
+ for (id = 0, ri = as3711_reg_info; id < AS3711_REGULATOR_NUM; ++id, ri++) {
+ reg_data = pdata ? pdata->init_data[id] : NULL;
+
+ /* No need to register if there is no regulator data */
+ if (!ri->desc.name)
+ continue;
+
+ reg = &regs[id];
+ reg->reg_info = ri;
+
+ config.init_data = reg_data;
+ config.driver_data = reg;
+ config.regmap = as3711->regmap;
+
+ rdev = regulator_register(&ri->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "Failed to register regulator %s\n",
+ ri->desc.name);
+ ret = PTR_ERR(rdev);
+ goto eregreg;
+ }
+ reg->rdev = rdev;
+ }
+ platform_set_drvdata(pdev, regs);
+ return 0;
+
+eregreg:
+ while (--id >= 0)
+ regulator_unregister(regs[id].rdev);
+
+ return ret;
+}
+
+static int as3711_regulator_remove(struct platform_device *pdev)
+{
+ struct as3711_regulator *regs = platform_get_drvdata(pdev);
+ int id;
+
+ for (id = 0; id < AS3711_REGULATOR_NUM; ++id)
+ regulator_unregister(regs[id].rdev);
+ return 0;
+}
+
+static struct platform_driver as3711_regulator_driver = {
+ .driver = {
+ .name = "as3711-regulator",
+ .owner = THIS_MODULE,
+ },
+ .probe = as3711_regulator_probe,
+ .remove = as3711_regulator_remove,
+};
+
+static int __init as3711_regulator_init(void)
+{
+ return platform_driver_register(&as3711_regulator_driver);
+}
+subsys_initcall(as3711_regulator_init);
+
+static void __exit as3711_regulator_exit(void)
+{
+ platform_driver_unregister(&as3711_regulator_driver);
+}
+module_exit(as3711_regulator_exit);
+
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_DESCRIPTION("AS3711 regulator driver");
+MODULE_ALIAS("platform:as3711-regulator");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index e872c8be080..0f65b246cc0 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -199,8 +199,11 @@ static int regulator_check_consumers(struct regulator_dev *rdev,
*min_uV = regulator->min_uV;
}
- if (*min_uV > *max_uV)
+ if (*min_uV > *max_uV) {
+ dev_err(regulator->dev, "Restricting voltage, %u-%uuV\n",
+ regulator->min_uV, regulator->max_uV);
return -EINVAL;
+ }
return 0;
}
@@ -880,7 +883,9 @@ static int machine_constraints_voltage(struct regulator_dev *rdev,
/* final: [min_uV..max_uV] valid iff constraints valid */
if (max_uV < min_uV) {
- rdev_err(rdev, "unsupportable voltage constraints\n");
+ rdev_err(rdev,
+ "unsupportable voltage constraints %u-%uuV\n",
+ min_uV, max_uV);
return -EINVAL;
}
@@ -1867,6 +1872,28 @@ int regulator_is_enabled(struct regulator *regulator)
EXPORT_SYMBOL_GPL(regulator_is_enabled);
/**
+ * regulator_can_change_voltage - check if regulator can change voltage
+ * @regulator: regulator source
+ *
+ * Returns positive if the regulator driver backing the source/client
+ * can change its voltage, false otherwise. Usefull for detecting fixed
+ * or dummy regulators and disabling voltage change logic in the client
+ * driver.
+ */
+int regulator_can_change_voltage(struct regulator *regulator)
+{
+ struct regulator_dev *rdev = regulator->rdev;
+
+ if (rdev->constraints &&
+ rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE &&
+ (rdev->desc->n_voltages - rdev->desc->linear_min_sel) > 1)
+ return 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(regulator_can_change_voltage);
+
+/**
* regulator_count_voltages - count regulator_list_voltage() selectors
* @regulator: regulator source
*
@@ -1897,6 +1924,10 @@ int regulator_list_voltage_linear(struct regulator_dev *rdev,
{
if (selector >= rdev->desc->n_voltages)
return -EINVAL;
+ if (selector < rdev->desc->linear_min_sel)
+ return 0;
+
+ selector -= rdev->desc->linear_min_sel;
return rdev->desc->min_uV + (rdev->desc->uV_step * selector);
}
@@ -1985,6 +2016,11 @@ int regulator_is_supported_voltage(struct regulator *regulator,
return ret;
}
+ /* Any voltage within constrains range is fine? */
+ if (rdev->desc->continuous_voltage_range)
+ return min_uV >= rdev->constraints->min_uV &&
+ max_uV <= rdev->constraints->max_uV;
+
ret = regulator_count_voltages(regulator);
if (ret < 0)
return ret;
@@ -2120,6 +2156,8 @@ int regulator_map_voltage_linear(struct regulator_dev *rdev,
if (ret < 0)
return ret;
+ ret += rdev->desc->linear_min_sel;
+
/* Map back into a voltage to verify we're still in bounds */
voltage = rdev->desc->ops->list_voltage(rdev, ret);
if (voltage < min_uV || voltage > max_uV)
diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c
index 36c5b92fe0a..2afa5730f32 100644
--- a/drivers/regulator/da903x.c
+++ b/drivers/regulator/da903x.c
@@ -460,7 +460,7 @@ static inline struct da903x_regulator_info *find_regulator_info(int id)
return NULL;
}
-static int __devinit da903x_regulator_probe(struct platform_device *pdev)
+static int da903x_regulator_probe(struct platform_device *pdev)
{
struct da903x_regulator_info *ri = NULL;
struct regulator_dev *rdev;
@@ -499,7 +499,7 @@ static int __devinit da903x_regulator_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit da903x_regulator_remove(struct platform_device *pdev)
+static int da903x_regulator_remove(struct platform_device *pdev)
{
struct regulator_dev *rdev = platform_get_drvdata(pdev);
@@ -513,7 +513,7 @@ static struct platform_driver da903x_regulator_driver = {
.owner = THIS_MODULE,
},
.probe = da903x_regulator_probe,
- .remove = __devexit_p(da903x_regulator_remove),
+ .remove = da903x_regulator_remove,
};
static int __init da903x_regulator_init(void)
diff --git a/drivers/regulator/da9052-regulator.c b/drivers/regulator/da9052-regulator.c
index 27355b1199e..d0963090442 100644
--- a/drivers/regulator/da9052-regulator.c
+++ b/drivers/regulator/da9052-regulator.c
@@ -129,17 +129,17 @@ static int da9052_dcdc_set_current_limit(struct regulator_dev *rdev, int min_uA,
else if (offset == 0)
row = 1;
- if (min_uA > da9052_current_limits[row][DA9052_MAX_UA] ||
- max_uA < da9052_current_limits[row][DA9052_MIN_UA])
- return -EINVAL;
-
for (i = DA9052_CURRENT_RANGE - 1; i >= 0; i--) {
- if (da9052_current_limits[row][i] <= max_uA) {
+ if ((min_uA <= da9052_current_limits[row][i]) &&
+ (da9052_current_limits[row][i] <= max_uA)) {
reg_val = i;
break;
}
}
+ if (i < 0)
+ return -EINVAL;
+
/* Determine the even or odd position of the buck current limit
* register field
*/
@@ -365,7 +365,7 @@ static inline struct da9052_regulator_info *find_regulator_info(u8 chip_id,
return NULL;
}
-static int __devinit da9052_regulator_probe(struct platform_device *pdev)
+static int da9052_regulator_probe(struct platform_device *pdev)
{
struct regulator_config config = { };
struct da9052_regulator *regulator;
@@ -430,7 +430,7 @@ static int __devinit da9052_regulator_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit da9052_regulator_remove(struct platform_device *pdev)
+static int da9052_regulator_remove(struct platform_device *pdev)
{
struct da9052_regulator *regulator = platform_get_drvdata(pdev);
@@ -440,7 +440,7 @@ static int __devexit da9052_regulator_remove(struct platform_device *pdev)
static struct platform_driver da9052_regulator_driver = {
.probe = da9052_regulator_probe,
- .remove = __devexit_p(da9052_regulator_remove),
+ .remove = da9052_regulator_remove,
.driver = {
.name = "da9052-regulator",
.owner = THIS_MODULE,
diff --git a/drivers/regulator/da9055-regulator.c b/drivers/regulator/da9055-regulator.c
new file mode 100644
index 00000000000..a4b9cb8c431
--- /dev/null
+++ b/drivers/regulator/da9055-regulator.c
@@ -0,0 +1,641 @@
+/*
+* Regulator driver for DA9055 PMIC
+*
+* Copyright(c) 2012 Dialog Semiconductor Ltd.
+*
+* Author: David Dajun Chen <dchen@diasemi.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#include <linux/mfd/da9055/core.h>
+#include <linux/mfd/da9055/reg.h>
+#include <linux/mfd/da9055/pdata.h>
+
+#define DA9055_MIN_UA 0
+#define DA9055_MAX_UA 3
+
+#define DA9055_LDO_MODE_SYNC 0
+#define DA9055_LDO_MODE_SLEEP 1
+
+#define DA9055_BUCK_MODE_SLEEP 1
+#define DA9055_BUCK_MODE_SYNC 2
+#define DA9055_BUCK_MODE_AUTO 3
+
+/* DA9055 REGULATOR IDs */
+#define DA9055_ID_BUCK1 0
+#define DA9055_ID_BUCK2 1
+#define DA9055_ID_LDO1 2
+#define DA9055_ID_LDO2 3
+#define DA9055_ID_LDO3 4
+#define DA9055_ID_LDO4 5
+#define DA9055_ID_LDO5 6
+#define DA9055_ID_LDO6 7
+
+/* DA9055 BUCK current limit */
+static const int da9055_current_limits[] = { 500000, 600000, 700000, 800000 };
+
+struct da9055_conf_reg {
+ int reg;
+ int sel_mask;
+ int en_mask;
+};
+
+struct da9055_volt_reg {
+ int reg_a;
+ int reg_b;
+ int sl_shift;
+ int v_mask;
+ int v_shift;
+};
+
+struct da9055_mode_reg {
+ int reg;
+ int mask;
+ int shift;
+};
+
+struct da9055_regulator_info {
+ struct regulator_desc reg_desc;
+ struct da9055_conf_reg conf;
+ struct da9055_volt_reg volt;
+ struct da9055_mode_reg mode;
+};
+
+struct da9055_regulator {
+ struct da9055 *da9055;
+ struct da9055_regulator_info *info;
+ struct regulator_dev *rdev;
+ enum gpio_select reg_rselect;
+};
+
+static unsigned int da9055_buck_get_mode(struct regulator_dev *rdev)
+{
+ struct da9055_regulator *regulator = rdev_get_drvdata(rdev);
+ struct da9055_regulator_info *info = regulator->info;
+ int ret, mode = 0;
+
+ ret = da9055_reg_read(regulator->da9055, info->mode.reg);
+ if (ret < 0)
+ return ret;
+
+ switch ((ret & info->mode.mask) >> info->mode.shift) {
+ case DA9055_BUCK_MODE_SYNC:
+ mode = REGULATOR_MODE_FAST;
+ break;
+ case DA9055_BUCK_MODE_AUTO:
+ mode = REGULATOR_MODE_NORMAL;
+ break;
+ case DA9055_BUCK_MODE_SLEEP:
+ mode = REGULATOR_MODE_STANDBY;
+ break;
+ }
+
+ return mode;
+}
+
+static int da9055_buck_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct da9055_regulator *regulator = rdev_get_drvdata(rdev);
+ struct da9055_regulator_info *info = regulator->info;
+ int val = 0;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = DA9055_BUCK_MODE_SYNC << info->mode.shift;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = DA9055_BUCK_MODE_AUTO << info->mode.shift;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = DA9055_BUCK_MODE_SLEEP << info->mode.shift;
+ break;
+ }
+
+ return da9055_reg_update(regulator->da9055, info->mode.reg,
+ info->mode.mask, val);
+}
+
+static unsigned int da9055_ldo_get_mode(struct regulator_dev *rdev)
+{
+ struct da9055_regulator *regulator = rdev_get_drvdata(rdev);
+ struct da9055_regulator_info *info = regulator->info;
+ int ret;
+
+ ret = da9055_reg_read(regulator->da9055, info->volt.reg_b);
+ if (ret < 0)
+ return ret;
+
+ if (ret >> info->volt.sl_shift)
+ return REGULATOR_MODE_STANDBY;
+ else
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int da9055_ldo_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct da9055_regulator *regulator = rdev_get_drvdata(rdev);
+ struct da9055_regulator_info *info = regulator->info;
+ struct da9055_volt_reg volt = info->volt;
+ int val = 0;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ case REGULATOR_MODE_FAST:
+ val = DA9055_LDO_MODE_SYNC;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = DA9055_LDO_MODE_SLEEP;
+ break;
+ }
+
+ return da9055_reg_update(regulator->da9055, volt.reg_b,
+ 1 << volt.sl_shift,
+ val << volt.sl_shift);
+}
+
+static int da9055_buck_get_current_limit(struct regulator_dev *rdev)
+{
+ struct da9055_regulator *regulator = rdev_get_drvdata(rdev);
+ struct da9055_regulator_info *info = regulator->info;
+ int ret;
+
+ ret = da9055_reg_read(regulator->da9055, DA9055_REG_BUCK_LIM);
+ if (ret < 0)
+ return ret;
+
+ ret &= info->mode.mask;
+ return da9055_current_limits[ret >> info->mode.shift];
+}
+
+static int da9055_buck_set_current_limit(struct regulator_dev *rdev, int min_uA,
+ int max_uA)
+{
+ struct da9055_regulator *regulator = rdev_get_drvdata(rdev);
+ struct da9055_regulator_info *info = regulator->info;
+ int i;
+
+ for (i = ARRAY_SIZE(da9055_current_limits) - 1; i >= 0; i--) {
+ if ((min_uA <= da9055_current_limits[i]) &&
+ (da9055_current_limits[i] <= max_uA))
+ return da9055_reg_update(regulator->da9055,
+ DA9055_REG_BUCK_LIM,
+ info->mode.mask,
+ i << info->mode.shift);
+ }
+
+ return -EINVAL;
+}
+
+static int da9055_regulator_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct da9055_regulator *regulator = rdev_get_drvdata(rdev);
+ struct da9055_regulator_info *info = regulator->info;
+ struct da9055_volt_reg volt = info->volt;
+ int ret, sel;
+
+ /*
+ * There are two voltage register set A & B for voltage ramping but
+ * either one of then can be active therefore we first determine
+ * the active register set.
+ */
+ ret = da9055_reg_read(regulator->da9055, info->conf.reg);
+ if (ret < 0)
+ return ret;
+
+ ret &= info->conf.sel_mask;
+
+ /* Get the voltage for the active register set A/B */
+ if (ret == DA9055_REGUALTOR_SET_A)
+ ret = da9055_reg_read(regulator->da9055, volt.reg_a);
+ else
+ ret = da9055_reg_read(regulator->da9055, volt.reg_b);
+
+ if (ret < 0)
+ return ret;
+
+ sel = (ret & volt.v_mask);
+ return sel;
+}
+
+static int da9055_regulator_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ struct da9055_regulator *regulator = rdev_get_drvdata(rdev);
+ struct da9055_regulator_info *info = regulator->info;
+ int ret;
+
+ /*
+ * Regulator register set A/B is not selected through GPIO therefore
+ * we use default register set A for voltage ramping.
+ */
+ if (regulator->reg_rselect == NO_GPIO) {
+ /* Select register set A */
+ ret = da9055_reg_update(regulator->da9055, info->conf.reg,
+ info->conf.sel_mask, DA9055_SEL_REG_A);
+ if (ret < 0)
+ return ret;
+
+ /* Set the voltage */
+ return da9055_reg_update(regulator->da9055, info->volt.reg_a,
+ info->volt.v_mask, selector);
+ }
+
+ /*
+ * Here regulator register set A/B is selected through GPIO.
+ * Therefore we first determine the selected register set A/B and
+ * then set the desired voltage for that register set A/B.
+ */
+ ret = da9055_reg_read(regulator->da9055, info->conf.reg);
+ if (ret < 0)
+ return ret;
+
+ ret &= info->conf.sel_mask;
+
+ /* Set the voltage */
+ if (ret == DA9055_REGUALTOR_SET_A)
+ return da9055_reg_update(regulator->da9055, info->volt.reg_a,
+ info->volt.v_mask, selector);
+ else
+ return da9055_reg_update(regulator->da9055, info->volt.reg_b,
+ info->volt.v_mask, selector);
+}
+
+static int da9055_regulator_set_suspend_voltage(struct regulator_dev *rdev,
+ int uV)
+{
+ struct da9055_regulator *regulator = rdev_get_drvdata(rdev);
+ struct da9055_regulator_info *info = regulator->info;
+ int ret;
+
+ /* Select register set B for suspend voltage ramping. */
+ if (regulator->reg_rselect == NO_GPIO) {
+ ret = da9055_reg_update(regulator->da9055, info->conf.reg,
+ info->conf.sel_mask, DA9055_SEL_REG_B);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = regulator_map_voltage_linear(rdev, uV, uV);
+ if (ret < 0)
+ return ret;
+
+ return da9055_reg_update(regulator->da9055, info->volt.reg_b,
+ info->volt.v_mask, ret);
+}
+
+static int da9055_suspend_enable(struct regulator_dev *rdev)
+{
+ struct da9055_regulator *regulator = rdev_get_drvdata(rdev);
+ struct da9055_regulator_info *info = regulator->info;
+
+ /* Select register set B for voltage ramping. */
+ if (regulator->reg_rselect == NO_GPIO)
+ return da9055_reg_update(regulator->da9055, info->conf.reg,
+ info->conf.sel_mask, DA9055_SEL_REG_B);
+ else
+ return 0;
+}
+
+static int da9055_suspend_disable(struct regulator_dev *rdev)
+{
+ struct da9055_regulator *regulator = rdev_get_drvdata(rdev);
+ struct da9055_regulator_info *info = regulator->info;
+
+ /* Diselect register set B. */
+ if (regulator->reg_rselect == NO_GPIO)
+ return da9055_reg_update(regulator->da9055, info->conf.reg,
+ info->conf.sel_mask, DA9055_SEL_REG_A);
+ else
+ return 0;
+}
+
+static struct regulator_ops da9055_buck_ops = {
+ .get_mode = da9055_buck_get_mode,
+ .set_mode = da9055_buck_set_mode,
+
+ .get_current_limit = da9055_buck_get_current_limit,
+ .set_current_limit = da9055_buck_set_current_limit,
+
+ .get_voltage_sel = da9055_regulator_get_voltage_sel,
+ .set_voltage_sel = da9055_regulator_set_voltage_sel,
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+
+ .set_suspend_voltage = da9055_regulator_set_suspend_voltage,
+ .set_suspend_enable = da9055_suspend_enable,
+ .set_suspend_disable = da9055_suspend_disable,
+ .set_suspend_mode = da9055_buck_set_mode,
+};
+
+static struct regulator_ops da9055_ldo_ops = {
+ .get_mode = da9055_ldo_get_mode,
+ .set_mode = da9055_ldo_set_mode,
+
+ .get_voltage_sel = da9055_regulator_get_voltage_sel,
+ .set_voltage_sel = da9055_regulator_set_voltage_sel,
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+
+ .set_suspend_voltage = da9055_regulator_set_suspend_voltage,
+ .set_suspend_enable = da9055_suspend_enable,
+ .set_suspend_disable = da9055_suspend_disable,
+ .set_suspend_mode = da9055_ldo_set_mode,
+
+};
+
+#define DA9055_LDO(_id, step, min, max, vbits, voffset) \
+{\
+ .reg_desc = {\
+ .name = #_id,\
+ .ops = &da9055_ldo_ops,\
+ .type = REGULATOR_VOLTAGE,\
+ .id = DA9055_ID_##_id,\
+ .n_voltages = (max - min) / step + 1 + (voffset), \
+ .enable_reg = DA9055_REG_BCORE_CONT + DA9055_ID_##_id, \
+ .enable_mask = 1, \
+ .min_uV = (min) * 1000,\
+ .uV_step = (step) * 1000,\
+ .linear_min_sel = (voffset),\
+ .owner = THIS_MODULE,\
+ },\
+ .conf = {\
+ .reg = DA9055_REG_BCORE_CONT + DA9055_ID_##_id, \
+ .sel_mask = (1 << 4),\
+ .en_mask = 1,\
+ },\
+ .volt = {\
+ .reg_a = DA9055_REG_VBCORE_A + DA9055_ID_##_id, \
+ .reg_b = DA9055_REG_VBCORE_B + DA9055_ID_##_id, \
+ .sl_shift = 7,\
+ .v_mask = (1 << (vbits)) - 1,\
+ .v_shift = (vbits),\
+ },\
+}
+
+#define DA9055_BUCK(_id, step, min, max, vbits, voffset, mbits, sbits) \
+{\
+ .reg_desc = {\
+ .name = #_id,\
+ .ops = &da9055_buck_ops,\
+ .type = REGULATOR_VOLTAGE,\
+ .id = DA9055_ID_##_id,\
+ .n_voltages = (max - min) / step + 1 + (voffset), \
+ .enable_reg = DA9055_REG_BCORE_CONT + DA9055_ID_##_id, \
+ .enable_mask = 1,\
+ .min_uV = (min) * 1000,\
+ .uV_step = (step) * 1000,\
+ .linear_min_sel = (voffset),\
+ .owner = THIS_MODULE,\
+ },\
+ .conf = {\
+ .reg = DA9055_REG_BCORE_CONT + DA9055_ID_##_id, \
+ .sel_mask = (1 << 4),\
+ .en_mask = 1,\
+ },\
+ .volt = {\
+ .reg_a = DA9055_REG_VBCORE_A + DA9055_ID_##_id, \
+ .reg_b = DA9055_REG_VBCORE_B + DA9055_ID_##_id, \
+ .sl_shift = 7,\
+ .v_mask = (1 << (vbits)) - 1,\
+ .v_shift = (vbits),\
+ },\
+ .mode = {\
+ .reg = DA9055_REG_BCORE_MODE,\
+ .mask = (mbits),\
+ .shift = (sbits),\
+ },\
+}
+
+static struct da9055_regulator_info da9055_regulator_info[] = {
+ DA9055_BUCK(BUCK1, 25, 725, 2075, 6, 9, 0xc, 2),
+ DA9055_BUCK(BUCK2, 25, 925, 2500, 6, 0, 3, 0),
+ DA9055_LDO(LDO1, 50, 900, 3300, 6, 2),
+ DA9055_LDO(LDO2, 50, 900, 3300, 6, 3),
+ DA9055_LDO(LDO3, 50, 900, 3300, 6, 2),
+ DA9055_LDO(LDO4, 50, 900, 3300, 6, 2),
+ DA9055_LDO(LDO5, 50, 900, 2750, 6, 2),
+ DA9055_LDO(LDO6, 20, 900, 3300, 7, 0),
+};
+
+/*
+ * Configures regulator to be controlled either through GPIO 1 or 2.
+ * GPIO can control regulator state and/or select the regulator register
+ * set A/B for voltage ramping.
+ */
+static __devinit int da9055_gpio_init(struct da9055_regulator *regulator,
+ struct regulator_config *config,
+ struct da9055_pdata *pdata, int id)
+{
+ struct da9055_regulator_info *info = regulator->info;
+ int ret = 0;
+
+ if (pdata->gpio_ren && pdata->gpio_ren[id]) {
+ char name[18];
+ int gpio_mux = pdata->gpio_ren[id];
+
+ config->ena_gpio = pdata->ena_gpio[id];
+ config->ena_gpio_flags = GPIOF_OUT_INIT_HIGH;
+ config->ena_gpio_invert = 1;
+
+ /*
+ * GPI pin is muxed with regulator to control the
+ * regulator state.
+ */
+ sprintf(name, "DA9055 GPI %d", gpio_mux);
+ ret = devm_gpio_request_one(config->dev, gpio_mux, GPIOF_DIR_IN,
+ name);
+ if (ret < 0)
+ goto err;
+
+ /*
+ * Let the regulator know that its state is controlled
+ * through GPI.
+ */
+ ret = da9055_reg_update(regulator->da9055, info->conf.reg,
+ DA9055_E_GPI_MASK,
+ pdata->reg_ren[id]
+ << DA9055_E_GPI_SHIFT);
+ if (ret < 0)
+ goto err;
+ }
+
+ if (pdata->gpio_rsel && pdata->gpio_rsel[id]) {
+ char name[18];
+ int gpio_mux = pdata->gpio_rsel[id];
+
+ regulator->reg_rselect = pdata->reg_rsel[id];
+
+ /*
+ * GPI pin is muxed with regulator to select the
+ * regulator register set A/B for voltage ramping.
+ */
+ sprintf(name, "DA9055 GPI %d", gpio_mux);
+ ret = devm_gpio_request_one(config->dev, gpio_mux, GPIOF_DIR_IN,
+ name);
+ if (ret < 0)
+ goto err;
+
+ /*
+ * Let the regulator know that its register set A/B
+ * will be selected through GPI for voltage ramping.
+ */
+ ret = da9055_reg_update(regulator->da9055, info->conf.reg,
+ DA9055_V_GPI_MASK,
+ pdata->reg_rsel[id]
+ << DA9055_V_GPI_SHIFT);
+ }
+
+err:
+ return ret;
+}
+
+static irqreturn_t da9055_ldo5_6_oc_irq(int irq, void *data)
+{
+ struct da9055_regulator *regulator = data;
+
+ regulator_notifier_call_chain(regulator->rdev,
+ REGULATOR_EVENT_OVER_CURRENT, NULL);
+
+ return IRQ_HANDLED;
+}
+
+static inline struct da9055_regulator_info *find_regulator_info(int id)
+{
+ struct da9055_regulator_info *info;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(da9055_regulator_info); i++) {
+ info = &da9055_regulator_info[i];
+ if (info->reg_desc.id == id)
+ return info;
+ }
+
+ return NULL;
+}
+
+static int __devinit da9055_regulator_probe(struct platform_device *pdev)
+{
+ struct regulator_config config = { };
+ struct da9055_regulator *regulator;
+ struct da9055 *da9055 = dev_get_drvdata(pdev->dev.parent);
+ struct da9055_pdata *pdata = da9055->dev->platform_data;
+ int ret, irq;
+
+ if (pdata == NULL || pdata->regulators[pdev->id] == NULL)
+ return -ENODEV;
+
+ regulator = devm_kzalloc(&pdev->dev, sizeof(struct da9055_regulator),
+ GFP_KERNEL);
+ if (!regulator)
+ return -ENOMEM;
+
+ regulator->info = find_regulator_info(pdev->id);
+ if (regulator->info == NULL) {
+ dev_err(&pdev->dev, "invalid regulator ID specified\n");
+ return -EINVAL;
+ }
+
+ regulator->da9055 = da9055;
+ config.dev = &pdev->dev;
+ config.driver_data = regulator;
+ config.regmap = da9055->regmap;
+
+ if (pdata && pdata->regulators)
+ config.init_data = pdata->regulators[pdev->id];
+
+ ret = da9055_gpio_init(regulator, &config, pdata, pdev->id);
+ if (ret < 0)
+ return ret;
+
+ regulator->rdev = regulator_register(&regulator->info->reg_desc,
+ &config);
+ if (IS_ERR(regulator->rdev)) {
+ dev_err(&pdev->dev, "Failed to register regulator %s\n",
+ regulator->info->reg_desc.name);
+ ret = PTR_ERR(regulator->rdev);
+ return ret;
+ }
+
+ /* Only LDO 5 and 6 has got the over current interrupt */
+ if (pdev->id == DA9055_ID_LDO5 || pdev->id == DA9055_ID_LDO6) {
+ irq = platform_get_irq_byname(pdev, "REGULATOR");
+ irq = regmap_irq_get_virq(da9055->irq_data, irq);
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ da9055_ldo5_6_oc_irq,
+ IRQF_TRIGGER_HIGH |
+ IRQF_ONESHOT |
+ IRQF_PROBE_SHARED,
+ pdev->name, regulator);
+ if (ret != 0) {
+ if (ret != -EBUSY) {
+ dev_err(&pdev->dev,
+ "Failed to request Regulator IRQ %d: %d\n",
+ irq, ret);
+ goto err_regulator;
+ }
+ }
+ }
+
+ platform_set_drvdata(pdev, regulator);
+
+ return 0;
+
+err_regulator:
+ regulator_unregister(regulator->rdev);
+ return ret;
+}
+
+static int __devexit da9055_regulator_remove(struct platform_device *pdev)
+{
+ struct da9055_regulator *regulator = platform_get_drvdata(pdev);
+
+ regulator_unregister(regulator->rdev);
+
+ return 0;
+}
+
+static struct platform_driver da9055_regulator_driver = {
+ .probe = da9055_regulator_probe,
+ .remove = __devexit_p(da9055_regulator_remove),
+ .driver = {
+ .name = "da9055-regulator",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init da9055_regulator_init(void)
+{
+ return platform_driver_register(&da9055_regulator_driver);
+}
+subsys_initcall(da9055_regulator_init);
+
+static void __exit da9055_regulator_exit(void)
+{
+ platform_driver_unregister(&da9055_regulator_driver);
+}
+module_exit(da9055_regulator_exit);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("Power Regulator driver for Dialog DA9055 PMIC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9055-regulator");
diff --git a/drivers/regulator/db8500-prcmu.c b/drivers/regulator/db8500-prcmu.c
index 359f8d18fc3..219d162b651 100644
--- a/drivers/regulator/db8500-prcmu.c
+++ b/drivers/regulator/db8500-prcmu.c
@@ -412,7 +412,7 @@ dbx500_regulator_info[DB8500_NUM_REGULATORS] = {
},
};
-static __devinit int db8500_regulator_register(struct platform_device *pdev,
+static int db8500_regulator_register(struct platform_device *pdev,
struct regulator_init_data *init_data,
int id,
struct device_node *np)
@@ -474,7 +474,7 @@ static struct of_regulator_match db8500_regulator_matches[] = {
{ .name = "db8500_esram34_ret", .driver_data = (void *) DB8500_REGULATOR_SWITCH_ESRAM34RET, },
};
-static __devinit int
+static int
db8500_regulator_of_probe(struct platform_device *pdev,
struct device_node *np)
{
@@ -491,7 +491,7 @@ db8500_regulator_of_probe(struct platform_device *pdev,
return 0;
}
-static int __devinit db8500_regulator_probe(struct platform_device *pdev)
+static int db8500_regulator_probe(struct platform_device *pdev)
{
struct regulator_init_data *db8500_init_data =
dev_get_platdata(&pdev->dev);
diff --git a/drivers/regulator/dbx500-prcmu.c b/drivers/regulator/dbx500-prcmu.c
index f2e5ecdc586..261f3d2299b 100644
--- a/drivers/regulator/dbx500-prcmu.c
+++ b/drivers/regulator/dbx500-prcmu.c
@@ -173,7 +173,7 @@ int __attribute__((weak)) dbx500_regulator_testcase(
return 0;
}
-int __devinit
+int
ux500_regulator_debug_init(struct platform_device *pdev,
struct dbx500_regulator_info *regulator_info,
int num_regulators)
@@ -230,7 +230,7 @@ exit_no_debugfs:
return -ENOMEM;
}
-int __devexit ux500_regulator_debug_exit(void)
+int ux500_regulator_debug_exit(void)
{
debugfs_remove_recursive(rdebug.dir);
kfree(rdebug.state_after_suspend);
diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c
index 03a1d7c11ef..df9f42524ab 100644
--- a/drivers/regulator/dummy.c
+++ b/drivers/regulator/dummy.c
@@ -37,7 +37,7 @@ static struct regulator_desc dummy_desc = {
.ops = &dummy_ops,
};
-static int __devinit dummy_regulator_probe(struct platform_device *pdev)
+static int dummy_regulator_probe(struct platform_device *pdev)
{
struct regulator_config config = { };
int ret;
diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c
index 339f4d732e9..9165b0c40ed 100644
--- a/drivers/regulator/fan53555.c
+++ b/drivers/regulator/fan53555.c
@@ -230,7 +230,7 @@ static struct regmap_config fan53555_regmap_config = {
.val_bits = 8,
};
-static int __devinit fan53555_regulator_probe(struct i2c_client *client,
+static int fan53555_regulator_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct fan53555_device_info *di;
@@ -293,7 +293,7 @@ static int __devinit fan53555_regulator_probe(struct i2c_client *client,
}
-static int __devexit fan53555_regulator_remove(struct i2c_client *client)
+static int fan53555_regulator_remove(struct i2c_client *client)
{
struct fan53555_device_info *di = i2c_get_clientdata(client);
@@ -311,7 +311,7 @@ static struct i2c_driver fan53555_regulator_driver = {
.name = "fan53555-regulator",
},
.probe = fan53555_regulator_probe,
- .remove = __devexit_p(fan53555_regulator_remove),
+ .remove = fan53555_regulator_remove,
.id_table = fan53555_id,
};
diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c
index 185468c4d38..48d5b7608b0 100644
--- a/drivers/regulator/fixed.c
+++ b/drivers/regulator/fixed.c
@@ -134,7 +134,7 @@ static struct regulator_ops fixed_voltage_ops = {
.list_voltage = fixed_voltage_list_voltage,
};
-static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev)
+static int reg_fixed_voltage_probe(struct platform_device *pdev)
{
struct fixed_voltage_config *config;
struct fixed_voltage_data *drvdata;
@@ -234,7 +234,7 @@ err:
return ret;
}
-static int __devexit reg_fixed_voltage_remove(struct platform_device *pdev)
+static int reg_fixed_voltage_remove(struct platform_device *pdev)
{
struct fixed_voltage_data *drvdata = platform_get_drvdata(pdev);
@@ -255,7 +255,7 @@ MODULE_DEVICE_TABLE(of, fixed_of_match);
static struct platform_driver regulator_fixed_voltage_driver = {
.probe = reg_fixed_voltage_probe,
- .remove = __devexit_p(reg_fixed_voltage_remove),
+ .remove = reg_fixed_voltage_remove,
.driver = {
.name = "reg-fixed-voltage",
.owner = THIS_MODULE,
diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c
index 8b5944f2d7d..8ae288fc150 100644
--- a/drivers/regulator/gpio-regulator.c
+++ b/drivers/regulator/gpio-regulator.c
@@ -28,9 +28,12 @@
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
#include <linux/regulator/gpio-regulator.h>
#include <linux/gpio.h>
#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
struct gpio_regulator_data {
struct regulator_desc desc;
@@ -79,7 +82,7 @@ static int gpio_regulator_set_voltage(struct regulator_dev *dev,
for (ptr = 0; ptr < data->nr_gpios; ptr++) {
state = (target & (1 << ptr)) >> ptr;
- gpio_set_value(data->gpios[ptr].gpio, state);
+ gpio_set_value_cansleep(data->gpios[ptr].gpio, state);
}
data->state = target;
@@ -116,7 +119,7 @@ static int gpio_regulator_set_current_limit(struct regulator_dev *dev,
for (ptr = 0; ptr < data->nr_gpios; ptr++) {
state = (target & (1 << ptr)) >> ptr;
- gpio_set_value(data->gpios[ptr].gpio, state);
+ gpio_set_value_cansleep(data->gpios[ptr].gpio, state);
}
data->state = target;
@@ -129,18 +132,108 @@ static struct regulator_ops gpio_regulator_voltage_ops = {
.list_voltage = gpio_regulator_list_voltage,
};
+struct gpio_regulator_config *
+of_get_gpio_regulator_config(struct device *dev, struct device_node *np)
+{
+ struct gpio_regulator_config *config;
+ struct property *prop;
+ const char *regtype;
+ int proplen, gpio, i;
+
+ config = devm_kzalloc(dev,
+ sizeof(struct gpio_regulator_config),
+ GFP_KERNEL);
+ if (!config)
+ return ERR_PTR(-ENOMEM);
+
+ config->init_data = of_get_regulator_init_data(dev, np);
+ if (!config->init_data)
+ return ERR_PTR(-EINVAL);
+
+ config->supply_name = config->init_data->constraints.name;
+
+ if (of_property_read_bool(np, "enable-active-high"))
+ config->enable_high = true;
+
+ if (of_property_read_bool(np, "enable-at-boot"))
+ config->enabled_at_boot = true;
+
+ of_property_read_u32(np, "startup-delay-us", &config->startup_delay);
+
+ config->enable_gpio = of_get_named_gpio(np, "enable-gpio", 0);
+
+ /* Fetch GPIOs. */
+ for (i = 0; ; i++)
+ if (of_get_named_gpio(np, "gpios", i) < 0)
+ break;
+ config->nr_gpios = i;
+
+ config->gpios = devm_kzalloc(dev,
+ sizeof(struct gpio) * config->nr_gpios,
+ GFP_KERNEL);
+ if (!config->gpios)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < config->nr_gpios; i++) {
+ gpio = of_get_named_gpio(np, "gpios", i);
+ if (gpio < 0)
+ break;
+ config->gpios[i].gpio = gpio;
+ }
+
+ /* Fetch states. */
+ prop = of_find_property(np, "states", NULL);
+ if (!prop) {
+ dev_err(dev, "No 'states' property found\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ proplen = prop->length / sizeof(int);
+
+ config->states = devm_kzalloc(dev,
+ sizeof(struct gpio_regulator_state)
+ * (proplen / 2),
+ GFP_KERNEL);
+ if (!config->states)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < proplen / 2; i++) {
+ config->states[i].value =
+ be32_to_cpup((int *)prop->value + (i * 2));
+ config->states[i].gpios =
+ be32_to_cpup((int *)prop->value + (i * 2 + 1));
+ }
+ config->nr_states = i;
+
+ of_property_read_string(np, "regulator-type", &regtype);
+
+ if (!strncmp("voltage", regtype, 7))
+ config->type = REGULATOR_VOLTAGE;
+ else if (!strncmp("current", regtype, 7))
+ config->type = REGULATOR_CURRENT;
+
+ return config;
+}
+
static struct regulator_ops gpio_regulator_current_ops = {
.get_current_limit = gpio_regulator_get_value,
.set_current_limit = gpio_regulator_set_current_limit,
};
-static int __devinit gpio_regulator_probe(struct platform_device *pdev)
+static int gpio_regulator_probe(struct platform_device *pdev)
{
struct gpio_regulator_config *config = pdev->dev.platform_data;
+ struct device_node *np = pdev->dev.of_node;
struct gpio_regulator_data *drvdata;
struct regulator_config cfg = { };
int ptr, ret, state;
+ if (np) {
+ config = of_get_gpio_regulator_config(&pdev->dev, np);
+ if (IS_ERR(config))
+ return PTR_ERR(config);
+ }
+
drvdata = devm_kzalloc(&pdev->dev, sizeof(struct gpio_regulator_data),
GFP_KERNEL);
if (drvdata == NULL) {
@@ -215,6 +308,7 @@ static int __devinit gpio_regulator_probe(struct platform_device *pdev)
cfg.dev = &pdev->dev;
cfg.init_data = config->init_data;
cfg.driver_data = drvdata;
+ cfg.of_node = np;
if (config->enable_gpio >= 0)
cfg.ena_gpio = config->enable_gpio;
@@ -254,7 +348,7 @@ err:
return ret;
}
-static int __devexit gpio_regulator_remove(struct platform_device *pdev)
+static int gpio_regulator_remove(struct platform_device *pdev)
{
struct gpio_regulator_data *drvdata = platform_get_drvdata(pdev);
@@ -270,12 +364,20 @@ static int __devexit gpio_regulator_remove(struct platform_device *pdev)
return 0;
}
+#if defined(CONFIG_OF)
+static const struct of_device_id regulator_gpio_of_match[] __devinitconst = {
+ { .compatible = "regulator-gpio", },
+ {},
+};
+#endif
+
static struct platform_driver gpio_regulator_driver = {
.probe = gpio_regulator_probe,
- .remove = __devexit_p(gpio_regulator_remove),
+ .remove = gpio_regulator_remove,
.driver = {
.name = "gpio-regulator",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(regulator_gpio_of_match),
},
};
diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c
index d8ecf49a577..d1e5bee2a26 100644
--- a/drivers/regulator/isl6271a-regulator.c
+++ b/drivers/regulator/isl6271a-regulator.c
@@ -106,7 +106,7 @@ static const struct regulator_desc isl_rd[] = {
},
};
-static int __devinit isl6271a_probe(struct i2c_client *i2c,
+static int isl6271a_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct regulator_config config = { };
@@ -151,7 +151,7 @@ error:
return err;
}
-static int __devexit isl6271a_remove(struct i2c_client *i2c)
+static int isl6271a_remove(struct i2c_client *i2c)
{
struct isl_pmic *pmic = i2c_get_clientdata(i2c);
int i;
@@ -174,7 +174,7 @@ static struct i2c_driver isl6271a_i2c_driver = {
.owner = THIS_MODULE,
},
.probe = isl6271a_probe,
- .remove = __devexit_p(isl6271a_remove),
+ .remove = isl6271a_remove,
.id_table = isl6271a_id,
};
diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c
index 7c6e3b8ff48..5f68ff11a29 100644
--- a/drivers/regulator/lp3971.c
+++ b/drivers/regulator/lp3971.c
@@ -386,7 +386,7 @@ static int lp3971_set_bits(struct lp3971 *lp3971, u8 reg, u16 mask, u16 val)
return ret;
}
-static int __devinit setup_regulators(struct lp3971 *lp3971,
+static int setup_regulators(struct lp3971 *lp3971,
struct lp3971_platform_data *pdata)
{
int i, err;
@@ -429,7 +429,7 @@ err_nomem:
return err;
}
-static int __devinit lp3971_i2c_probe(struct i2c_client *i2c,
+static int lp3971_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct lp3971 *lp3971;
@@ -472,7 +472,7 @@ err_detect:
return ret;
}
-static int __devexit lp3971_i2c_remove(struct i2c_client *i2c)
+static int lp3971_i2c_remove(struct i2c_client *i2c)
{
struct lp3971 *lp3971 = i2c_get_clientdata(i2c);
int i;
@@ -498,7 +498,7 @@ static struct i2c_driver lp3971_i2c_driver = {
.owner = THIS_MODULE,
},
.probe = lp3971_i2c_probe,
- .remove = __devexit_p(lp3971_i2c_remove),
+ .remove = lp3971_i2c_remove,
.id_table = lp3971_i2c_id,
};
diff --git a/drivers/regulator/lp3972.c b/drivers/regulator/lp3972.c
index 3cdc755d9b2..69c42c318b8 100644
--- a/drivers/regulator/lp3972.c
+++ b/drivers/regulator/lp3972.c
@@ -481,7 +481,7 @@ static const struct regulator_desc regulators[] = {
},
};
-static int __devinit setup_regulators(struct lp3972 *lp3972,
+static int setup_regulators(struct lp3972 *lp3972,
struct lp3972_platform_data *pdata)
{
int i, err;
@@ -523,7 +523,7 @@ err_nomem:
return err;
}
-static int __devinit lp3972_i2c_probe(struct i2c_client *i2c,
+static int lp3972_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct lp3972 *lp3972;
@@ -569,7 +569,7 @@ err_detect:
return ret;
}
-static int __devexit lp3972_i2c_remove(struct i2c_client *i2c)
+static int lp3972_i2c_remove(struct i2c_client *i2c)
{
struct lp3972 *lp3972 = i2c_get_clientdata(i2c);
int i;
@@ -594,7 +594,7 @@ static struct i2c_driver lp3972_i2c_driver = {
.owner = THIS_MODULE,
},
.probe = lp3972_i2c_probe,
- .remove = __devexit_p(lp3972_i2c_remove),
+ .remove = lp3972_i2c_remove,
.id_table = lp3972_i2c_id,
};
diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c
index 708f4b6a17d..9289ead715c 100644
--- a/drivers/regulator/lp872x.c
+++ b/drivers/regulator/lp872x.c
@@ -893,7 +893,7 @@ err_dev:
return ret;
}
-static int __devexit lp872x_remove(struct i2c_client *cl)
+static int lp872x_remove(struct i2c_client *cl)
{
struct lp872x *lp = i2c_get_clientdata(cl);
@@ -914,7 +914,7 @@ static struct i2c_driver lp872x_driver = {
.owner = THIS_MODULE,
},
.probe = lp872x_probe,
- .remove = __devexit_p(lp872x_remove),
+ .remove = lp872x_remove,
.id_table = lp872x_ids,
};
diff --git a/drivers/regulator/lp8788-buck.c b/drivers/regulator/lp8788-buck.c
index ba3e0aa402d..aef3f2b0c5e 100644
--- a/drivers/regulator/lp8788-buck.c
+++ b/drivers/regulator/lp8788-buck.c
@@ -429,18 +429,6 @@ static struct regulator_desc lp8788_buck_desc[] = {
},
};
-static int _gpio_request(struct lp8788_buck *buck, int gpio, char *name)
-{
- struct device *dev = buck->lp->dev;
-
- if (!gpio_is_valid(gpio)) {
- dev_err(dev, "invalid gpio: %d\n", gpio);
- return -EINVAL;
- }
-
- return devm_gpio_request_one(dev, gpio, DVS_LOW, name);
-}
-
static int lp8788_dvs_gpio_request(struct lp8788_buck *buck,
enum lp8788_buck_id id)
{
@@ -452,7 +440,8 @@ static int lp8788_dvs_gpio_request(struct lp8788_buck *buck,
switch (id) {
case BUCK1:
gpio = pdata->buck1_dvs->gpio;
- ret = _gpio_request(buck, gpio, b1_name);
+ ret = devm_gpio_request_one(buck->lp->dev, gpio, DVS_LOW,
+ b1_name);
if (ret)
return ret;
@@ -461,7 +450,8 @@ static int lp8788_dvs_gpio_request(struct lp8788_buck *buck,
case BUCK2:
for (i = 0 ; i < LP8788_NUM_BUCK2_DVS ; i++) {
gpio = pdata->buck2_dvs->gpio[i];
- ret = _gpio_request(buck, gpio, b2_name[i]);
+ ret = devm_gpio_request_one(buck->lp->dev, gpio,
+ DVS_LOW, b2_name[i]);
if (ret)
return ret;
}
@@ -504,7 +494,7 @@ set_default_dvs_mode:
default_dvs_mode[id]);
}
-static __devinit int lp8788_buck_probe(struct platform_device *pdev)
+static int lp8788_buck_probe(struct platform_device *pdev)
{
struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
int id = pdev->id;
@@ -542,7 +532,7 @@ static __devinit int lp8788_buck_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit lp8788_buck_remove(struct platform_device *pdev)
+static int lp8788_buck_remove(struct platform_device *pdev)
{
struct lp8788_buck *buck = platform_get_drvdata(pdev);
@@ -554,7 +544,7 @@ static int __devexit lp8788_buck_remove(struct platform_device *pdev)
static struct platform_driver lp8788_buck_driver = {
.probe = lp8788_buck_probe,
- .remove = __devexit_p(lp8788_buck_remove),
+ .remove = lp8788_buck_remove,
.driver = {
.name = LP8788_DEV_BUCK,
.owner = THIS_MODULE,
diff --git a/drivers/regulator/lp8788-ldo.c b/drivers/regulator/lp8788-ldo.c
index 6796eeb47dc..3792741708c 100644
--- a/drivers/regulator/lp8788-ldo.c
+++ b/drivers/regulator/lp8788-ldo.c
@@ -126,7 +126,7 @@ struct lp8788_ldo {
};
/* DLDO 1, 2, 3, 9 voltage table */
-const int lp8788_dldo1239_vtbl[] = {
+static const int lp8788_dldo1239_vtbl[] = {
1800000, 1900000, 2000000, 2100000, 2200000, 2300000, 2400000, 2500000,
2600000, 2700000, 2800000, 2900000, 3000000, 2850000, 2850000, 2850000,
2850000, 2850000, 2850000, 2850000, 2850000, 2850000, 2850000, 2850000,
@@ -662,14 +662,6 @@ static int lp8788_config_ldo_enable_mode(struct lp8788_ldo *ldo,
[EN_DLDO7] = LP8788_EN_SEL_DLDO7_M,
[EN_DLDO911] = LP8788_EN_SEL_DLDO911_M,
};
- u8 val[] = {
- [EN_ALDO1] = 0 << 5,
- [EN_ALDO234] = 0 << 4,
- [EN_ALDO5] = 0 << 3,
- [EN_ALDO7] = 0 << 2,
- [EN_DLDO7] = 0 << 1,
- [EN_DLDO911] = 0 << 0,
- };
switch (id) {
case DLDO7:
@@ -708,11 +700,10 @@ static int lp8788_config_ldo_enable_mode(struct lp8788_ldo *ldo,
return ret;
set_default_ldo_enable_mode:
- return lp8788_update_bits(lp, LP8788_EN_SEL, en_mask[enable_id],
- val[enable_id]);
+ return lp8788_update_bits(lp, LP8788_EN_SEL, en_mask[enable_id], 0);
}
-static __devinit int lp8788_dldo_probe(struct platform_device *pdev)
+static int lp8788_dldo_probe(struct platform_device *pdev)
{
struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
int id = pdev->id;
@@ -749,7 +740,7 @@ static __devinit int lp8788_dldo_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit lp8788_dldo_remove(struct platform_device *pdev)
+static int lp8788_dldo_remove(struct platform_device *pdev)
{
struct lp8788_ldo *ldo = platform_get_drvdata(pdev);
@@ -761,14 +752,14 @@ static int __devexit lp8788_dldo_remove(struct platform_device *pdev)
static struct platform_driver lp8788_dldo_driver = {
.probe = lp8788_dldo_probe,
- .remove = __devexit_p(lp8788_dldo_remove),
+ .remove = lp8788_dldo_remove,
.driver = {
.name = LP8788_DEV_DLDO,
.owner = THIS_MODULE,
},
};
-static __devinit int lp8788_aldo_probe(struct platform_device *pdev)
+static int lp8788_aldo_probe(struct platform_device *pdev)
{
struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
int id = pdev->id;
@@ -805,7 +796,7 @@ static __devinit int lp8788_aldo_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit lp8788_aldo_remove(struct platform_device *pdev)
+static int lp8788_aldo_remove(struct platform_device *pdev)
{
struct lp8788_ldo *ldo = platform_get_drvdata(pdev);
@@ -817,7 +808,7 @@ static int __devexit lp8788_aldo_remove(struct platform_device *pdev)
static struct platform_driver lp8788_aldo_driver = {
.probe = lp8788_aldo_probe,
- .remove = __devexit_p(lp8788_aldo_remove),
+ .remove = lp8788_aldo_remove,
.driver = {
.name = LP8788_DEV_ALDO,
.owner = THIS_MODULE,
diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c
index f67af3c1b96..8c5a54f541b 100644
--- a/drivers/regulator/max1586.c
+++ b/drivers/regulator/max1586.c
@@ -44,6 +44,9 @@ struct max1586_data {
unsigned int min_uV;
unsigned int max_uV;
+ unsigned int v3_curr_sel;
+ unsigned int v6_curr_sel;
+
struct regulator_dev *rdev[0];
};
@@ -63,31 +66,60 @@ static int v6_voltages_uv[] = { 1, 1800000, 2500000, 3000000 };
* R24 and R25=100kOhm as described in the data sheet.
* The gain is approximately: 1 + R24/R25 + R24/185.5kOhm
*/
+static int max1586_v3_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct max1586_data *max1586 = rdev_get_drvdata(rdev);
+
+ return max1586->v3_curr_sel;
+}
+
static int max1586_v3_set_voltage_sel(struct regulator_dev *rdev,
unsigned selector)
{
struct max1586_data *max1586 = rdev_get_drvdata(rdev);
struct i2c_client *client = max1586->client;
+ int ret;
u8 v3_prog;
dev_dbg(&client->dev, "changing voltage v3 to %dmv\n",
regulator_list_voltage_linear(rdev, selector) / 1000);
v3_prog = I2C_V3_SELECT | (u8) selector;
- return i2c_smbus_write_byte(client, v3_prog);
+ ret = i2c_smbus_write_byte(client, v3_prog);
+ if (ret)
+ return ret;
+
+ max1586->v3_curr_sel = selector;
+
+ return 0;
+}
+
+static int max1586_v6_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct max1586_data *max1586 = rdev_get_drvdata(rdev);
+
+ return max1586->v6_curr_sel;
}
static int max1586_v6_set_voltage_sel(struct regulator_dev *rdev,
unsigned int selector)
{
- struct i2c_client *client = rdev_get_drvdata(rdev);
+ struct max1586_data *max1586 = rdev_get_drvdata(rdev);
+ struct i2c_client *client = max1586->client;
u8 v6_prog;
+ int ret;
dev_dbg(&client->dev, "changing voltage v6 to %dmv\n",
rdev->desc->volt_table[selector] / 1000);
v6_prog = I2C_V6_SELECT | (u8) selector;
- return i2c_smbus_write_byte(client, v6_prog);
+ ret = i2c_smbus_write_byte(client, v6_prog);
+ if (ret)
+ return ret;
+
+ max1586->v6_curr_sel = selector;
+
+ return 0;
}
/*
@@ -95,12 +127,14 @@ static int max1586_v6_set_voltage_sel(struct regulator_dev *rdev,
* the set up value.
*/
static struct regulator_ops max1586_v3_ops = {
+ .get_voltage_sel = max1586_v3_get_voltage_sel,
.set_voltage_sel = max1586_v3_set_voltage_sel,
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
};
static struct regulator_ops max1586_v6_ops = {
+ .get_voltage_sel = max1586_v6_get_voltage_sel,
.set_voltage_sel = max1586_v6_set_voltage_sel,
.list_voltage = regulator_list_voltage_table,
};
@@ -125,7 +159,7 @@ static struct regulator_desc max1586_reg[] = {
},
};
-static int __devinit max1586_pmic_probe(struct i2c_client *client,
+static int max1586_pmic_probe(struct i2c_client *client,
const struct i2c_device_id *i2c_id)
{
struct regulator_dev **rdev;
@@ -148,6 +182,10 @@ static int __devinit max1586_pmic_probe(struct i2c_client *client,
max1586->min_uV = MAX1586_V3_MIN_UV / 1000 * pdata->v3_gain / 1000;
max1586->max_uV = MAX1586_V3_MAX_UV / 1000 * pdata->v3_gain / 1000;
+ /* Set curr_sel to default voltage on power-up */
+ max1586->v3_curr_sel = 24; /* 1.3V */
+ max1586->v6_curr_sel = 0;
+
rdev = max1586->rdev;
for (i = 0; i < pdata->num_subdevs && i <= MAX1586_V6; i++) {
id = pdata->subdevs[i].id;
@@ -188,7 +226,7 @@ err:
return ret;
}
-static int __devexit max1586_pmic_remove(struct i2c_client *client)
+static int max1586_pmic_remove(struct i2c_client *client)
{
struct max1586_data *max1586 = i2c_get_clientdata(client);
int i;
@@ -207,7 +245,7 @@ MODULE_DEVICE_TABLE(i2c, max1586_id);
static struct i2c_driver max1586_pmic_driver = {
.probe = max1586_pmic_probe,
- .remove = __devexit_p(max1586_pmic_remove),
+ .remove = max1586_pmic_remove,
.driver = {
.name = "max1586",
.owner = THIS_MODULE,
diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c
index 2a67d08658a..b85040caaea 100644
--- a/drivers/regulator/max77686.c
+++ b/drivers/regulator/max77686.c
@@ -67,8 +67,94 @@ enum max77686_ramp_rate {
struct max77686_data {
struct regulator_dev *rdev[MAX77686_REGULATORS];
+ unsigned int opmode[MAX77686_REGULATORS];
};
+/* Some BUCKS supports Normal[ON/OFF] mode during suspend */
+static int max77686_buck_set_suspend_disable(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+
+ if (rdev->desc->id == MAX77686_BUCK1)
+ val = 0x1;
+ else
+ val = 0x1 << MAX77686_OPMODE_BUCK234_SHIFT;
+
+ max77686->opmode[rdev->desc->id] = val;
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask,
+ val);
+}
+
+/* Some LDOs supports [LPM/Normal]ON mode during suspend state */
+static int max77686_set_suspend_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+ unsigned int val;
+
+ /* BUCK[5-9] doesn't support this feature */
+ if (rdev->desc->id >= MAX77686_BUCK5)
+ return 0;
+
+ switch (mode) {
+ case REGULATOR_MODE_IDLE: /* ON in LP Mode */
+ val = 0x2 << MAX77686_OPMODE_SHIFT;
+ break;
+ case REGULATOR_MODE_NORMAL: /* ON in Normal Mode */
+ val = 0x3 << MAX77686_OPMODE_SHIFT;
+ break;
+ default:
+ pr_warn("%s: regulator_suspend_mode : 0x%x not supported\n",
+ rdev->desc->name, mode);
+ return -EINVAL;
+ }
+
+ max77686->opmode[rdev->desc->id] = val;
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask,
+ val);
+}
+
+/* Some LDOs supports LPM-ON/OFF/Normal-ON mode during suspend state */
+static int max77686_ldo_set_suspend_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ unsigned int val;
+ struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+
+ switch (mode) {
+ case REGULATOR_MODE_STANDBY: /* switch off */
+ val = 0x1 << MAX77686_OPMODE_SHIFT;
+ break;
+ case REGULATOR_MODE_IDLE: /* ON in LP Mode */
+ val = 0x2 << MAX77686_OPMODE_SHIFT;
+ break;
+ case REGULATOR_MODE_NORMAL: /* ON in Normal Mode */
+ val = 0x3 << MAX77686_OPMODE_SHIFT;
+ break;
+ default:
+ pr_warn("%s: regulator_suspend_mode : 0x%x not supported\n",
+ rdev->desc->name, mode);
+ return -EINVAL;
+ }
+
+ max77686->opmode[rdev->desc->id] = val;
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask,
+ val);
+}
+
+static int max77686_enable(struct regulator_dev *rdev)
+{
+ struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask,
+ max77686->opmode[rdev->desc->id]);
+}
+
static int max77686_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
{
unsigned int ramp_value = RAMP_RATE_NO_CTRL;
@@ -98,23 +184,49 @@ static struct regulator_ops max77686_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
- .enable = regulator_enable_regmap,
+ .enable = max77686_enable,
+ .disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_suspend_mode = max77686_set_suspend_mode,
+};
+
+static struct regulator_ops max77686_ldo_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = max77686_enable,
.disable = regulator_disable_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_suspend_mode = max77686_ldo_set_suspend_mode,
+};
+
+static struct regulator_ops max77686_buck1_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = max77686_enable,
+ .disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_suspend_disable = max77686_buck_set_suspend_disable,
};
static struct regulator_ops max77686_buck_dvs_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
- .enable = regulator_enable_regmap,
+ .enable = max77686_enable,
.disable = regulator_disable_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
.set_ramp_delay = max77686_set_ramp_delay,
+ .set_suspend_disable = max77686_buck_set_suspend_disable,
};
#define regulator_desc_ldo(num) { \
@@ -133,9 +245,41 @@ static struct regulator_ops max77686_buck_dvs_ops = {
.enable_mask = MAX77686_OPMODE_MASK \
<< MAX77686_OPMODE_SHIFT, \
}
+#define regulator_desc_lpm_ldo(num) { \
+ .name = "LDO"#num, \
+ .id = MAX77686_LDO##num, \
+ .ops = &max77686_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = MAX77686_LDO_MINUV, \
+ .uV_step = MAX77686_LDO_UVSTEP, \
+ .ramp_delay = MAX77686_RAMP_DELAY, \
+ .n_voltages = MAX77686_VSEL_MASK + 1, \
+ .vsel_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \
+ .vsel_mask = MAX77686_VSEL_MASK, \
+ .enable_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \
+ .enable_mask = MAX77686_OPMODE_MASK \
+ << MAX77686_OPMODE_SHIFT, \
+}
#define regulator_desc_ldo_low(num) { \
.name = "LDO"#num, \
.id = MAX77686_LDO##num, \
+ .ops = &max77686_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = MAX77686_LDO_LOW_MINUV, \
+ .uV_step = MAX77686_LDO_LOW_UVSTEP, \
+ .ramp_delay = MAX77686_RAMP_DELAY, \
+ .n_voltages = MAX77686_VSEL_MASK + 1, \
+ .vsel_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \
+ .vsel_mask = MAX77686_VSEL_MASK, \
+ .enable_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \
+ .enable_mask = MAX77686_OPMODE_MASK \
+ << MAX77686_OPMODE_SHIFT, \
+}
+#define regulator_desc_ldo1_low(num) { \
+ .name = "LDO"#num, \
+ .id = MAX77686_LDO##num, \
.ops = &max77686_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -167,7 +311,7 @@ static struct regulator_ops max77686_buck_dvs_ops = {
#define regulator_desc_buck1(num) { \
.name = "BUCK"#num, \
.id = MAX77686_BUCK##num, \
- .ops = &max77686_ops, \
+ .ops = &max77686_buck1_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
.min_uV = MAX77686_BUCK_MINUV, \
@@ -197,7 +341,7 @@ static struct regulator_ops max77686_buck_dvs_ops = {
}
static struct regulator_desc regulators[] = {
- regulator_desc_ldo_low(1),
+ regulator_desc_ldo1_low(1),
regulator_desc_ldo_low(2),
regulator_desc_ldo(3),
regulator_desc_ldo(4),
@@ -206,13 +350,13 @@ static struct regulator_desc regulators[] = {
regulator_desc_ldo_low(7),
regulator_desc_ldo_low(8),
regulator_desc_ldo(9),
- regulator_desc_ldo(10),
- regulator_desc_ldo(11),
- regulator_desc_ldo(12),
+ regulator_desc_lpm_ldo(10),
+ regulator_desc_lpm_ldo(11),
+ regulator_desc_lpm_ldo(12),
regulator_desc_ldo(13),
- regulator_desc_ldo(14),
+ regulator_desc_lpm_ldo(14),
regulator_desc_ldo_low(15),
- regulator_desc_ldo(16),
+ regulator_desc_lpm_ldo(16),
regulator_desc_ldo(17),
regulator_desc_ldo(18),
regulator_desc_ldo(19),
@@ -280,7 +424,7 @@ static int max77686_pmic_dt_parse_pdata(struct max77686_dev *iodev,
}
#endif /* CONFIG_OF */
-static __devinit int max77686_pmic_probe(struct platform_device *pdev)
+static int max77686_pmic_probe(struct platform_device *pdev)
{
struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
struct max77686_platform_data *pdata = dev_get_platdata(iodev->dev);
@@ -314,12 +458,14 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev)
config.dev = &pdev->dev;
config.regmap = iodev->regmap;
+ config.driver_data = max77686;
platform_set_drvdata(pdev, max77686);
for (i = 0; i < MAX77686_REGULATORS; i++) {
config.init_data = pdata->regulators[i].initdata;
config.of_node = pdata->regulators[i].of_node;
+ max77686->opmode[i] = regulators[i].enable_mask;
max77686->rdev[i] = regulator_register(&regulators[i], &config);
if (IS_ERR(max77686->rdev[i])) {
ret = PTR_ERR(max77686->rdev[i]);
@@ -337,7 +483,7 @@ err:
return ret;
}
-static int __devexit max77686_pmic_remove(struct platform_device *pdev)
+static int max77686_pmic_remove(struct platform_device *pdev)
{
struct max77686_data *max77686 = platform_get_drvdata(pdev);
int i;
@@ -360,7 +506,7 @@ static struct platform_driver max77686_pmic_driver = {
.owner = THIS_MODULE,
},
.probe = max77686_pmic_probe,
- .remove = __devexit_p(max77686_pmic_remove),
+ .remove = max77686_pmic_remove,
.id_table = max77686_pmic_id,
};
diff --git a/drivers/regulator/max8649.c b/drivers/regulator/max8649.c
index 9d540cd02da..3ca14380f22 100644
--- a/drivers/regulator/max8649.c
+++ b/drivers/regulator/max8649.c
@@ -176,7 +176,7 @@ static struct regmap_config max8649_regmap_config = {
.val_bits = 8,
};
-static int __devinit max8649_regulator_probe(struct i2c_client *client,
+static int max8649_regulator_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct max8649_platform_data *pdata = client->dev.platform_data;
@@ -271,7 +271,7 @@ static int __devinit max8649_regulator_probe(struct i2c_client *client,
return 0;
}
-static int __devexit max8649_regulator_remove(struct i2c_client *client)
+static int max8649_regulator_remove(struct i2c_client *client)
{
struct max8649_regulator_info *info = i2c_get_clientdata(client);
@@ -291,7 +291,7 @@ MODULE_DEVICE_TABLE(i2c, max8649_id);
static struct i2c_driver max8649_driver = {
.probe = max8649_regulator_probe,
- .remove = __devexit_p(max8649_regulator_remove),
+ .remove = max8649_regulator_remove,
.driver = {
.name = "max8649",
},
diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c
index 8d531742f59..4d7c635c36c 100644
--- a/drivers/regulator/max8660.c
+++ b/drivers/regulator/max8660.c
@@ -305,7 +305,7 @@ static const struct regulator_desc max8660_reg[] = {
},
};
-static int __devinit max8660_probe(struct i2c_client *client,
+static int max8660_probe(struct i2c_client *client,
const struct i2c_device_id *i2c_id)
{
struct regulator_dev **rdev;
@@ -420,7 +420,7 @@ err_out:
return ret;
}
-static int __devexit max8660_remove(struct i2c_client *client)
+static int max8660_remove(struct i2c_client *client)
{
struct max8660 *max8660 = i2c_get_clientdata(client);
int i;
@@ -440,7 +440,7 @@ MODULE_DEVICE_TABLE(i2c, max8660_id);
static struct i2c_driver max8660_driver = {
.probe = max8660_probe,
- .remove = __devexit_p(max8660_remove),
+ .remove = max8660_remove,
.driver = {
.name = "max8660",
.owner = THIS_MODULE,
diff --git a/drivers/regulator/max8907-regulator.c b/drivers/regulator/max8907-regulator.c
index af7607515ab..d1a77512d83 100644
--- a/drivers/regulator/max8907-regulator.c
+++ b/drivers/regulator/max8907-regulator.c
@@ -275,7 +275,7 @@ static inline struct device_node *match_of_node(int index)
}
#endif
-static __devinit int max8907_regulator_probe(struct platform_device *pdev)
+static int max8907_regulator_probe(struct platform_device *pdev)
{
struct max8907 *max8907 = dev_get_drvdata(pdev->dev.parent);
struct max8907_platform_data *pdata = dev_get_platdata(max8907->dev);
@@ -368,7 +368,7 @@ err_unregister_regulator:
return ret;
}
-static __devexit int max8907_regulator_remove(struct platform_device *pdev)
+static int max8907_regulator_remove(struct platform_device *pdev)
{
struct max8907_regulator *pmic = platform_get_drvdata(pdev);
int i;
@@ -385,7 +385,7 @@ static struct platform_driver max8907_regulator_driver = {
.owner = THIS_MODULE,
},
.probe = max8907_regulator_probe,
- .remove = __devexit_p(max8907_regulator_remove),
+ .remove = max8907_regulator_remove,
};
static int __init max8907_regulator_init(void)
diff --git a/drivers/regulator/max8925-regulator.c b/drivers/regulator/max8925-regulator.c
index 9bb0be37495..446a8544555 100644
--- a/drivers/regulator/max8925-regulator.c
+++ b/drivers/regulator/max8925-regulator.c
@@ -17,6 +17,8 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/mfd/max8925.h>
+#include <linux/of.h>
+#include <linux/regulator/of_regulator.h>
#define SD1_DVM_VMIN 850000
#define SD1_DVM_VMAX 1000000
@@ -187,6 +189,34 @@ static struct regulator_ops max8925_regulator_ldo_ops = {
.enable_reg = MAX8925_LDOCTL##_id, \
}
+#ifdef CONFIG_OF
+static struct of_regulator_match max8925_regulator_matches[] = {
+ { .name = "SDV1",},
+ { .name = "SDV2",},
+ { .name = "SDV3",},
+ { .name = "LDO1",},
+ { .name = "LDO2",},
+ { .name = "LDO3",},
+ { .name = "LDO4",},
+ { .name = "LDO5",},
+ { .name = "LDO6",},
+ { .name = "LDO7",},
+ { .name = "LDO8",},
+ { .name = "LDO9",},
+ { .name = "LDO10",},
+ { .name = "LDO11",},
+ { .name = "LDO12",},
+ { .name = "LDO13",},
+ { .name = "LDO14",},
+ { .name = "LDO15",},
+ { .name = "LDO16",},
+ { .name = "LDO17",},
+ { .name = "LDO18",},
+ { .name = "LDO19",},
+ { .name = "LDO20",},
+};
+#endif
+
static struct max8925_regulator_info max8925_regulator_info[] = {
MAX8925_SDV(1, 637.5, 1425, 12.5),
MAX8925_SDV(2, 650, 2225, 25),
@@ -214,7 +244,37 @@ static struct max8925_regulator_info max8925_regulator_info[] = {
MAX8925_LDO(20, 750, 3900, 50),
};
-static int __devinit max8925_regulator_probe(struct platform_device *pdev)
+#ifdef CONFIG_OF
+static int max8925_regulator_dt_init(struct platform_device *pdev,
+ struct max8925_regulator_info *info,
+ struct regulator_config *config,
+ int ridx)
+{
+ struct device_node *nproot, *np;
+ int rcount;
+ nproot = pdev->dev.parent->of_node;
+ if (!nproot)
+ return -ENODEV;
+ np = of_find_node_by_name(nproot, "regulators");
+ if (!np) {
+ dev_err(&pdev->dev, "failed to find regulators node\n");
+ return -ENODEV;
+ }
+
+ rcount = of_regulator_match(&pdev->dev, np,
+ &max8925_regulator_matches[ridx], 1);
+ if (rcount < 0)
+ return -ENODEV;
+ config->init_data = max8925_regulator_matches[ridx].init_data;
+ config->of_node = max8925_regulator_matches[ridx].of_node;
+
+ return 0;
+}
+#else
+#define max8925_regulator_dt_init(w, x, y, z) (-1)
+#endif
+
+static int max8925_regulator_probe(struct platform_device *pdev)
{
struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct regulator_init_data *pdata = pdev->dev.platform_data;
@@ -222,7 +282,7 @@ static int __devinit max8925_regulator_probe(struct platform_device *pdev)
struct max8925_regulator_info *ri;
struct resource *res;
struct regulator_dev *rdev;
- int i;
+ int i, regulator_idx;
res = platform_get_resource(pdev, IORESOURCE_REG, 0);
if (!res) {
@@ -231,9 +291,12 @@ static int __devinit max8925_regulator_probe(struct platform_device *pdev)
}
for (i = 0; i < ARRAY_SIZE(max8925_regulator_info); i++) {
ri = &max8925_regulator_info[i];
- if (ri->vol_reg == res->start)
+ if (ri->vol_reg == res->start) {
+ regulator_idx = i;
break;
+ }
}
+
if (i == ARRAY_SIZE(max8925_regulator_info)) {
dev_err(&pdev->dev, "Failed to find regulator %llu\n",
(unsigned long long)res->start);
@@ -243,9 +306,12 @@ static int __devinit max8925_regulator_probe(struct platform_device *pdev)
ri->chip = chip;
config.dev = &pdev->dev;
- config.init_data = pdata;
config.driver_data = ri;
+ if (max8925_regulator_dt_init(pdev, ri, &config, regulator_idx))
+ if (pdata)
+ config.init_data = pdata;
+
rdev = regulator_register(&ri->desc, &config);
if (IS_ERR(rdev)) {
dev_err(&pdev->dev, "failed to register regulator %s\n",
@@ -257,7 +323,7 @@ static int __devinit max8925_regulator_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit max8925_regulator_remove(struct platform_device *pdev)
+static int max8925_regulator_remove(struct platform_device *pdev)
{
struct regulator_dev *rdev = platform_get_drvdata(pdev);
@@ -273,7 +339,7 @@ static struct platform_driver max8925_regulator_driver = {
.owner = THIS_MODULE,
},
.probe = max8925_regulator_probe,
- .remove = __devexit_p(max8925_regulator_remove),
+ .remove = max8925_regulator_remove,
};
static int __init max8925_regulator_init(void)
diff --git a/drivers/regulator/max8952.c b/drivers/regulator/max8952.c
index 355ca7bad9d..fc7935a19e3 100644
--- a/drivers/regulator/max8952.c
+++ b/drivers/regulator/max8952.c
@@ -126,7 +126,7 @@ static const struct regulator_desc regulator = {
.owner = THIS_MODULE,
};
-static int __devinit max8952_pmic_probe(struct i2c_client *client,
+static int max8952_pmic_probe(struct i2c_client *client,
const struct i2c_device_id *i2c_id)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
@@ -247,7 +247,7 @@ static int __devinit max8952_pmic_probe(struct i2c_client *client,
return 0;
}
-static int __devexit max8952_pmic_remove(struct i2c_client *client)
+static int max8952_pmic_remove(struct i2c_client *client)
{
struct max8952_data *max8952 = i2c_get_clientdata(client);
struct max8952_platform_data *pdata = max8952->pdata;
@@ -268,7 +268,7 @@ MODULE_DEVICE_TABLE(i2c, max8952_ids);
static struct i2c_driver max8952_pmic_driver = {
.probe = max8952_pmic_probe,
- .remove = __devexit_p(max8952_pmic_remove),
+ .remove = max8952_pmic_remove,
.driver = {
.name = "max8952",
},
diff --git a/drivers/regulator/max8973-regulator.c b/drivers/regulator/max8973-regulator.c
new file mode 100644
index 00000000000..3ee26387b12
--- /dev/null
+++ b/drivers/regulator/max8973-regulator.c
@@ -0,0 +1,505 @@
+/*
+ * max8973-regulator.c -- Maxim max8973
+ *
+ * Regulator driver for MAXIM 8973 DC-DC step-down switching regulator.
+ *
+ * Copyright (c) 2012, NVIDIA Corporation.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * 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/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/max8973-regulator.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+
+/* Register definitions */
+#define MAX8973_VOUT 0x0
+#define MAX8973_VOUT_DVS 0x1
+#define MAX8973_CONTROL1 0x2
+#define MAX8973_CONTROL2 0x3
+#define MAX8973_CHIPID1 0x4
+#define MAX8973_CHIPID2 0x5
+
+#define MAX8973_MAX_VOUT_REG 2
+
+/* MAX8973_VOUT */
+#define MAX8973_VOUT_ENABLE BIT(7)
+#define MAX8973_VOUT_MASK 0x7F
+
+/* MAX8973_VOUT_DVS */
+#define MAX8973_DVS_VOUT_MASK 0x7F
+
+/* MAX8973_CONTROL1 */
+#define MAX8973_SNS_ENABLE BIT(7)
+#define MAX8973_FPWM_EN_M BIT(6)
+#define MAX8973_NFSR_ENABLE BIT(5)
+#define MAX8973_AD_ENABLE BIT(4)
+#define MAX8973_BIAS_ENABLE BIT(3)
+#define MAX8973_FREQSHIFT_9PER BIT(2)
+
+#define MAX8973_RAMP_12mV_PER_US 0x0
+#define MAX8973_RAMP_25mV_PER_US 0x1
+#define MAX8973_RAMP_50mV_PER_US 0x2
+#define MAX8973_RAMP_200mV_PER_US 0x3
+
+/* MAX8973_CONTROL2 */
+#define MAX8973_WDTMR_ENABLE BIT(6)
+#define MAX8973_DISCH_ENBABLE BIT(5)
+#define MAX8973_FT_ENABLE BIT(4)
+
+#define MAX8973_CKKADV_TRIP_DISABLE 0xC
+#define MAX8973_CKKADV_TRIP_75mV_PER_US 0x0
+#define MAX8973_CKKADV_TRIP_150mV_PER_US 0x4
+#define MAX8973_CKKADV_TRIP_75mV_PER_US_HIST_DIS 0x8
+#define MAX8973_CONTROL_CLKADV_TRIP_MASK 0x00030000
+
+#define MAX8973_INDUCTOR_MIN_30_PER 0x0
+#define MAX8973_INDUCTOR_NOMINAL 0x1
+#define MAX8973_INDUCTOR_PLUS_30_PER 0x2
+#define MAX8973_INDUCTOR_PLUS_60_PER 0x3
+#define MAX8973_CONTROL_INDUCTOR_VALUE_MASK 0x00300000
+
+#define MAX8973_MIN_VOLATGE 606250
+#define MAX8973_MAX_VOLATGE 1400000
+#define MAX8973_VOLATGE_STEP 6250
+#define MAX8973_BUCK_N_VOLTAGE 0x80
+
+/* Maxim 8973 chip information */
+struct max8973_chip {
+ struct device *dev;
+ struct regulator_desc desc;
+ struct regulator_dev *rdev;
+ struct regmap *regmap;
+ bool enable_external_control;
+ int dvs_gpio;
+ int lru_index[MAX8973_MAX_VOUT_REG];
+ int curr_vout_val[MAX8973_MAX_VOUT_REG];
+ int curr_vout_reg;
+ int curr_gpio_val;
+ bool valid_dvs_gpio;
+};
+
+/*
+ * find_voltage_set_register: Find new voltage configuration register (VOUT).
+ * The finding of the new VOUT register will be based on the LRU mechanism.
+ * Each VOUT register will have different voltage configured . This
+ * Function will look if any of the VOUT register have requested voltage set
+ * or not.
+ * - If it is already there then it will make that register as most
+ * recently used and return as found so that caller need not to set
+ * the VOUT register but need to set the proper gpios to select this
+ * VOUT register.
+ * - If requested voltage is not found then it will use the least
+ * recently mechanism to get new VOUT register for new configuration
+ * and will return not_found so that caller need to set new VOUT
+ * register and then gpios (both).
+ */
+static bool find_voltage_set_register(struct max8973_chip *tps,
+ int req_vsel, int *vout_reg, int *gpio_val)
+{
+ int i;
+ bool found = false;
+ int new_vout_reg = tps->lru_index[MAX8973_MAX_VOUT_REG - 1];
+ int found_index = MAX8973_MAX_VOUT_REG - 1;
+
+ for (i = 0; i < MAX8973_MAX_VOUT_REG; ++i) {
+ if (tps->curr_vout_val[tps->lru_index[i]] == req_vsel) {
+ new_vout_reg = tps->lru_index[i];
+ found_index = i;
+ found = true;
+ goto update_lru_index;
+ }
+ }
+
+update_lru_index:
+ for (i = found_index; i > 0; i--)
+ tps->lru_index[i] = tps->lru_index[i - 1];
+
+ tps->lru_index[0] = new_vout_reg;
+ *gpio_val = new_vout_reg;
+ *vout_reg = MAX8973_VOUT + new_vout_reg;
+ return found;
+}
+
+static int max8973_dcdc_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct max8973_chip *max = rdev_get_drvdata(rdev);
+ unsigned int data;
+ int ret;
+
+ ret = regmap_read(max->regmap, max->curr_vout_reg, &data);
+ if (ret < 0) {
+ dev_err(max->dev, "register %d read failed, err = %d\n",
+ max->curr_vout_reg, ret);
+ return ret;
+ }
+ return data & MAX8973_VOUT_MASK;
+}
+
+static int max8973_dcdc_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned vsel)
+{
+ struct max8973_chip *max = rdev_get_drvdata(rdev);
+ int ret;
+ bool found = false;
+ int vout_reg = max->curr_vout_reg;
+ int gpio_val = max->curr_gpio_val;
+
+ /*
+ * If gpios are available to select the VOUT register then least
+ * recently used register for new configuration.
+ */
+ if (max->valid_dvs_gpio)
+ found = find_voltage_set_register(max, vsel,
+ &vout_reg, &gpio_val);
+
+ if (!found) {
+ ret = regmap_update_bits(max->regmap, vout_reg,
+ MAX8973_VOUT_MASK, vsel);
+ if (ret < 0) {
+ dev_err(max->dev, "register %d update failed, err %d\n",
+ vout_reg, ret);
+ return ret;
+ }
+ max->curr_vout_reg = vout_reg;
+ max->curr_vout_val[gpio_val] = vsel;
+ }
+
+ /* Select proper VOUT register vio gpios */
+ if (max->valid_dvs_gpio) {
+ gpio_set_value_cansleep(max->dvs_gpio, gpio_val & 0x1);
+ max->curr_gpio_val = gpio_val;
+ }
+ return 0;
+}
+
+static int max8973_dcdc_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct max8973_chip *max = rdev_get_drvdata(rdev);
+ int ret;
+ int pwm;
+
+ /* Enable force PWM mode in FAST mode only. */
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ pwm = MAX8973_FPWM_EN_M;
+ break;
+
+ case REGULATOR_MODE_NORMAL:
+ pwm = 0;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(max->regmap, MAX8973_CONTROL1,
+ MAX8973_FPWM_EN_M, pwm);
+ if (ret < 0)
+ dev_err(max->dev, "register %d update failed, err %d\n",
+ MAX8973_CONTROL1, ret);
+ return ret;
+}
+
+static unsigned int max8973_dcdc_get_mode(struct regulator_dev *rdev)
+{
+ struct max8973_chip *max = rdev_get_drvdata(rdev);
+ unsigned int data;
+ int ret;
+
+ ret = regmap_read(max->regmap, MAX8973_CONTROL1, &data);
+ if (ret < 0) {
+ dev_err(max->dev, "register %d read failed, err %d\n",
+ MAX8973_CONTROL1, ret);
+ return ret;
+ }
+ return (data & MAX8973_FPWM_EN_M) ?
+ REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL;
+}
+
+static struct regulator_ops max8973_dcdc_ops = {
+ .get_voltage_sel = max8973_dcdc_get_voltage_sel,
+ .set_voltage_sel = max8973_dcdc_set_voltage_sel,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_mode = max8973_dcdc_set_mode,
+ .get_mode = max8973_dcdc_get_mode,
+};
+
+static int __devinit max8973_init_dcdc(struct max8973_chip *max,
+ struct max8973_regulator_platform_data *pdata)
+{
+ int ret;
+ uint8_t control1 = 0;
+ uint8_t control2 = 0;
+
+ if (pdata->control_flags & MAX8973_CONTROL_REMOTE_SENSE_ENABLE)
+ control1 |= MAX8973_SNS_ENABLE;
+
+ if (!(pdata->control_flags & MAX8973_CONTROL_FALLING_SLEW_RATE_ENABLE))
+ control1 |= MAX8973_NFSR_ENABLE;
+
+ if (pdata->control_flags & MAX8973_CONTROL_OUTPUT_ACTIVE_DISCH_ENABLE)
+ control1 |= MAX8973_AD_ENABLE;
+
+ if (pdata->control_flags & MAX8973_CONTROL_BIAS_ENABLE)
+ control1 |= MAX8973_BIAS_ENABLE;
+
+ if (pdata->control_flags & MAX8973_CONTROL_FREQ_SHIFT_9PER_ENABLE)
+ control1 |= MAX8973_FREQSHIFT_9PER;
+
+ /* Set ramp delay */
+ if (pdata->reg_init_data &&
+ pdata->reg_init_data->constraints.ramp_delay) {
+ if (pdata->reg_init_data->constraints.ramp_delay < 25000)
+ control1 = MAX8973_RAMP_12mV_PER_US;
+ else if (pdata->reg_init_data->constraints.ramp_delay < 50000)
+ control1 = MAX8973_RAMP_25mV_PER_US;
+ else if (pdata->reg_init_data->constraints.ramp_delay < 200000)
+ control1 = MAX8973_RAMP_50mV_PER_US;
+ else
+ control1 = MAX8973_RAMP_200mV_PER_US;
+ } else {
+ control1 = MAX8973_RAMP_12mV_PER_US;
+ max->desc.ramp_delay = 12500;
+ }
+
+ if (!(pdata->control_flags & MAX8973_CONTROL_PULL_DOWN_ENABLE))
+ control2 |= MAX8973_DISCH_ENBABLE;
+
+ /* Clock advance trip configuration */
+ switch (pdata->control_flags & MAX8973_CONTROL_CLKADV_TRIP_MASK) {
+ case MAX8973_CONTROL_CLKADV_TRIP_DISABLED:
+ control2 |= MAX8973_CKKADV_TRIP_DISABLE;
+ break;
+
+ case MAX8973_CONTROL_CLKADV_TRIP_75mV_PER_US:
+ control2 |= MAX8973_CKKADV_TRIP_75mV_PER_US;
+ break;
+
+ case MAX8973_CONTROL_CLKADV_TRIP_150mV_PER_US:
+ control2 |= MAX8973_CKKADV_TRIP_150mV_PER_US;
+ break;
+
+ case MAX8973_CONTROL_CLKADV_TRIP_75mV_PER_US_HIST_DIS:
+ control2 |= MAX8973_CKKADV_TRIP_75mV_PER_US_HIST_DIS;
+ break;
+ }
+
+ /* Configure inductor value */
+ switch (pdata->control_flags & MAX8973_CONTROL_INDUCTOR_VALUE_MASK) {
+ case MAX8973_CONTROL_INDUCTOR_VALUE_NOMINAL:
+ control2 |= MAX8973_INDUCTOR_NOMINAL;
+ break;
+
+ case MAX8973_CONTROL_INDUCTOR_VALUE_MINUS_30_PER:
+ control2 |= MAX8973_INDUCTOR_MIN_30_PER;
+ break;
+
+ case MAX8973_CONTROL_INDUCTOR_VALUE_PLUS_30_PER:
+ control2 |= MAX8973_INDUCTOR_PLUS_30_PER;
+ break;
+
+ case MAX8973_CONTROL_INDUCTOR_VALUE_PLUS_60_PER:
+ control2 |= MAX8973_INDUCTOR_PLUS_60_PER;
+ break;
+ }
+
+ ret = regmap_write(max->regmap, MAX8973_CONTROL1, control1);
+ if (ret < 0) {
+ dev_err(max->dev, "register %d write failed, err = %d",
+ MAX8973_CONTROL1, ret);
+ return ret;
+ }
+
+ ret = regmap_write(max->regmap, MAX8973_CONTROL2, control2);
+ if (ret < 0) {
+ dev_err(max->dev, "register %d write failed, err = %d",
+ MAX8973_CONTROL2, ret);
+ return ret;
+ }
+
+ /* If external control is enabled then disable EN bit */
+ if (max->enable_external_control) {
+ ret = regmap_update_bits(max->regmap, MAX8973_VOUT,
+ MAX8973_VOUT_ENABLE, 0);
+ if (ret < 0)
+ dev_err(max->dev, "register %d update failed, err = %d",
+ MAX8973_VOUT, ret);
+ }
+ return ret;
+}
+
+static const struct regmap_config max8973_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX8973_CHIPID2,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int __devinit max8973_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max8973_regulator_platform_data *pdata;
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ struct max8973_chip *max;
+ int ret;
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->dev, "No Platform data");
+ return -EIO;
+ }
+
+ max = devm_kzalloc(&client->dev, sizeof(*max), GFP_KERNEL);
+ if (!max) {
+ dev_err(&client->dev, "Memory allocation for max failed\n");
+ return -ENOMEM;
+ }
+
+ max->regmap = devm_regmap_init_i2c(client, &max8973_regmap_config);
+ if (IS_ERR(max->regmap)) {
+ ret = PTR_ERR(max->regmap);
+ dev_err(&client->dev, "regmap init failed, err %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(client, max);
+ max->dev = &client->dev;
+ max->desc.name = id->name;
+ max->desc.id = 0;
+ max->desc.ops = &max8973_dcdc_ops;
+ max->desc.type = REGULATOR_VOLTAGE;
+ max->desc.owner = THIS_MODULE;
+ max->desc.min_uV = MAX8973_MIN_VOLATGE;
+ max->desc.uV_step = MAX8973_VOLATGE_STEP;
+ max->desc.n_voltages = MAX8973_BUCK_N_VOLTAGE;
+
+ if (!pdata->enable_ext_control) {
+ max->desc.enable_reg = MAX8973_VOUT;
+ max->desc.enable_mask = MAX8973_VOUT_ENABLE;
+ max8973_dcdc_ops.enable = regulator_enable_regmap;
+ max8973_dcdc_ops.disable = regulator_disable_regmap;
+ max8973_dcdc_ops.is_enabled = regulator_is_enabled_regmap;
+ }
+
+ max->enable_external_control = pdata->enable_ext_control;
+ max->dvs_gpio = pdata->dvs_gpio;
+ max->curr_gpio_val = pdata->dvs_def_state;
+ max->curr_vout_reg = MAX8973_VOUT + pdata->dvs_def_state;
+ max->lru_index[0] = max->curr_vout_reg;
+ max->valid_dvs_gpio = false;
+
+ if (gpio_is_valid(max->dvs_gpio)) {
+ int gpio_flags;
+ int i;
+
+ gpio_flags = (pdata->dvs_def_state) ?
+ GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+ ret = devm_gpio_request_one(&client->dev, max->dvs_gpio,
+ gpio_flags, "max8973-dvs");
+ if (ret) {
+ dev_err(&client->dev,
+ "gpio_request for gpio %d failed, err = %d\n",
+ max->dvs_gpio, ret);
+ return ret;
+ }
+ max->valid_dvs_gpio = true;
+
+ /*
+ * Initialize the lru index with vout_reg id
+ * The index 0 will be most recently used and
+ * set with the max->curr_vout_reg */
+ for (i = 0; i < MAX8973_MAX_VOUT_REG; ++i)
+ max->lru_index[i] = i;
+ max->lru_index[0] = max->curr_vout_reg;
+ max->lru_index[max->curr_vout_reg] = 0;
+ }
+
+ ret = max8973_init_dcdc(max, pdata);
+ if (ret < 0) {
+ dev_err(max->dev, "Max8973 Init failed, err = %d\n", ret);
+ return ret;
+ }
+
+ config.dev = &client->dev;
+ config.init_data = pdata->reg_init_data;
+ config.driver_data = max;
+ config.of_node = client->dev.of_node;
+ config.regmap = max->regmap;
+
+ /* Register the regulators */
+ rdev = regulator_register(&max->desc, &config);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(max->dev, "regulator register failed, err %d\n", ret);
+ return ret;
+ }
+
+ max->rdev = rdev;
+ return 0;
+}
+
+static int __devexit max8973_remove(struct i2c_client *client)
+{
+ struct max8973_chip *max = i2c_get_clientdata(client);
+
+ regulator_unregister(max->rdev);
+ return 0;
+}
+
+static const struct i2c_device_id max8973_id[] = {
+ {.name = "max8973",},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, max8973_id);
+
+static struct i2c_driver max8973_i2c_driver = {
+ .driver = {
+ .name = "max8973",
+ .owner = THIS_MODULE,
+ },
+ .probe = max8973_probe,
+ .remove = __devexit_p(max8973_remove),
+ .id_table = max8973_id,
+};
+
+static int __init max8973_init(void)
+{
+ return i2c_add_driver(&max8973_i2c_driver);
+}
+subsys_initcall(max8973_init);
+
+static void __exit max8973_cleanup(void)
+{
+ i2c_del_driver(&max8973_i2c_driver);
+}
+module_exit(max8973_cleanup);
+
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_DESCRIPTION("MAX8973 voltage regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c
index e39a0c7260d..df0eafb0dc7 100644
--- a/drivers/regulator/max8997.c
+++ b/drivers/regulator/max8997.c
@@ -24,6 +24,7 @@
#include <linux/bug.h>
#include <linux/err.h>
#include <linux/gpio.h>
+#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -31,6 +32,7 @@
#include <linux/regulator/machine.h>
#include <linux/mfd/max8997.h>
#include <linux/mfd/max8997-private.h>
+#include <linux/regulator/of_regulator.h>
struct max8997_data {
struct device *dev;
@@ -933,22 +935,163 @@ static struct regulator_desc regulators[] = {
max8997_charger_fixedstate_ops),
};
-static __devinit int max8997_pmic_probe(struct platform_device *pdev)
+#ifdef CONFIG_OF
+static int max8997_pmic_dt_parse_dvs_gpio(struct max8997_dev *iodev,
+ struct max8997_platform_data *pdata,
+ struct device_node *pmic_np)
+{
+ int i, gpio;
+
+ for (i = 0; i < 3; i++) {
+ gpio = of_get_named_gpio(pmic_np,
+ "max8997,pmic-buck125-dvs-gpios", i);
+ if (!gpio_is_valid(gpio)) {
+ dev_err(iodev->dev, "invalid gpio[%d]: %d\n", i, gpio);
+ return -EINVAL;
+ }
+ pdata->buck125_gpios[i] = gpio;
+ }
+ return 0;
+}
+
+static int max8997_pmic_dt_parse_pdata(struct max8997_dev *iodev,
+ struct max8997_platform_data *pdata)
+{
+ struct device_node *pmic_np, *regulators_np, *reg_np;
+ struct max8997_regulator_data *rdata;
+ unsigned int i, dvs_voltage_nr = 1, ret;
+
+ pmic_np = iodev->dev->of_node;
+ if (!pmic_np) {
+ dev_err(iodev->dev, "could not find pmic sub-node\n");
+ return -ENODEV;
+ }
+
+ regulators_np = of_find_node_by_name(pmic_np, "regulators");
+ if (!regulators_np) {
+ dev_err(iodev->dev, "could not find regulators sub-node\n");
+ return -EINVAL;
+ }
+
+ /* count the number of regulators to be supported in pmic */
+ pdata->num_regulators = 0;
+ for_each_child_of_node(regulators_np, reg_np)
+ pdata->num_regulators++;
+
+ rdata = devm_kzalloc(iodev->dev, sizeof(*rdata) *
+ pdata->num_regulators, GFP_KERNEL);
+ if (!rdata) {
+ dev_err(iodev->dev, "could not allocate memory for "
+ "regulator data\n");
+ return -ENOMEM;
+ }
+
+ pdata->regulators = rdata;
+ for_each_child_of_node(regulators_np, reg_np) {
+ for (i = 0; i < ARRAY_SIZE(regulators); i++)
+ if (!of_node_cmp(reg_np->name, regulators[i].name))
+ break;
+
+ if (i == ARRAY_SIZE(regulators)) {
+ dev_warn(iodev->dev, "don't know how to configure "
+ "regulator %s\n", reg_np->name);
+ continue;
+ }
+
+ rdata->id = i;
+ rdata->initdata = of_get_regulator_init_data(
+ iodev->dev, reg_np);
+ rdata->reg_node = reg_np;
+ rdata++;
+ }
+
+ if (of_get_property(pmic_np, "max8997,pmic-buck1-uses-gpio-dvs", NULL))
+ pdata->buck1_gpiodvs = true;
+
+ if (of_get_property(pmic_np, "max8997,pmic-buck2-uses-gpio-dvs", NULL))
+ pdata->buck2_gpiodvs = true;
+
+ if (of_get_property(pmic_np, "max8997,pmic-buck5-uses-gpio-dvs", NULL))
+ pdata->buck5_gpiodvs = true;
+
+ if (pdata->buck1_gpiodvs || pdata->buck2_gpiodvs ||
+ pdata->buck5_gpiodvs) {
+ ret = max8997_pmic_dt_parse_dvs_gpio(iodev, pdata, pmic_np);
+ if (ret)
+ return -EINVAL;
+
+ if (of_property_read_u32(pmic_np,
+ "max8997,pmic-buck125-default-dvs-idx",
+ &pdata->buck125_default_idx)) {
+ pdata->buck125_default_idx = 0;
+ } else {
+ if (pdata->buck125_default_idx >= 8) {
+ pdata->buck125_default_idx = 0;
+ dev_info(iodev->dev, "invalid value for "
+ "default dvs index, using 0 instead\n");
+ }
+ }
+
+ if (of_get_property(pmic_np,
+ "max8997,pmic-ignore-gpiodvs-side-effect", NULL))
+ pdata->ignore_gpiodvs_side_effect = true;
+
+ dvs_voltage_nr = 8;
+ }
+
+ if (of_property_read_u32_array(pmic_np,
+ "max8997,pmic-buck1-dvs-voltage",
+ pdata->buck1_voltage, dvs_voltage_nr)) {
+ dev_err(iodev->dev, "buck1 voltages not specified\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32_array(pmic_np,
+ "max8997,pmic-buck2-dvs-voltage",
+ pdata->buck2_voltage, dvs_voltage_nr)) {
+ dev_err(iodev->dev, "buck2 voltages not specified\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32_array(pmic_np,
+ "max8997,pmic-buck5-dvs-voltage",
+ pdata->buck5_voltage, dvs_voltage_nr)) {
+ dev_err(iodev->dev, "buck5 voltages not specified\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+#else
+static int max8997_pmic_dt_parse_pdata(struct max8997_dev *iodev,
+ struct max8997_platform_data *pdata)
+{
+ return 0;
+}
+#endif /* CONFIG_OF */
+
+static int max8997_pmic_probe(struct platform_device *pdev)
{
struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent);
- struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev);
+ struct max8997_platform_data *pdata = iodev->pdata;
struct regulator_config config = { };
struct regulator_dev **rdev;
struct max8997_data *max8997;
struct i2c_client *i2c;
- int i, ret, size;
+ int i, ret, size, nr_dvs;
u8 max_buck1 = 0, max_buck2 = 0, max_buck5 = 0;
- if (!pdata) {
+ if (IS_ERR_OR_NULL(pdata)) {
dev_err(pdev->dev.parent, "No platform init data supplied.\n");
return -ENODEV;
}
+ if (iodev->dev->of_node) {
+ ret = max8997_pmic_dt_parse_pdata(iodev, pdata);
+ if (ret)
+ return ret;
+ }
+
max8997 = devm_kzalloc(&pdev->dev, sizeof(struct max8997_data),
GFP_KERNEL);
if (!max8997)
@@ -973,7 +1116,10 @@ static __devinit int max8997_pmic_probe(struct platform_device *pdev)
memcpy(max8997->buck125_gpios, pdata->buck125_gpios, sizeof(int) * 3);
max8997->ignore_gpiodvs_side_effect = pdata->ignore_gpiodvs_side_effect;
- for (i = 0; i < 8; i++) {
+ nr_dvs = (pdata->buck1_gpiodvs || pdata->buck2_gpiodvs ||
+ pdata->buck5_gpiodvs) ? 8 : 1;
+
+ for (i = 0; i < nr_dvs; i++) {
max8997->buck1_vol[i] = ret =
max8997_get_voltage_proper_val(
&buck1245_voltage_map_desc,
@@ -1019,6 +1165,19 @@ static __devinit int max8997_pmic_probe(struct platform_device *pdev)
max_buck5, 0x3f);
}
+ /* Initialize all the DVS related BUCK registers */
+ for (i = 0; i < nr_dvs; i++) {
+ max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS1 + i,
+ max8997->buck1_vol[i],
+ 0x3f);
+ max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS1 + i,
+ max8997->buck2_vol[i],
+ 0x3f);
+ max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS1 + i,
+ max8997->buck5_vol[i],
+ 0x3f);
+ }
+
/*
* If buck 1, 2, and 5 do not care DVS GPIO settings, ignore them.
* If at least one of them cares, set gpios.
@@ -1068,19 +1227,6 @@ static __devinit int max8997_pmic_probe(struct platform_device *pdev)
max8997_update_reg(i2c, MAX8997_REG_BUCK5CTRL, (pdata->buck5_gpiodvs) ?
(1 << 1) : (0 << 1), 1 << 1);
- /* Initialize all the DVS related BUCK registers */
- for (i = 0; i < 8; i++) {
- max8997_update_reg(i2c, MAX8997_REG_BUCK1DVS1 + i,
- max8997->buck1_vol[i],
- 0x3f);
- max8997_update_reg(i2c, MAX8997_REG_BUCK2DVS1 + i,
- max8997->buck2_vol[i],
- 0x3f);
- max8997_update_reg(i2c, MAX8997_REG_BUCK5DVS1 + i,
- max8997->buck5_vol[i],
- 0x3f);
- }
-
/* Misc Settings */
max8997->ramp_delay = 10; /* set 10mV/us, which is the default */
max8997_write_reg(i2c, MAX8997_REG_BUCKRAMP, (0xf << 4) | 0x9);
@@ -1101,6 +1247,7 @@ static __devinit int max8997_pmic_probe(struct platform_device *pdev)
config.dev = max8997->dev;
config.init_data = pdata->regulators[i].initdata;
config.driver_data = max8997;
+ config.of_node = pdata->regulators[i].reg_node;
rdev[i] = regulator_register(&regulators[id], &config);
if (IS_ERR(rdev[i])) {
@@ -1120,7 +1267,7 @@ err_out:
return ret;
}
-static int __devexit max8997_pmic_remove(struct platform_device *pdev)
+static int max8997_pmic_remove(struct platform_device *pdev)
{
struct max8997_data *max8997 = platform_get_drvdata(pdev);
struct regulator_dev **rdev = max8997->rdev;
@@ -1143,7 +1290,7 @@ static struct platform_driver max8997_pmic_driver = {
.owner = THIS_MODULE,
},
.probe = max8997_pmic_probe,
- .remove = __devexit_p(max8997_pmic_remove),
+ .remove = max8997_pmic_remove,
.id_table = max8997_pmic_id,
};
diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c
index 5dfa920ff0c..b821d08eb64 100644
--- a/drivers/regulator/max8998.c
+++ b/drivers/regulator/max8998.c
@@ -633,7 +633,7 @@ static struct regulator_desc regulators[] = {
}
};
-static __devinit int max8998_pmic_probe(struct platform_device *pdev)
+static int max8998_pmic_probe(struct platform_device *pdev)
{
struct max8998_dev *iodev = dev_get_drvdata(pdev->dev.parent);
struct max8998_platform_data *pdata = dev_get_platdata(iodev->dev);
@@ -818,7 +818,7 @@ err_out:
return ret;
}
-static int __devexit max8998_pmic_remove(struct platform_device *pdev)
+static int max8998_pmic_remove(struct platform_device *pdev)
{
struct max8998_data *max8998 = platform_get_drvdata(pdev);
struct regulator_dev **rdev = max8998->rdev;
@@ -842,7 +842,7 @@ static struct platform_driver max8998_pmic_driver = {
.owner = THIS_MODULE,
},
.probe = max8998_pmic_probe,
- .remove = __devexit_p(max8998_pmic_remove),
+ .remove = max8998_pmic_remove,
.id_table = max8998_pmic_id,
};
diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c
index 0801a6d0c12..c46c6705cd7 100644
--- a/drivers/regulator/mc13783-regulator.c
+++ b/drivers/regulator/mc13783-regulator.c
@@ -392,7 +392,7 @@ static struct regulator_ops mc13783_gpo_regulator_ops = {
.set_voltage = mc13xxx_fixed_regulator_set_voltage,
};
-static int __devinit mc13783_regulator_probe(struct platform_device *pdev)
+static int mc13783_regulator_probe(struct platform_device *pdev)
{
struct mc13xxx_regulator_priv *priv;
struct mc13xxx *mc13783 = dev_get_drvdata(pdev->dev.parent);
@@ -445,7 +445,7 @@ err:
return ret;
}
-static int __devexit mc13783_regulator_remove(struct platform_device *pdev)
+static int mc13783_regulator_remove(struct platform_device *pdev)
{
struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev);
struct mc13xxx_regulator_platform_data *pdata =
@@ -465,7 +465,7 @@ static struct platform_driver mc13783_regulator_driver = {
.name = "mc13783-regulator",
.owner = THIS_MODULE,
},
- .remove = __devexit_p(mc13783_regulator_remove),
+ .remove = mc13783_regulator_remove,
.probe = mc13783_regulator_probe,
};
diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c
index 1fa63812f7a..0d84b1f3319 100644
--- a/drivers/regulator/mc13892-regulator.c
+++ b/drivers/regulator/mc13892-regulator.c
@@ -486,7 +486,7 @@ static unsigned int mc13892_vcam_get_mode(struct regulator_dev *rdev)
}
-static int __devinit mc13892_regulator_probe(struct platform_device *pdev)
+static int mc13892_regulator_probe(struct platform_device *pdev)
{
struct mc13xxx_regulator_priv *priv;
struct mc13xxx *mc13892 = dev_get_drvdata(pdev->dev.parent);
@@ -588,7 +588,7 @@ err_unlock:
return ret;
}
-static int __devexit mc13892_regulator_remove(struct platform_device *pdev)
+static int mc13892_regulator_remove(struct platform_device *pdev)
{
struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev);
int i;
@@ -606,7 +606,7 @@ static struct platform_driver mc13892_regulator_driver = {
.name = "mc13892-regulator",
.owner = THIS_MODULE,
},
- .remove = __devexit_p(mc13892_regulator_remove),
+ .remove = mc13892_regulator_remove,
.probe = mc13892_regulator_probe,
};
diff --git a/drivers/regulator/mc13xxx-regulator-core.c b/drivers/regulator/mc13xxx-regulator-core.c
index 88cbb832d55..4ed89c65411 100644
--- a/drivers/regulator/mc13xxx-regulator-core.c
+++ b/drivers/regulator/mc13xxx-regulator-core.c
@@ -162,7 +162,7 @@ struct regulator_ops mc13xxx_fixed_regulator_ops = {
EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_ops);
#ifdef CONFIG_OF
-int __devinit mc13xxx_get_num_regulators_dt(struct platform_device *pdev)
+int mc13xxx_get_num_regulators_dt(struct platform_device *pdev)
{
struct device_node *parent, *child;
int num = 0;
@@ -179,7 +179,7 @@ int __devinit mc13xxx_get_num_regulators_dt(struct platform_device *pdev)
}
EXPORT_SYMBOL_GPL(mc13xxx_get_num_regulators_dt);
-struct mc13xxx_regulator_init_data * __devinit mc13xxx_parse_regulators_dt(
+struct mc13xxx_regulator_init_data *mc13xxx_parse_regulators_dt(
struct platform_device *pdev, struct mc13xxx_regulator *regulators,
int num_regulators)
{
diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c
index 07aee694ba9..e915629a25c 100644
--- a/drivers/regulator/palmas-regulator.c
+++ b/drivers/regulator/palmas-regulator.c
@@ -309,68 +309,22 @@ static int palmas_list_voltage_smps(struct regulator_dev *dev,
int id = rdev_get_id(dev);
int mult = 1;
- if (!selector)
- return 0;
-
/* Read the multiplier set in VSEL register to return
* the correct voltage.
*/
if (pmic->range[id])
mult = 2;
- /* Voltage is (0.49V + (selector * 0.01V)) * RANGE
- * as defined in data sheet. RANGE is either x1 or x2
- */
- return (490000 + (selector * 10000)) * mult;
-}
-
-static int palmas_get_voltage_smps_sel(struct regulator_dev *dev)
-{
- struct palmas_pmic *pmic = rdev_get_drvdata(dev);
- int id = rdev_get_id(dev);
- int selector;
- unsigned int reg;
- unsigned int addr;
-
- addr = palmas_regs_info[id].vsel_addr;
-
- palmas_smps_read(pmic->palmas, addr, &reg);
-
- selector = reg & PALMAS_SMPS12_VOLTAGE_VSEL_MASK;
-
- /* Adjust selector to match list_voltage ranges */
- if ((selector > 0) && (selector < 6))
- selector = 6;
- if (!selector)
- selector = 5;
- if (selector > 121)
- selector = 121;
- selector -= 5;
-
- return selector;
-}
-
-static int palmas_set_voltage_smps_sel(struct regulator_dev *dev,
- unsigned selector)
-{
- struct palmas_pmic *pmic = rdev_get_drvdata(dev);
- int id = rdev_get_id(dev);
- unsigned int reg = 0;
- unsigned int addr;
-
- addr = palmas_regs_info[id].vsel_addr;
-
- /* Make sure we don't change the value of RANGE */
- if (pmic->range[id])
- reg |= PALMAS_SMPS12_VOLTAGE_RANGE;
-
- /* Adjust the linux selector into range used in VSEL register */
- if (selector)
- reg |= selector + 5;
-
- palmas_smps_write(pmic->palmas, addr, reg);
-
- return 0;
+ if (selector == 0)
+ return 0;
+ else if (selector < 6)
+ return 500000 * mult;
+ else
+ /* Voltage is linear mapping starting from selector 6,
+ * volt = (0.49V + ((selector - 5) * 0.01V)) * RANGE
+ * RANGE is either x1 or x2
+ */
+ return (490000 + ((selector - 5) * 10000)) * mult;
}
static int palmas_map_voltage_smps(struct regulator_dev *rdev,
@@ -386,11 +340,11 @@ static int palmas_map_voltage_smps(struct regulator_dev *rdev,
if (pmic->range[id]) { /* RANGE is x2 */
if (min_uV < 1000000)
min_uV = 1000000;
- ret = DIV_ROUND_UP(min_uV - 1000000, 20000) + 1;
+ ret = DIV_ROUND_UP(min_uV - 1000000, 20000) + 6;
} else { /* RANGE is x1 */
if (min_uV < 500000)
min_uV = 500000;
- ret = DIV_ROUND_UP(min_uV - 500000, 10000) + 1;
+ ret = DIV_ROUND_UP(min_uV - 500000, 10000) + 6;
}
/* Map back into a voltage to verify we're still in bounds */
@@ -407,8 +361,8 @@ static struct regulator_ops palmas_ops_smps = {
.disable = palmas_disable_smps,
.set_mode = palmas_set_mode_smps,
.get_mode = palmas_get_mode_smps,
- .get_voltage_sel = palmas_get_voltage_smps_sel,
- .set_voltage_sel = palmas_set_voltage_smps_sel,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
.list_voltage = palmas_list_voltage_smps,
.map_voltage = palmas_map_voltage_smps,
};
@@ -436,44 +390,14 @@ static int palmas_is_enabled_ldo(struct regulator_dev *dev)
return !!(reg);
}
-static int palmas_list_voltage_ldo(struct regulator_dev *dev,
- unsigned selector)
-{
- if (!selector)
- return 0;
-
- /* voltage is 0.85V + (selector * 0.05v) */
- return 850000 + (selector * 50000);
-}
-
-static int palmas_map_voltage_ldo(struct regulator_dev *rdev,
- int min_uV, int max_uV)
-{
- int ret, voltage;
-
- if (min_uV == 0)
- return 0;
-
- if (min_uV < 900000)
- min_uV = 900000;
- ret = DIV_ROUND_UP(min_uV - 900000, 50000) + 1;
-
- /* Map back into a voltage to verify we're still in bounds */
- voltage = palmas_list_voltage_ldo(rdev, ret);
- if (voltage < min_uV || voltage > max_uV)
- return -EINVAL;
-
- return ret;
-}
-
static struct regulator_ops palmas_ops_ldo = {
.is_enabled = palmas_is_enabled_ldo,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
- .list_voltage = palmas_list_voltage_ldo,
- .map_voltage = palmas_map_voltage_ldo,
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
};
/*
@@ -595,7 +519,7 @@ static struct of_regulator_match palmas_matches[] = {
{ .name = "ldousb", },
};
-static void __devinit palmas_dt_to_pdata(struct device *dev,
+static void palmas_dt_to_pdata(struct device *dev,
struct device_node *node,
struct palmas_pmic_platform_data *pdata)
{
@@ -663,7 +587,7 @@ static void __devinit palmas_dt_to_pdata(struct device *dev,
}
-static __devinit int palmas_probe(struct platform_device *pdev)
+static int palmas_probe(struct platform_device *pdev)
{
struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
struct palmas_pmic_platform_data *pdata = pdev->dev.platform_data;
@@ -733,6 +657,14 @@ static __devinit int palmas_probe(struct platform_device *pdev)
continue;
}
+ /* Initialise sleep/init values from platform data */
+ if (pdata && pdata->reg_init[id]) {
+ reg_init = pdata->reg_init[id];
+ ret = palmas_smps_init(palmas, id, reg_init);
+ if (ret)
+ goto err_unregister_regulator;
+ }
+
/* Register the regulators */
pmic->desc[id].name = palmas_regs_info[id].name;
pmic->desc[id].id = id;
@@ -753,29 +685,11 @@ static __devinit int palmas_probe(struct platform_device *pdev)
pmic->desc[id].uV_step = 1250000;
break;
default:
- pmic->desc[id].ops = &palmas_ops_smps;
- pmic->desc[id].n_voltages = PALMAS_SMPS_NUM_VOLTAGES;
- }
-
- pmic->desc[id].type = REGULATOR_VOLTAGE;
- pmic->desc[id].owner = THIS_MODULE;
-
- /* Initialise sleep/init values from platform data */
- if (pdata) {
- reg_init = pdata->reg_init[id];
- if (reg_init) {
- ret = palmas_smps_init(palmas, id, reg_init);
- if (ret)
- goto err_unregister_regulator;
- }
- }
-
- /*
- * read and store the RANGE bit for later use
- * This must be done before regulator is probed otherwise
- * we error in probe with unsuportable ranges.
- */
- if (id != PALMAS_REG_SMPS10) {
+ /*
+ * Read and store the RANGE bit for later use
+ * This must be done before regulator is probed,
+ * otherwise we error in probe with unsupportable ranges.
+ */
addr = palmas_regs_info[id].vsel_addr;
ret = palmas_smps_read(pmic->palmas, addr, &reg);
@@ -783,8 +697,19 @@ static __devinit int palmas_probe(struct platform_device *pdev)
goto err_unregister_regulator;
if (reg & PALMAS_SMPS12_VOLTAGE_RANGE)
pmic->range[id] = 1;
+
+ pmic->desc[id].ops = &palmas_ops_smps;
+ pmic->desc[id].n_voltages = PALMAS_SMPS_NUM_VOLTAGES;
+ pmic->desc[id].vsel_reg =
+ PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE,
+ palmas_regs_info[id].vsel_addr);
+ pmic->desc[id].vsel_mask =
+ PALMAS_SMPS12_VOLTAGE_VSEL_MASK;
}
+ pmic->desc[id].type = REGULATOR_VOLTAGE;
+ pmic->desc[id].owner = THIS_MODULE;
+
if (pdata)
config.init_data = pdata->reg_data[id];
else
@@ -821,6 +746,9 @@ static __devinit int palmas_probe(struct platform_device *pdev)
pmic->desc[id].type = REGULATOR_VOLTAGE;
pmic->desc[id].owner = THIS_MODULE;
+ pmic->desc[id].min_uV = 900000;
+ pmic->desc[id].uV_step = 50000;
+ pmic->desc[id].linear_min_sel = 1;
pmic->desc[id].vsel_reg = PALMAS_BASE_TO_REG(PALMAS_LDO_BASE,
palmas_regs_info[id].vsel_addr);
pmic->desc[id].vsel_mask = PALMAS_LDO1_VOLTAGE_VSEL_MASK;
@@ -868,7 +796,7 @@ err_unregister_regulator:
return ret;
}
-static int __devexit palmas_remove(struct platform_device *pdev)
+static int palmas_remove(struct platform_device *pdev)
{
struct palmas_pmic *pmic = platform_get_drvdata(pdev);
int id;
@@ -890,7 +818,7 @@ static struct platform_driver palmas_driver = {
.owner = THIS_MODULE,
},
.probe = palmas_probe,
- .remove = __devexit_p(palmas_remove),
+ .remove = palmas_remove,
};
static int __init palmas_init(void)
diff --git a/drivers/regulator/pcap-regulator.c b/drivers/regulator/pcap-regulator.c
index 68777acc099..4899342f1fc 100644
--- a/drivers/regulator/pcap-regulator.c
+++ b/drivers/regulator/pcap-regulator.c
@@ -236,7 +236,7 @@ static const struct regulator_desc pcap_regulators[] = {
VREG(VAUX4), VREG(VSIM), VREG(VSIM2), VREG(VVIB), VREG(SW1), VREG(SW2),
};
-static int __devinit pcap_regulator_probe(struct platform_device *pdev)
+static int pcap_regulator_probe(struct platform_device *pdev)
{
struct regulator_dev *rdev;
void *pcap = dev_get_drvdata(pdev->dev.parent);
@@ -255,7 +255,7 @@ static int __devinit pcap_regulator_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit pcap_regulator_remove(struct platform_device *pdev)
+static int pcap_regulator_remove(struct platform_device *pdev)
{
struct regulator_dev *rdev = platform_get_drvdata(pdev);
@@ -271,7 +271,7 @@ static struct platform_driver pcap_regulator_driver = {
.owner = THIS_MODULE,
},
.probe = pcap_regulator_probe,
- .remove = __devexit_p(pcap_regulator_remove),
+ .remove = pcap_regulator_remove,
};
static int __init pcap_regulator_init(void)
diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c
index 092e5cb848a..534075e13d6 100644
--- a/drivers/regulator/pcf50633-regulator.c
+++ b/drivers/regulator/pcf50633-regulator.c
@@ -24,12 +24,15 @@
#include <linux/mfd/pcf50633/core.h>
#include <linux/mfd/pcf50633/pmic.h>
-#define PCF50633_REGULATOR(_name, _id, _n) \
+#define PCF50633_REGULATOR(_name, _id, _min_uV, _uV_step, _min_sel, _n) \
{ \
.name = _name, \
.id = PCF50633_REGULATOR_##_id, \
.ops = &pcf50633_regulator_ops, \
.n_voltages = _n, \
+ .min_uV = _min_uV, \
+ .uV_step = _uV_step, \
+ .linear_min_sel = _min_sel, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
.vsel_reg = PCF50633_REG_##_id##OUT, \
@@ -38,165 +41,42 @@
.enable_mask = PCF50633_REGULATOR_ON, \
}
-/* Bits from voltage value */
-static u8 auto_voltage_bits(unsigned int millivolts)
-{
- if (millivolts < 1800)
- return 0x2f;
- if (millivolts > 3800)
- return 0xff;
-
- millivolts -= 625;
-
- return millivolts / 25;
-}
-
-static u8 down_voltage_bits(unsigned int millivolts)
-{
- if (millivolts < 625)
- return 0;
- else if (millivolts > 3000)
- return 0xff;
-
- millivolts -= 625;
-
- return millivolts / 25;
-}
-
-static u8 ldo_voltage_bits(unsigned int millivolts)
-{
- if (millivolts < 900)
- return 0;
- else if (millivolts > 3600)
- return 0x1f;
-
- millivolts -= 900;
- return millivolts / 100;
-}
-
-/* Obtain voltage value from bits */
-static unsigned int auto_voltage_value(u8 bits)
-{
- /* AUTOOUT: 00000000 to 00101110 are reserved.
- * Return 0 for bits in reserved range, which means this selector code
- * can't be used on this system */
- if (bits < 0x2f)
- return 0;
-
- return 625 + (bits * 25);
-}
-
-
-static unsigned int down_voltage_value(u8 bits)
-{
- return 625 + (bits * 25);
-}
-
-
-static unsigned int ldo_voltage_value(u8 bits)
-{
- bits &= 0x1f;
-
- return 900 + (bits * 100);
-}
-
-static int pcf50633_regulator_map_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
-{
- struct pcf50633 *pcf;
- int regulator_id, millivolts;
- u8 volt_bits;
-
- pcf = rdev_get_drvdata(rdev);
-
- regulator_id = rdev_get_id(rdev);
- if (regulator_id >= PCF50633_NUM_REGULATORS)
- return -EINVAL;
-
- millivolts = min_uV / 1000;
-
- switch (regulator_id) {
- case PCF50633_REGULATOR_AUTO:
- volt_bits = auto_voltage_bits(millivolts);
- break;
- case PCF50633_REGULATOR_DOWN1:
- case PCF50633_REGULATOR_DOWN2:
- volt_bits = down_voltage_bits(millivolts);
- break;
- case PCF50633_REGULATOR_LDO1:
- case PCF50633_REGULATOR_LDO2:
- case PCF50633_REGULATOR_LDO3:
- case PCF50633_REGULATOR_LDO4:
- case PCF50633_REGULATOR_LDO5:
- case PCF50633_REGULATOR_LDO6:
- case PCF50633_REGULATOR_HCLDO:
- case PCF50633_REGULATOR_MEMLDO:
- volt_bits = ldo_voltage_bits(millivolts);
- break;
- default:
- return -EINVAL;
- }
-
- return volt_bits;
-}
-
-static int pcf50633_regulator_list_voltage(struct regulator_dev *rdev,
- unsigned int index)
-{
- int regulator_id = rdev_get_id(rdev);
-
- int millivolts;
-
- switch (regulator_id) {
- case PCF50633_REGULATOR_AUTO:
- millivolts = auto_voltage_value(index);
- break;
- case PCF50633_REGULATOR_DOWN1:
- case PCF50633_REGULATOR_DOWN2:
- millivolts = down_voltage_value(index);
- break;
- case PCF50633_REGULATOR_LDO1:
- case PCF50633_REGULATOR_LDO2:
- case PCF50633_REGULATOR_LDO3:
- case PCF50633_REGULATOR_LDO4:
- case PCF50633_REGULATOR_LDO5:
- case PCF50633_REGULATOR_LDO6:
- case PCF50633_REGULATOR_HCLDO:
- case PCF50633_REGULATOR_MEMLDO:
- millivolts = ldo_voltage_value(index);
- break;
- default:
- return -EINVAL;
- }
-
- return millivolts * 1000;
-}
-
static struct regulator_ops pcf50633_regulator_ops = {
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
- .list_voltage = pcf50633_regulator_list_voltage,
- .map_voltage = pcf50633_regulator_map_voltage,
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
};
static const struct regulator_desc regulators[] = {
- [PCF50633_REGULATOR_AUTO] = PCF50633_REGULATOR("auto", AUTO, 128),
- [PCF50633_REGULATOR_DOWN1] = PCF50633_REGULATOR("down1", DOWN1, 96),
- [PCF50633_REGULATOR_DOWN2] = PCF50633_REGULATOR("down2", DOWN2, 96),
- [PCF50633_REGULATOR_LDO1] = PCF50633_REGULATOR("ldo1", LDO1, 28),
- [PCF50633_REGULATOR_LDO2] = PCF50633_REGULATOR("ldo2", LDO2, 28),
- [PCF50633_REGULATOR_LDO3] = PCF50633_REGULATOR("ldo3", LDO3, 28),
- [PCF50633_REGULATOR_LDO4] = PCF50633_REGULATOR("ldo4", LDO4, 28),
- [PCF50633_REGULATOR_LDO5] = PCF50633_REGULATOR("ldo5", LDO5, 28),
- [PCF50633_REGULATOR_LDO6] = PCF50633_REGULATOR("ldo6", LDO6, 28),
- [PCF50633_REGULATOR_HCLDO] = PCF50633_REGULATOR("hcldo", HCLDO, 28),
- [PCF50633_REGULATOR_MEMLDO] = PCF50633_REGULATOR("memldo", MEMLDO, 28),
+ [PCF50633_REGULATOR_AUTO] =
+ PCF50633_REGULATOR("auto", AUTO, 1800000, 25000, 0x2f, 128),
+ [PCF50633_REGULATOR_DOWN1] =
+ PCF50633_REGULATOR("down1", DOWN1, 625000, 25000, 0, 96),
+ [PCF50633_REGULATOR_DOWN2] =
+ PCF50633_REGULATOR("down2", DOWN2, 625000, 25000, 0, 96),
+ [PCF50633_REGULATOR_LDO1] =
+ PCF50633_REGULATOR("ldo1", LDO1, 900000, 100000, 0, 28),
+ [PCF50633_REGULATOR_LDO2] =
+ PCF50633_REGULATOR("ldo2", LDO2, 900000, 100000, 0, 28),
+ [PCF50633_REGULATOR_LDO3] =
+ PCF50633_REGULATOR("ldo3", LDO3, 900000, 100000, 0, 28),
+ [PCF50633_REGULATOR_LDO4] =
+ PCF50633_REGULATOR("ldo4", LDO4, 900000, 100000, 0, 28),
+ [PCF50633_REGULATOR_LDO5] =
+ PCF50633_REGULATOR("ldo5", LDO5, 900000, 100000, 0, 28),
+ [PCF50633_REGULATOR_LDO6] =
+ PCF50633_REGULATOR("ldo6", LDO6, 900000, 100000, 0, 28),
+ [PCF50633_REGULATOR_HCLDO] =
+ PCF50633_REGULATOR("hcldo", HCLDO, 900000, 100000, 0, 28),
+ [PCF50633_REGULATOR_MEMLDO] =
+ PCF50633_REGULATOR("memldo", MEMLDO, 900000, 100000, 0, 28),
};
-static int __devinit pcf50633_regulator_probe(struct platform_device *pdev)
+static int pcf50633_regulator_probe(struct platform_device *pdev)
{
struct regulator_dev *rdev;
struct pcf50633 *pcf;
@@ -222,7 +102,7 @@ static int __devinit pcf50633_regulator_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit pcf50633_regulator_remove(struct platform_device *pdev)
+static int pcf50633_regulator_remove(struct platform_device *pdev)
{
struct regulator_dev *rdev = platform_get_drvdata(pdev);
@@ -237,7 +117,7 @@ static struct platform_driver pcf50633_regulator_driver = {
.name = "pcf50633-regltr",
},
.probe = pcf50633_regulator_probe,
- .remove = __devexit_p(pcf50633_regulator_remove),
+ .remove = pcf50633_regulator_remove,
};
static int __init pcf50633_regulator_init(void)
diff --git a/drivers/regulator/rc5t583-regulator.c b/drivers/regulator/rc5t583-regulator.c
index 8bf4e8c9de9..9e6f78694bf 100644
--- a/drivers/regulator/rc5t583-regulator.c
+++ b/drivers/regulator/rc5t583-regulator.c
@@ -119,7 +119,7 @@ static struct rc5t583_regulator_info rc5t583_reg_info[RC5T583_REGULATOR_MAX] = {
RC5T583_REG(LDO9, LDOEN1, 1, LDODIS1, 1, 0x7F, 900, 3400, 25000, 133),
};
-static int __devinit rc5t583_regulator_probe(struct platform_device *pdev)
+static int rc5t583_regulator_probe(struct platform_device *pdev)
{
struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent);
struct rc5t583_platform_data *pdata = dev_get_platdata(rc5t583->dev);
@@ -198,7 +198,7 @@ clean_exit:
return ret;
}
-static int __devexit rc5t583_regulator_remove(struct platform_device *pdev)
+static int rc5t583_regulator_remove(struct platform_device *pdev)
{
struct rc5t583_regulator *regs = platform_get_drvdata(pdev);
int id;
@@ -214,7 +214,7 @@ static struct platform_driver rc5t583_regulator_driver = {
.owner = THIS_MODULE,
},
.probe = rc5t583_regulator_probe,
- .remove = __devexit_p(rc5t583_regulator_remove),
+ .remove = rc5t583_regulator_remove,
};
static int __init rc5t583_regulator_init(void)
diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c
index 926f9c8f2fa..bd062a2ffbe 100644
--- a/drivers/regulator/s2mps11.c
+++ b/drivers/regulator/s2mps11.c
@@ -231,7 +231,7 @@ static struct regulator_desc regulators[] = {
regulator_desc_buck10,
};
-static __devinit int s2mps11_pmic_probe(struct platform_device *pdev)
+static int s2mps11_pmic_probe(struct platform_device *pdev)
{
struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
struct sec_platform_data *pdata = dev_get_platdata(iodev->dev);
@@ -269,16 +269,16 @@ static __devinit int s2mps11_pmic_probe(struct platform_device *pdev)
if (ramp_enable) {
if (s2mps11->buck2_ramp)
- ramp_reg |= get_ramp_delay(s2mps11->ramp_delay2) >> 6;
+ ramp_reg |= get_ramp_delay(s2mps11->ramp_delay2) << 6;
if (s2mps11->buck3_ramp || s2mps11->buck4_ramp)
- ramp_reg |= get_ramp_delay(s2mps11->ramp_delay34) >> 4;
+ ramp_reg |= get_ramp_delay(s2mps11->ramp_delay34) << 4;
sec_reg_write(iodev, S2MPS11_REG_RAMP, ramp_reg | ramp_enable);
}
ramp_reg &= 0x00;
- ramp_reg |= get_ramp_delay(s2mps11->ramp_delay5) >> 6;
- ramp_reg |= get_ramp_delay(s2mps11->ramp_delay16) >> 4;
- ramp_reg |= get_ramp_delay(s2mps11->ramp_delay7810) >> 2;
+ ramp_reg |= get_ramp_delay(s2mps11->ramp_delay5) << 6;
+ ramp_reg |= get_ramp_delay(s2mps11->ramp_delay16) << 4;
+ ramp_reg |= get_ramp_delay(s2mps11->ramp_delay7810) << 2;
ramp_reg |= get_ramp_delay(s2mps11->ramp_delay9);
sec_reg_write(iodev, S2MPS11_REG_RAMP_BUCK, ramp_reg);
@@ -307,7 +307,7 @@ err:
return ret;
}
-static int __devexit s2mps11_pmic_remove(struct platform_device *pdev)
+static int s2mps11_pmic_remove(struct platform_device *pdev)
{
struct s2mps11_info *s2mps11 = platform_get_drvdata(pdev);
int i;
@@ -330,7 +330,7 @@ static struct platform_driver s2mps11_pmic_driver = {
.owner = THIS_MODULE,
},
.probe = s2mps11_pmic_probe,
- .remove = __devexit_p(s2mps11_pmic_remove),
+ .remove = s2mps11_pmic_remove,
.id_table = s2mps11_pmic_id,
};
diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c
index abe64a32aed..9f991f2c525 100644
--- a/drivers/regulator/s5m8767.c
+++ b/drivers/regulator/s5m8767.c
@@ -168,7 +168,7 @@ static unsigned int s5m8767_opmode_reg[][4] = {
static int s5m8767_get_register(struct regulator_dev *rdev, int *reg,
int *enable_ctrl)
{
- int reg_id = rdev_get_id(rdev);
+ int i, reg_id = rdev_get_id(rdev);
unsigned int mode;
struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
@@ -195,8 +195,17 @@ static int s5m8767_get_register(struct regulator_dev *rdev, int *reg,
return -EINVAL;
}
- mode = s5m8767->opmode[reg_id].mode;
- *enable_ctrl = s5m8767_opmode_reg[reg_id][mode] << S5M8767_ENCTRL_SHIFT;
+ for (i = 0; i < s5m8767->num_regulators; i++) {
+ if (s5m8767->opmode[i].id == reg_id) {
+ mode = s5m8767->opmode[i].mode;
+ break;
+ }
+ }
+
+ if (i < s5m8767->num_regulators)
+ *enable_ctrl =
+ s5m8767_opmode_reg[reg_id][mode] << S5M8767_ENCTRL_SHIFT;
+
return 0;
}
@@ -263,17 +272,17 @@ static int s5m8767_get_voltage_register(struct regulator_dev *rdev, int *_reg)
reg = S5M8767_REG_BUCK1CTRL2;
break;
case S5M8767_BUCK2:
- reg = S5M8767_REG_BUCK2DVS2;
+ reg = S5M8767_REG_BUCK2DVS1;
if (s5m8767->buck2_gpiodvs)
reg += s5m8767->buck_gpioindex;
break;
case S5M8767_BUCK3:
- reg = S5M8767_REG_BUCK3DVS2;
+ reg = S5M8767_REG_BUCK3DVS1;
if (s5m8767->buck3_gpiodvs)
reg += s5m8767->buck_gpioindex;
break;
case S5M8767_BUCK4:
- reg = S5M8767_REG_BUCK4DVS2;
+ reg = S5M8767_REG_BUCK4DVS1;
if (s5m8767->buck4_gpiodvs)
reg += s5m8767->buck_gpioindex;
break;
@@ -499,7 +508,7 @@ static struct regulator_desc regulators[] = {
s5m8767_regulator_desc(BUCK9),
};
-static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
+static int s5m8767_pmic_probe(struct platform_device *pdev)
{
struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
struct sec_platform_data *pdata = dev_get_platdata(iodev->dev);
@@ -547,7 +556,7 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
rdev = s5m8767->rdev;
s5m8767->dev = &pdev->dev;
s5m8767->iodev = iodev;
- s5m8767->num_regulators = S5M8767_REG_MAX - 2;
+ s5m8767->num_regulators = pdata->num_regulators;
platform_set_drvdata(pdev, s5m8767);
s5m8767->buck_gpioindex = pdata->buck_default_idx;
@@ -617,9 +626,16 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
}
}
- if (gpio_is_valid(pdata->buck_gpios[0]) &&
- gpio_is_valid(pdata->buck_gpios[1]) &&
- gpio_is_valid(pdata->buck_gpios[2])) {
+ if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs ||
+ pdata->buck4_gpiodvs) {
+
+ if (!gpio_is_valid(pdata->buck_gpios[0]) ||
+ !gpio_is_valid(pdata->buck_gpios[1]) ||
+ !gpio_is_valid(pdata->buck_gpios[2])) {
+ dev_err(&pdev->dev, "GPIO NOT VALID\n");
+ return -EINVAL;
+ }
+
ret = devm_gpio_request(&pdev->dev, pdata->buck_gpios[0],
"S5M8767 SET1");
if (ret)
@@ -644,10 +660,6 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
/* SET3 GPIO */
gpio_direction_output(pdata->buck_gpios[2],
(s5m8767->buck_gpioindex >> 0) & 0x1);
- } else {
- dev_err(&pdev->dev, "GPIO NOT VALID\n");
- ret = -EINVAL;
- return ret;
}
ret = devm_gpio_request(&pdev->dev, pdata->buck_ds[0], "S5M8767 DS2");
@@ -773,7 +785,7 @@ err:
return ret;
}
-static int __devexit s5m8767_pmic_remove(struct platform_device *pdev)
+static int s5m8767_pmic_remove(struct platform_device *pdev)
{
struct s5m8767_info *s5m8767 = platform_get_drvdata(pdev);
struct regulator_dev **rdev = s5m8767->rdev;
@@ -798,7 +810,7 @@ static struct platform_driver s5m8767_pmic_driver = {
.owner = THIS_MODULE,
},
.probe = s5m8767_pmic_probe,
- .remove = __devexit_p(s5m8767_pmic_remove),
+ .remove = s5m8767_pmic_remove,
.id_table = s5m8767_pmic_id,
};
diff --git a/drivers/regulator/tps51632-regulator.c b/drivers/regulator/tps51632-regulator.c
new file mode 100644
index 00000000000..ab21133e678
--- /dev/null
+++ b/drivers/regulator/tps51632-regulator.c
@@ -0,0 +1,342 @@
+/*
+ * tps51632-regulator.c -- TI TPS51632
+ *
+ * Regulator driver for TPS51632 3-2-1 Phase D-Cap Step Down Driverless
+ * Controller with serial VID control and DVFS.
+ *
+ * Copyright (c) 2012, NVIDIA Corporation.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * 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/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/tps51632-regulator.h>
+#include <linux/slab.h>
+
+/* Register definitions */
+#define TPS51632_VOLTAGE_SELECT_REG 0x0
+#define TPS51632_VOLTAGE_BASE_REG 0x1
+#define TPS51632_OFFSET_REG 0x2
+#define TPS51632_IMON_REG 0x3
+#define TPS51632_VMAX_REG 0x4
+#define TPS51632_DVFS_CONTROL_REG 0x5
+#define TPS51632_POWER_STATE_REG 0x6
+#define TPS51632_SLEW_REGS 0x7
+#define TPS51632_FAULT_REG 0x14
+
+#define TPS51632_MAX_REG 0x15
+
+#define TPS51632_VOUT_MASK 0x7F
+#define TPS51632_VOUT_OFFSET_MASK 0x1F
+#define TPS51632_VMAX_MASK 0x7F
+#define TPS51632_VMAX_LOCK 0x80
+
+/* TPS51632_DVFS_CONTROL_REG */
+#define TPS51632_DVFS_PWMEN 0x1
+#define TPS51632_DVFS_STEP_20 0x2
+#define TPS51632_DVFS_VMAX_PG 0x4
+#define TPS51632_DVFS_PWMRST 0x8
+#define TPS51632_DVFS_OCA_EN 0x10
+#define TPS51632_DVFS_FCCM 0x20
+
+/* TPS51632_POWER_STATE_REG */
+#define TPS51632_POWER_STATE_MASK 0x03
+#define TPS51632_POWER_STATE_MULTI_PHASE_CCM 0x0
+#define TPS51632_POWER_STATE_SINGLE_PHASE_CCM 0x1
+#define TPS51632_POWER_STATE_SINGLE_PHASE_DCM 0x2
+
+#define TPS51632_MIN_VOLATGE 500000
+#define TPS51632_MAX_VOLATGE 1520000
+#define TPS51632_VOLATGE_STEP_10mV 10000
+#define TPS51632_VOLATGE_STEP_20mV 20000
+#define TPS51632_MAX_VSEL 0x7F
+#define TPS51632_MIN_VSEL 0x19
+#define TPS51632_DEFAULT_RAMP_DELAY 6000
+#define TPS51632_VOLT_VSEL(uV) \
+ (DIV_ROUND_UP(uV - TPS51632_MIN_VOLATGE, \
+ TPS51632_VOLATGE_STEP_10mV) + \
+ TPS51632_MIN_VSEL)
+
+/* TPS51632 chip information */
+struct tps51632_chip {
+ struct device *dev;
+ struct regulator_desc desc;
+ struct regulator_dev *rdev;
+ struct regmap *regmap;
+ bool enable_pwm_dvfs;
+};
+
+static int tps51632_dcdc_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct tps51632_chip *tps = rdev_get_drvdata(rdev);
+ unsigned int data;
+ int ret;
+ unsigned int reg = TPS51632_VOLTAGE_SELECT_REG;
+ int vsel;
+
+ if (tps->enable_pwm_dvfs)
+ reg = TPS51632_VOLTAGE_BASE_REG;
+
+ ret = regmap_read(tps->regmap, reg, &data);
+ if (ret < 0) {
+ dev_err(tps->dev, "reg read failed, err %d\n", ret);
+ return ret;
+ }
+
+ vsel = data & TPS51632_VOUT_MASK;
+ return vsel;
+}
+
+static int tps51632_dcdc_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct tps51632_chip *tps = rdev_get_drvdata(rdev);
+ int ret;
+ unsigned int reg = TPS51632_VOLTAGE_SELECT_REG;
+
+ if (tps->enable_pwm_dvfs)
+ reg = TPS51632_VOLTAGE_BASE_REG;
+
+ if (selector > TPS51632_MAX_VSEL)
+ return -EINVAL;
+
+ ret = regmap_write(tps->regmap, reg, selector);
+ if (ret < 0)
+ dev_err(tps->dev, "reg write failed, err %d\n", ret);
+ return ret;
+}
+
+static int tps51632_dcdc_set_ramp_delay(struct regulator_dev *rdev,
+ int ramp_delay)
+{
+ struct tps51632_chip *tps = rdev_get_drvdata(rdev);
+ int bit = ramp_delay/6000;
+ int ret;
+
+ if (bit)
+ bit--;
+ ret = regmap_write(tps->regmap, TPS51632_SLEW_REGS, BIT(bit));
+ if (ret < 0)
+ dev_err(tps->dev, "SLEW reg write failed, err %d\n", ret);
+ return ret;
+}
+
+static struct regulator_ops tps51632_dcdc_ops = {
+ .get_voltage_sel = tps51632_dcdc_get_voltage_sel,
+ .set_voltage_sel = tps51632_dcdc_set_voltage_sel,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_ramp_delay = tps51632_dcdc_set_ramp_delay,
+};
+
+static int tps51632_init_dcdc(struct tps51632_chip *tps,
+ struct tps51632_regulator_platform_data *pdata)
+{
+ int ret;
+ uint8_t control = 0;
+ int vsel;
+
+ if (!pdata->enable_pwm_dvfs)
+ goto skip_pwm_config;
+
+ control |= TPS51632_DVFS_PWMEN;
+ tps->enable_pwm_dvfs = pdata->enable_pwm_dvfs;
+ vsel = TPS51632_VOLT_VSEL(pdata->base_voltage_uV);
+ ret = regmap_write(tps->regmap, TPS51632_VOLTAGE_BASE_REG, vsel);
+ if (ret < 0) {
+ dev_err(tps->dev, "BASE reg write failed, err %d\n", ret);
+ return ret;
+ }
+
+ if (pdata->dvfs_step_20mV)
+ control |= TPS51632_DVFS_STEP_20;
+
+ if (pdata->max_voltage_uV) {
+ unsigned int vmax;
+ /**
+ * TPS51632 hw behavior: VMAX register can be write only
+ * once as it get locked after first write. The lock get
+ * reset only when device is power-reset.
+ * Write register only when lock bit is not enabled.
+ */
+ ret = regmap_read(tps->regmap, TPS51632_VMAX_REG, &vmax);
+ if (ret < 0) {
+ dev_err(tps->dev, "VMAX read failed, err %d\n", ret);
+ return ret;
+ }
+ if (!(vmax & TPS51632_VMAX_LOCK)) {
+ vsel = TPS51632_VOLT_VSEL(pdata->max_voltage_uV);
+ ret = regmap_write(tps->regmap, TPS51632_VMAX_REG,
+ vsel);
+ if (ret < 0) {
+ dev_err(tps->dev,
+ "VMAX write failed, err %d\n", ret);
+ return ret;
+ }
+ }
+ }
+
+skip_pwm_config:
+ ret = regmap_write(tps->regmap, TPS51632_DVFS_CONTROL_REG, control);
+ if (ret < 0)
+ dev_err(tps->dev, "DVFS reg write failed, err %d\n", ret);
+ return ret;
+}
+
+static bool rd_wr_reg(struct device *dev, unsigned int reg)
+{
+ if ((reg >= 0x8) && (reg <= 0x10))
+ return false;
+ return true;
+}
+
+static const struct regmap_config tps51632_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = rd_wr_reg,
+ .readable_reg = rd_wr_reg,
+ .max_register = TPS51632_MAX_REG - 1,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int tps51632_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tps51632_regulator_platform_data *pdata;
+ struct regulator_dev *rdev;
+ struct tps51632_chip *tps;
+ int ret;
+ struct regulator_config config = { };
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->dev, "No Platform data\n");
+ return -EINVAL;
+ }
+
+ if (pdata->enable_pwm_dvfs) {
+ if ((pdata->base_voltage_uV < TPS51632_MIN_VOLATGE) ||
+ (pdata->base_voltage_uV > TPS51632_MAX_VOLATGE)) {
+ dev_err(&client->dev, "Invalid base_voltage_uV setting\n");
+ return -EINVAL;
+ }
+
+ if ((pdata->max_voltage_uV) &&
+ ((pdata->max_voltage_uV < TPS51632_MIN_VOLATGE) ||
+ (pdata->max_voltage_uV > TPS51632_MAX_VOLATGE))) {
+ dev_err(&client->dev, "Invalid max_voltage_uV setting\n");
+ return -EINVAL;
+ }
+ }
+
+ tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
+ if (!tps) {
+ dev_err(&client->dev, "Memory allocation failed\n");
+ return -ENOMEM;
+ }
+
+ tps->dev = &client->dev;
+ tps->desc.name = id->name;
+ tps->desc.id = 0;
+ tps->desc.ramp_delay = TPS51632_DEFAULT_RAMP_DELAY;
+ tps->desc.min_uV = TPS51632_MIN_VOLATGE;
+ tps->desc.uV_step = TPS51632_VOLATGE_STEP_10mV;
+ tps->desc.linear_min_sel = TPS51632_MIN_VSEL;
+ tps->desc.n_voltages = TPS51632_MAX_VSEL + 1;
+ tps->desc.ops = &tps51632_dcdc_ops;
+ tps->desc.type = REGULATOR_VOLTAGE;
+ tps->desc.owner = THIS_MODULE;
+
+ tps->regmap = devm_regmap_init_i2c(client, &tps51632_regmap_config);
+ if (IS_ERR(tps->regmap)) {
+ ret = PTR_ERR(tps->regmap);
+ dev_err(&client->dev, "regmap init failed, err %d\n", ret);
+ return ret;
+ }
+ i2c_set_clientdata(client, tps);
+
+ ret = tps51632_init_dcdc(tps, pdata);
+ if (ret < 0) {
+ dev_err(tps->dev, "Init failed, err = %d\n", ret);
+ return ret;
+ }
+
+ /* Register the regulators */
+ config.dev = &client->dev;
+ config.init_data = pdata->reg_init_data;
+ config.driver_data = tps;
+ config.regmap = tps->regmap;
+ config.of_node = client->dev.of_node;
+
+ rdev = regulator_register(&tps->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(tps->dev, "regulator register failed\n");
+ return PTR_ERR(rdev);
+ }
+
+ tps->rdev = rdev;
+ return 0;
+}
+
+static int tps51632_remove(struct i2c_client *client)
+{
+ struct tps51632_chip *tps = i2c_get_clientdata(client);
+
+ regulator_unregister(tps->rdev);
+ return 0;
+}
+
+static const struct i2c_device_id tps51632_id[] = {
+ {.name = "tps51632",},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, tps51632_id);
+
+static struct i2c_driver tps51632_i2c_driver = {
+ .driver = {
+ .name = "tps51632",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps51632_probe,
+ .remove = tps51632_remove,
+ .id_table = tps51632_id,
+};
+
+static int __init tps51632_init(void)
+{
+ return i2c_add_driver(&tps51632_i2c_driver);
+}
+subsys_initcall(tps51632_init);
+
+static void __exit tps51632_cleanup(void)
+{
+ i2c_del_driver(&tps51632_i2c_driver);
+}
+module_exit(tps51632_cleanup);
+
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_DESCRIPTION("TPS51632 voltage regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/tps6105x-regulator.c b/drivers/regulator/tps6105x-regulator.c
index 1378409efae..ec9453ffb77 100644
--- a/drivers/regulator/tps6105x-regulator.c
+++ b/drivers/regulator/tps6105x-regulator.c
@@ -127,7 +127,7 @@ static const struct regulator_desc tps6105x_regulator_desc = {
/*
* Registers the chip as a voltage regulator
*/
-static int __devinit tps6105x_regulator_probe(struct platform_device *pdev)
+static int tps6105x_regulator_probe(struct platform_device *pdev)
{
struct tps6105x *tps6105x = dev_get_platdata(&pdev->dev);
struct tps6105x_platform_data *pdata = tps6105x->pdata;
@@ -159,7 +159,7 @@ static int __devinit tps6105x_regulator_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit tps6105x_regulator_remove(struct platform_device *pdev)
+static int tps6105x_regulator_remove(struct platform_device *pdev)
{
struct tps6105x *tps6105x = dev_get_platdata(&pdev->dev);
regulator_unregister(tps6105x->regulator);
@@ -172,7 +172,7 @@ static struct platform_driver tps6105x_regulator_driver = {
.owner = THIS_MODULE,
},
.probe = tps6105x_regulator_probe,
- .remove = __devexit_p(tps6105x_regulator_remove),
+ .remove = tps6105x_regulator_remove,
};
static __init int tps6105x_regulator_init(void)
diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c
index 68729a7c870..acbd63fde41 100644
--- a/drivers/regulator/tps62360-regulator.c
+++ b/drivers/regulator/tps62360-regulator.c
@@ -243,7 +243,7 @@ static struct regulator_ops tps62360_dcdc_ops = {
.get_mode = tps62360_get_mode,
};
-static int __devinit tps62360_init_dcdc(struct tps62360_chip *tps,
+static int tps62360_init_dcdc(struct tps62360_chip *tps,
struct tps62360_regulator_platform_data *pdata)
{
int ret;
@@ -339,7 +339,7 @@ static const struct of_device_id tps62360_of_match[] = {
MODULE_DEVICE_TABLE(of, tps62360_of_match);
#endif
-static int __devinit tps62360_probe(struct i2c_client *client,
+static int tps62360_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regulator_config config = { };
@@ -490,7 +490,7 @@ static int __devinit tps62360_probe(struct i2c_client *client,
*
* Unregister TPS driver as an i2c client device driver
*/
-static int __devexit tps62360_remove(struct i2c_client *client)
+static int tps62360_remove(struct i2c_client *client)
{
struct tps62360_chip *tps = i2c_get_clientdata(client);
@@ -531,7 +531,7 @@ static struct i2c_driver tps62360_i2c_driver = {
.of_match_table = of_match_ptr(tps62360_of_match),
},
.probe = tps62360_probe,
- .remove = __devexit_p(tps62360_remove),
+ .remove = tps62360_remove,
.shutdown = tps62360_shutdown,
.id_table = tps62360_id,
};
diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c
index 6998d579d07..9b9af6d889c 100644
--- a/drivers/regulator/tps65023-regulator.c
+++ b/drivers/regulator/tps65023-regulator.c
@@ -219,7 +219,7 @@ static struct regmap_config tps65023_regmap_config = {
.val_bits = 8,
};
-static int __devinit tps_65023_probe(struct i2c_client *client,
+static int tps_65023_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
const struct tps_driver_data *drv_data = (void *)id->driver_data;
@@ -319,7 +319,7 @@ static int __devinit tps_65023_probe(struct i2c_client *client,
return error;
}
-static int __devexit tps_65023_remove(struct i2c_client *client)
+static int tps_65023_remove(struct i2c_client *client)
{
struct tps_pmic *tps = i2c_get_clientdata(client);
int i;
@@ -446,7 +446,7 @@ static struct i2c_driver tps_65023_i2c_driver = {
.owner = THIS_MODULE,
},
.probe = tps_65023_probe,
- .remove = __devexit_p(tps_65023_remove),
+ .remove = tps_65023_remove,
.id_table = tps_65023_id,
};
diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c
index 07d01ccdf30..0233cfb5656 100644
--- a/drivers/regulator/tps6507x-regulator.c
+++ b/drivers/regulator/tps6507x-regulator.c
@@ -356,7 +356,7 @@ static struct regulator_ops tps6507x_pmic_ops = {
.list_voltage = regulator_list_voltage_table,
};
-static __devinit int tps6507x_pmic_probe(struct platform_device *pdev)
+static int tps6507x_pmic_probe(struct platform_device *pdev)
{
struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent);
struct tps_info *info = &tps6507x_pmic_regs[0];
@@ -439,7 +439,7 @@ fail:
return error;
}
-static int __devexit tps6507x_pmic_remove(struct platform_device *pdev)
+static int tps6507x_pmic_remove(struct platform_device *pdev)
{
struct tps6507x_dev *tps6507x_dev = platform_get_drvdata(pdev);
struct tps6507x_pmic *tps = tps6507x_dev->pmic;
@@ -456,7 +456,7 @@ static struct platform_driver tps6507x_pmic_driver = {
.owner = THIS_MODULE,
},
.probe = tps6507x_pmic_probe,
- .remove = __devexit_p(tps6507x_pmic_remove),
+ .remove = tps6507x_pmic_remove,
};
static int __init tps6507x_pmic_init(void)
diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c
index 001ad554ac6..41c391789c9 100644
--- a/drivers/regulator/tps65090-regulator.c
+++ b/drivers/regulator/tps65090-regulator.c
@@ -18,119 +18,240 @@
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/mfd/tps65090.h>
-#include <linux/regulator/tps65090-regulator.h>
struct tps65090_regulator {
- int id;
- /* used by regulator core */
- struct regulator_desc desc;
-
- /* Device */
struct device *dev;
+ struct regulator_desc *desc;
+ struct regulator_dev *rdev;
+};
+
+static struct regulator_ops tps65090_ext_control_ops = {
+};
+
+static struct regulator_ops tps65090_reg_contol_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
};
-static struct regulator_ops tps65090_ops = {
- .enable = regulator_enable_regmap,
- .disable = regulator_disable_regmap,
- .is_enabled = regulator_is_enabled_regmap,
+static struct regulator_ops tps65090_ldo_ops = {
};
-#define tps65090_REG(_id) \
+#define tps65090_REG_DESC(_id, _sname, _en_reg, _ops) \
{ \
- .id = TPS65090_ID_##_id, \
- .desc = { \
- .name = tps65090_rails(_id), \
- .id = TPS65090_ID_##_id, \
- .ops = &tps65090_ops, \
- .type = REGULATOR_VOLTAGE, \
- .owner = THIS_MODULE, \
- .enable_reg = (TPS65090_ID_##_id) + 12, \
- .enable_mask = BIT(0), \
- }, \
+ .name = "TPS65090_RAILS"#_id, \
+ .supply_name = _sname, \
+ .id = TPS65090_REGULATOR_##_id, \
+ .ops = &_ops, \
+ .enable_reg = _en_reg, \
+ .enable_mask = BIT(0), \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
}
-static struct tps65090_regulator TPS65090_regulator[] = {
- tps65090_REG(DCDC1),
- tps65090_REG(DCDC2),
- tps65090_REG(DCDC3),
- tps65090_REG(FET1),
- tps65090_REG(FET2),
- tps65090_REG(FET3),
- tps65090_REG(FET4),
- tps65090_REG(FET5),
- tps65090_REG(FET6),
- tps65090_REG(FET7),
+static struct regulator_desc tps65090_regulator_desc[] = {
+ tps65090_REG_DESC(DCDC1, "vsys1", 0x0C, tps65090_reg_contol_ops),
+ tps65090_REG_DESC(DCDC2, "vsys2", 0x0D, tps65090_reg_contol_ops),
+ tps65090_REG_DESC(DCDC3, "vsys3", 0x0E, tps65090_reg_contol_ops),
+ tps65090_REG_DESC(FET1, "infet1", 0x0F, tps65090_reg_contol_ops),
+ tps65090_REG_DESC(FET2, "infet2", 0x10, tps65090_reg_contol_ops),
+ tps65090_REG_DESC(FET3, "infet3", 0x11, tps65090_reg_contol_ops),
+ tps65090_REG_DESC(FET4, "infet4", 0x12, tps65090_reg_contol_ops),
+ tps65090_REG_DESC(FET5, "infet5", 0x13, tps65090_reg_contol_ops),
+ tps65090_REG_DESC(FET6, "infet6", 0x14, tps65090_reg_contol_ops),
+ tps65090_REG_DESC(FET7, "infet7", 0x15, tps65090_reg_contol_ops),
+ tps65090_REG_DESC(LDO1, "vsys_l1", 0, tps65090_ldo_ops),
+ tps65090_REG_DESC(LDO2, "vsys_l2", 0, tps65090_ldo_ops),
};
-static inline struct tps65090_regulator *find_regulator_info(int id)
+static inline bool is_dcdc(int id)
{
- struct tps65090_regulator *ri;
- int i;
+ switch (id) {
+ case TPS65090_REGULATOR_DCDC1:
+ case TPS65090_REGULATOR_DCDC2:
+ case TPS65090_REGULATOR_DCDC3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int tps65090_config_ext_control(
+ struct tps65090_regulator *ri, bool enable)
+{
+ int ret;
+ struct device *parent = ri->dev->parent;
+ unsigned int reg_en_reg = ri->desc->enable_reg;
+
+ if (enable)
+ ret = tps65090_set_bits(parent, reg_en_reg, 1);
+ else
+ ret = tps65090_clr_bits(parent, reg_en_reg, 1);
+ if (ret < 0)
+ dev_err(ri->dev, "Error in updating reg 0x%x\n", reg_en_reg);
+ return ret;
+}
+
+static int tps65090_regulator_disable_ext_control(
+ struct tps65090_regulator *ri,
+ struct tps65090_regulator_plat_data *tps_pdata)
+{
+ int ret = 0;
+ struct device *parent = ri->dev->parent;
+ unsigned int reg_en_reg = ri->desc->enable_reg;
+
+ /*
+ * First enable output for internal control if require.
+ * And then disable external control.
+ */
+ if (tps_pdata->reg_init_data->constraints.always_on ||
+ tps_pdata->reg_init_data->constraints.boot_on) {
+ ret = tps65090_set_bits(parent, reg_en_reg, 0);
+ if (ret < 0) {
+ dev_err(ri->dev, "Error in set reg 0x%x\n", reg_en_reg);
+ return ret;
+ }
+ }
+ return tps65090_config_ext_control(ri, false);
+}
+
+static void tps65090_configure_regulator_config(
+ struct tps65090_regulator_plat_data *tps_pdata,
+ struct regulator_config *config)
+{
+ if (gpio_is_valid(tps_pdata->gpio)) {
+ int gpio_flag = GPIOF_OUT_INIT_LOW;
+
+ if (tps_pdata->reg_init_data->constraints.always_on ||
+ tps_pdata->reg_init_data->constraints.boot_on)
+ gpio_flag = GPIOF_OUT_INIT_HIGH;
- for (i = 0; i < ARRAY_SIZE(TPS65090_regulator); i++) {
- ri = &TPS65090_regulator[i];
- if (ri->desc.id == id)
- return ri;
+ config->ena_gpio = tps_pdata->gpio;
+ config->ena_gpio_flags = gpio_flag;
}
- return NULL;
}
-static int __devinit tps65090_regulator_probe(struct platform_device *pdev)
+static int tps65090_regulator_probe(struct platform_device *pdev)
{
struct tps65090 *tps65090_mfd = dev_get_drvdata(pdev->dev.parent);
struct tps65090_regulator *ri = NULL;
struct regulator_config config = { };
struct regulator_dev *rdev;
- struct tps65090_regulator_platform_data *tps_pdata;
- int id = pdev->id;
+ struct tps65090_regulator_plat_data *tps_pdata;
+ struct tps65090_regulator *pmic;
+ struct tps65090_platform_data *tps65090_pdata;
+ int num;
+ int ret;
- dev_dbg(&pdev->dev, "Probing regulator %d\n", id);
+ dev_dbg(&pdev->dev, "Probing regulator\n");
- ri = find_regulator_info(id);
- if (ri == NULL) {
- dev_err(&pdev->dev, "invalid regulator ID specified\n");
+ tps65090_pdata = dev_get_platdata(pdev->dev.parent);
+ if (!tps65090_pdata) {
+ dev_err(&pdev->dev, "Platform data missing\n");
return -EINVAL;
}
- tps_pdata = pdev->dev.platform_data;
- ri->dev = &pdev->dev;
-
- config.dev = &pdev->dev;
- config.init_data = &tps_pdata->regulator;
- config.driver_data = ri;
- config.regmap = tps65090_mfd->rmap;
-
- rdev = regulator_register(&ri->desc, &config);
- if (IS_ERR(rdev)) {
- dev_err(&pdev->dev, "failed to register regulator %s\n",
- ri->desc.name);
- return PTR_ERR(rdev);
+
+ pmic = devm_kzalloc(&pdev->dev, TPS65090_REGULATOR_MAX * sizeof(*pmic),
+ GFP_KERNEL);
+ if (!pmic) {
+ dev_err(&pdev->dev, "mem alloc for pmic failed\n");
+ return -ENOMEM;
+ }
+
+ for (num = 0; num < TPS65090_REGULATOR_MAX; num++) {
+ tps_pdata = tps65090_pdata->reg_pdata[num];
+
+ ri = &pmic[num];
+ ri->dev = &pdev->dev;
+ ri->desc = &tps65090_regulator_desc[num];
+
+ /*
+ * TPS5090 DCDC support the control from external digital input.
+ * Configure it as per platform data.
+ */
+ if (tps_pdata && is_dcdc(num) && tps_pdata->reg_init_data) {
+ if (tps_pdata->enable_ext_control) {
+ tps65090_configure_regulator_config(
+ tps_pdata, &config);
+ ri->desc->ops = &tps65090_ext_control_ops;
+ } else {
+ ret = tps65090_regulator_disable_ext_control(
+ ri, tps_pdata);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "failed disable ext control\n");
+ goto scrub;
+ }
+ }
+ }
+
+ config.dev = &pdev->dev;
+ config.driver_data = ri;
+ config.regmap = tps65090_mfd->rmap;
+ if (tps_pdata)
+ config.init_data = tps_pdata->reg_init_data;
+ else
+ config.init_data = NULL;
+
+ rdev = regulator_register(ri->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ ri->desc->name);
+ ret = PTR_ERR(rdev);
+ goto scrub;
+ }
+ ri->rdev = rdev;
+
+ /* Enable external control if it is require */
+ if (tps_pdata && is_dcdc(num) && tps_pdata->reg_init_data &&
+ tps_pdata->enable_ext_control) {
+ ret = tps65090_config_ext_control(ri, true);
+ if (ret < 0) {
+ /* Increment num to get unregister rdev */
+ num++;
+ goto scrub;
+ }
+ }
}
- platform_set_drvdata(pdev, rdev);
+ platform_set_drvdata(pdev, pmic);
return 0;
+
+scrub:
+ while (--num >= 0) {
+ ri = &pmic[num];
+ regulator_unregister(ri->rdev);
+ }
+ return ret;
}
-static int __devexit tps65090_regulator_remove(struct platform_device *pdev)
+static int tps65090_regulator_remove(struct platform_device *pdev)
{
- struct regulator_dev *rdev = platform_get_drvdata(pdev);
+ struct tps65090_regulator *pmic = platform_get_drvdata(pdev);
+ struct tps65090_regulator *ri;
+ int num;
- regulator_unregister(rdev);
+ for (num = 0; num < TPS65090_REGULATOR_MAX; ++num) {
+ ri = &pmic[num];
+ regulator_unregister(ri->rdev);
+ }
return 0;
}
static struct platform_driver tps65090_regulator_driver = {
.driver = {
- .name = "tps65090-regulator",
+ .name = "tps65090-pmic",
.owner = THIS_MODULE,
},
.probe = tps65090_regulator_probe,
- .remove = __devexit_p(tps65090_regulator_remove),
+ .remove = tps65090_regulator_remove,
};
static int __init tps65090_regulator_init(void)
@@ -148,3 +269,4 @@ module_exit(tps65090_regulator_exit);
MODULE_DESCRIPTION("tps65090 regulator driver");
MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>");
MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tps65090-pmic");
diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c
index ab00cab905b..73dce766412 100644
--- a/drivers/regulator/tps65217-regulator.c
+++ b/drivers/regulator/tps65217-regulator.c
@@ -332,7 +332,7 @@ static struct tps65217_board *tps65217_parse_dt(struct platform_device *pdev)
}
#endif
-static int __devinit tps65217_regulator_probe(struct platform_device *pdev)
+static int tps65217_regulator_probe(struct platform_device *pdev)
{
struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
struct tps65217_board *pdata = dev_get_platdata(tps->dev);
@@ -397,7 +397,7 @@ err_unregister_regulator:
return ret;
}
-static int __devexit tps65217_regulator_remove(struct platform_device *pdev)
+static int tps65217_regulator_remove(struct platform_device *pdev)
{
struct tps65217 *tps = platform_get_drvdata(pdev);
unsigned int i;
@@ -415,7 +415,7 @@ static struct platform_driver tps65217_regulator_driver = {
.name = "tps65217-pmic",
},
.probe = tps65217_regulator_probe,
- .remove = __devexit_p(tps65217_regulator_remove),
+ .remove = tps65217_regulator_remove,
};
static int __init tps65217_regulator_init(void)
diff --git a/drivers/regulator/tps6524x-regulator.c b/drivers/regulator/tps6524x-regulator.c
index 058d2f2675e..843ee0a9bb9 100644
--- a/drivers/regulator/tps6524x-regulator.c
+++ b/drivers/regulator/tps6524x-regulator.c
@@ -592,7 +592,7 @@ static int pmic_remove(struct spi_device *spi)
return 0;
}
-static int __devinit pmic_probe(struct spi_device *spi)
+static int pmic_probe(struct spi_device *spi)
{
struct tps6524x *hw;
struct device *dev = &spi->dev;
@@ -649,7 +649,7 @@ fail:
static struct spi_driver pmic_driver = {
.probe = pmic_probe,
- .remove = __devexit_p(pmic_remove),
+ .remove = pmic_remove,
.driver = {
.name = "tps6524x",
.owner = THIS_MODULE,
diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c
index ce1e7cb8d51..f86da672c75 100644
--- a/drivers/regulator/tps6586x-regulator.c
+++ b/drivers/regulator/tps6586x-regulator.c
@@ -17,10 +17,12 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
+#include <linux/of.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
#include <linux/mfd/tps6586x.h>
/* supply control and voltage setting */
@@ -255,10 +257,10 @@ static inline int tps6586x_regulator_preinit(struct device *parent,
1 << ri->enable_bit[1]);
}
-static int tps6586x_regulator_set_slew_rate(struct platform_device *pdev)
+static int tps6586x_regulator_set_slew_rate(struct platform_device *pdev,
+ int id, struct regulator_init_data *p)
{
struct device *parent = pdev->dev.parent;
- struct regulator_init_data *p = pdev->dev.platform_data;
struct tps6586x_settings *setting = p->driver_data;
uint8_t reg;
@@ -269,7 +271,7 @@ static int tps6586x_regulator_set_slew_rate(struct platform_device *pdev)
return 0;
/* only SM0 and SM1 can have the slew rate settings */
- switch (pdev->id) {
+ switch (id) {
case TPS6586X_ID_SM_0:
reg = TPS6586X_SM0SL;
break;
@@ -298,58 +300,185 @@ static inline struct tps6586x_regulator *find_regulator_info(int id)
return NULL;
}
-static int __devinit tps6586x_regulator_probe(struct platform_device *pdev)
+#ifdef CONFIG_OF
+static struct of_regulator_match tps6586x_matches[] = {
+ { .name = "sys", .driver_data = (void *)TPS6586X_ID_SYS },
+ { .name = "sm0", .driver_data = (void *)TPS6586X_ID_SM_0 },
+ { .name = "sm1", .driver_data = (void *)TPS6586X_ID_SM_1 },
+ { .name = "sm2", .driver_data = (void *)TPS6586X_ID_SM_2 },
+ { .name = "ldo0", .driver_data = (void *)TPS6586X_ID_LDO_0 },
+ { .name = "ldo1", .driver_data = (void *)TPS6586X_ID_LDO_1 },
+ { .name = "ldo2", .driver_data = (void *)TPS6586X_ID_LDO_2 },
+ { .name = "ldo3", .driver_data = (void *)TPS6586X_ID_LDO_3 },
+ { .name = "ldo4", .driver_data = (void *)TPS6586X_ID_LDO_4 },
+ { .name = "ldo5", .driver_data = (void *)TPS6586X_ID_LDO_5 },
+ { .name = "ldo6", .driver_data = (void *)TPS6586X_ID_LDO_6 },
+ { .name = "ldo7", .driver_data = (void *)TPS6586X_ID_LDO_7 },
+ { .name = "ldo8", .driver_data = (void *)TPS6586X_ID_LDO_8 },
+ { .name = "ldo9", .driver_data = (void *)TPS6586X_ID_LDO_9 },
+ { .name = "ldo_rtc", .driver_data = (void *)TPS6586X_ID_LDO_RTC },
+};
+
+static struct tps6586x_platform_data *tps6586x_parse_regulator_dt(
+ struct platform_device *pdev,
+ struct of_regulator_match **tps6586x_reg_matches)
+{
+ const unsigned int num = ARRAY_SIZE(tps6586x_matches);
+ struct device_node *np = pdev->dev.parent->of_node;
+ struct device_node *regs;
+ const char *sys_rail = NULL;
+ unsigned int i;
+ struct tps6586x_platform_data *pdata;
+ int err;
+
+ regs = of_find_node_by_name(np, "regulators");
+ if (!regs) {
+ dev_err(&pdev->dev, "regulator node not found\n");
+ return NULL;
+ }
+
+ err = of_regulator_match(&pdev->dev, regs, tps6586x_matches, num);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Regulator match failed, e %d\n", err);
+ of_node_put(regs);
+ return NULL;
+ }
+
+ of_node_put(regs);
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&pdev->dev, "Memory alloction failed\n");
+ return NULL;
+ }
+
+ for (i = 0; i < num; i++) {
+ int id;
+ if (!tps6586x_matches[i].init_data)
+ continue;
+
+ pdata->reg_init_data[i] = tps6586x_matches[i].init_data;
+ id = (int)tps6586x_matches[i].driver_data;
+ if (id == TPS6586X_ID_SYS)
+ sys_rail = pdata->reg_init_data[i]->constraints.name;
+
+ if ((id == TPS6586X_ID_LDO_5) || (id == TPS6586X_ID_LDO_RTC))
+ pdata->reg_init_data[i]->supply_regulator = sys_rail;
+ }
+ *tps6586x_reg_matches = tps6586x_matches;
+ return pdata;
+}
+#else
+static struct tps6586x_platform_data *tps6586x_parse_regulator_dt(
+ struct platform_device *pdev,
+ struct of_regulator_match **tps6586x_reg_matches)
+{
+ *tps6586x_reg_matches = NULL;
+ return NULL;
+}
+#endif
+
+static int tps6586x_regulator_probe(struct platform_device *pdev)
{
struct tps6586x_regulator *ri = NULL;
struct regulator_config config = { };
- struct regulator_dev *rdev;
- int id = pdev->id;
+ struct regulator_dev **rdev;
+ struct regulator_init_data *reg_data;
+ struct tps6586x_platform_data *pdata;
+ struct of_regulator_match *tps6586x_reg_matches = NULL;
+ int id;
int err;
- dev_dbg(&pdev->dev, "Probing regulator %d\n", id);
+ dev_dbg(&pdev->dev, "Probing regulator\n");
- ri = find_regulator_info(id);
- if (ri == NULL) {
- dev_err(&pdev->dev, "invalid regulator ID specified\n");
- return -EINVAL;
- }
+ pdata = dev_get_platdata(pdev->dev.parent);
+ if ((!pdata) && (pdev->dev.parent->of_node))
+ pdata = tps6586x_parse_regulator_dt(pdev,
+ &tps6586x_reg_matches);
- err = tps6586x_regulator_preinit(pdev->dev.parent, ri);
- if (err)
- return err;
+ if (!pdata) {
+ dev_err(&pdev->dev, "Platform data not available, exiting\n");
+ return -ENODEV;
+ }
- config.dev = pdev->dev.parent;
- config.of_node = pdev->dev.of_node;
- config.init_data = pdev->dev.platform_data;
- config.driver_data = ri;
+ rdev = devm_kzalloc(&pdev->dev, TPS6586X_ID_MAX_REGULATOR *
+ sizeof(*rdev), GFP_KERNEL);
+ if (!rdev) {
+ dev_err(&pdev->dev, "Mmemory alloc failed\n");
+ return -ENOMEM;
+ }
- rdev = regulator_register(&ri->desc, &config);
- if (IS_ERR(rdev)) {
- dev_err(&pdev->dev, "failed to register regulator %s\n",
- ri->desc.name);
- return PTR_ERR(rdev);
+ for (id = 0; id < TPS6586X_ID_MAX_REGULATOR; ++id) {
+ reg_data = pdata->reg_init_data[id];
+
+ ri = find_regulator_info(id);
+ if (!ri) {
+ dev_err(&pdev->dev, "invalid regulator ID specified\n");
+ err = -EINVAL;
+ goto fail;
+ }
+
+ err = tps6586x_regulator_preinit(pdev->dev.parent, ri);
+ if (err) {
+ dev_err(&pdev->dev,
+ "regulator %d preinit failed, e %d\n", id, err);
+ goto fail;
+ }
+
+ config.dev = pdev->dev.parent;
+ config.init_data = reg_data;
+ config.driver_data = ri;
+
+ if (tps6586x_reg_matches)
+ config.of_node = tps6586x_reg_matches[id].of_node;
+
+ rdev[id] = regulator_register(&ri->desc, &config);
+ if (IS_ERR(rdev[id])) {
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ ri->desc.name);
+ err = PTR_ERR(rdev[id]);
+ goto fail;
+ }
+
+ if (reg_data) {
+ err = tps6586x_regulator_set_slew_rate(pdev, id,
+ reg_data);
+ if (err < 0) {
+ dev_err(&pdev->dev,
+ "Slew rate config failed, e %d\n", err);
+ regulator_unregister(rdev[id]);
+ goto fail;
+ }
+ }
}
platform_set_drvdata(pdev, rdev);
+ return 0;
- return tps6586x_regulator_set_slew_rate(pdev);
+fail:
+ while (--id >= 0)
+ regulator_unregister(rdev[id]);
+ return err;
}
-static int __devexit tps6586x_regulator_remove(struct platform_device *pdev)
+static int tps6586x_regulator_remove(struct platform_device *pdev)
{
- struct regulator_dev *rdev = platform_get_drvdata(pdev);
+ struct regulator_dev **rdev = platform_get_drvdata(pdev);
+ int id = TPS6586X_ID_MAX_REGULATOR;
+
+ while (--id >= 0)
+ regulator_unregister(rdev[id]);
- regulator_unregister(rdev);
return 0;
}
static struct platform_driver tps6586x_regulator_driver = {
.driver = {
- .name = "tps6586x-regulator",
+ .name = "tps6586x-pmic",
.owner = THIS_MODULE,
},
.probe = tps6586x_regulator_probe,
- .remove = __devexit_p(tps6586x_regulator_remove),
+ .remove = tps6586x_regulator_remove,
};
static int __init tps6586x_regulator_init(void)
diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c
index 793adda560c..59c3770fa77 100644
--- a/drivers/regulator/tps65910-regulator.c
+++ b/drivers/regulator/tps65910-regulator.c
@@ -38,6 +38,11 @@ static const unsigned int VIO_VSEL_table[] = {
/* VSEL tables for TPS65910 specific LDOs and dcdc's */
+/* supported VRTC voltages in microvolts */
+static const unsigned int VRTC_VSEL_table[] = {
+ 1800000,
+};
+
/* supported VDD3 voltages in microvolts */
static const unsigned int VDD3_VSEL_table[] = {
5000000,
@@ -95,6 +100,8 @@ static struct tps_info tps65910_regs[] = {
{
.name = "vrtc",
.vin_name = "vcc7",
+ .n_voltages = ARRAY_SIZE(VRTC_VSEL_table),
+ .voltage_table = VRTC_VSEL_table,
.enable_time_us = 2200,
},
{
@@ -1026,7 +1033,7 @@ static inline struct tps65910_board *tps65910_parse_dt_reg_data(
}
#endif
-static __devinit int tps65910_probe(struct platform_device *pdev)
+static int tps65910_probe(struct platform_device *pdev)
{
struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent);
struct regulator_config config = { };
@@ -1184,7 +1191,7 @@ err_unregister_regulator:
return err;
}
-static int __devexit tps65910_remove(struct platform_device *pdev)
+static int tps65910_remove(struct platform_device *pdev)
{
struct tps65910_reg *pmic = platform_get_drvdata(pdev);
int i;
@@ -1231,7 +1238,7 @@ static struct platform_driver tps65910_driver = {
.owner = THIS_MODULE,
},
.probe = tps65910_probe,
- .remove = __devexit_p(tps65910_remove),
+ .remove = tps65910_remove,
.shutdown = tps65910_shutdown,
};
diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c
index 18b2a1dcb4b..17e994e47dc 100644
--- a/drivers/regulator/tps65912-regulator.c
+++ b/drivers/regulator/tps65912-regulator.c
@@ -459,7 +459,7 @@ static struct regulator_ops tps65912_ops_ldo = {
.list_voltage = tps65912_list_voltage,
};
-static __devinit int tps65912_probe(struct platform_device *pdev)
+static int tps65912_probe(struct platform_device *pdev)
{
struct tps65912 *tps65912 = dev_get_drvdata(pdev->dev.parent);
struct regulator_config config = { };
@@ -525,7 +525,7 @@ err:
return err;
}
-static int __devexit tps65912_remove(struct platform_device *pdev)
+static int tps65912_remove(struct platform_device *pdev)
{
struct tps65912_reg *tps65912_reg = platform_get_drvdata(pdev);
int i;
@@ -541,7 +541,7 @@ static struct platform_driver tps65912_driver = {
.owner = THIS_MODULE,
},
.probe = tps65912_probe,
- .remove = __devexit_p(tps65912_remove),
+ .remove = tps65912_remove,
};
static int __init tps65912_init(void)
diff --git a/drivers/regulator/tps80031-regulator.c b/drivers/regulator/tps80031-regulator.c
new file mode 100644
index 00000000000..b15d711bc8c
--- /dev/null
+++ b/drivers/regulator/tps80031-regulator.c
@@ -0,0 +1,788 @@
+/*
+ * tps80031-regulator.c -- TI TPS80031 regulator driver.
+ *
+ * Regulator driver for TI TPS80031/TPS80032 Fully Integrated Power
+ * Management with Power Path and Battery Charger.
+ *
+ * Copyright (c) 2012, NVIDIA Corporation.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * 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/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mfd/tps80031.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/slab.h>
+
+/* Flags for DCDC Voltage reading */
+#define DCDC_OFFSET_EN BIT(0)
+#define DCDC_EXTENDED_EN BIT(1)
+#define TRACK_MODE_ENABLE BIT(2)
+
+#define SMPS_MULTOFFSET_VIO BIT(1)
+#define SMPS_MULTOFFSET_SMPS1 BIT(3)
+#define SMPS_MULTOFFSET_SMPS2 BIT(4)
+#define SMPS_MULTOFFSET_SMPS3 BIT(6)
+#define SMPS_MULTOFFSET_SMPS4 BIT(0)
+
+#define SMPS_CMD_MASK 0xC0
+#define SMPS_VSEL_MASK 0x3F
+#define LDO_VSEL_MASK 0x1F
+#define LDO_TRACK_VSEL_MASK 0x3F
+
+#define MISC2_LDOUSB_IN_VSYS BIT(4)
+#define MISC2_LDOUSB_IN_PMID BIT(3)
+#define MISC2_LDOUSB_IN_MASK 0x18
+
+#define MISC2_LDO3_SEL_VIB_VAL BIT(0)
+#define MISC2_LDO3_SEL_VIB_MASK 0x1
+
+#define BOOST_HW_PWR_EN BIT(5)
+#define BOOST_HW_PWR_EN_MASK BIT(5)
+
+#define OPA_MODE_EN BIT(6)
+#define OPA_MODE_EN_MASK BIT(6)
+
+#define USB_VBUS_CTRL_SET 0x04
+#define USB_VBUS_CTRL_CLR 0x05
+#define VBUS_DISCHRG 0x20
+
+struct tps80031_regulator_info {
+ /* Regulator register address.*/
+ u8 trans_reg;
+ u8 state_reg;
+ u8 force_reg;
+ u8 volt_reg;
+ u8 volt_id;
+
+ /*Power request bits */
+ int preq_bit;
+
+ /* used by regulator core */
+ struct regulator_desc desc;
+
+};
+
+struct tps80031_regulator {
+ struct device *dev;
+ struct regulator_dev *rdev;
+ struct tps80031_regulator_info *rinfo;
+
+ u8 device_flags;
+ unsigned int config_flags;
+ unsigned int ext_ctrl_flag;
+};
+
+static inline struct device *to_tps80031_dev(struct regulator_dev *rdev)
+{
+ return rdev_get_dev(rdev)->parent->parent;
+}
+
+static int tps80031_reg_is_enabled(struct regulator_dev *rdev)
+{
+ struct tps80031_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps80031_dev(rdev);
+ u8 reg_val;
+ int ret;
+
+ if (ri->ext_ctrl_flag & TPS80031_EXT_PWR_REQ)
+ return true;
+
+ ret = tps80031_read(parent, TPS80031_SLAVE_ID1, ri->rinfo->state_reg,
+ &reg_val);
+ if (ret < 0) {
+ dev_err(&rdev->dev, "Reg 0x%02x read failed, err = %d\n",
+ ri->rinfo->state_reg, ret);
+ return ret;
+ }
+ return ((reg_val & TPS80031_STATE_MASK) == TPS80031_STATE_ON);
+}
+
+static int tps80031_reg_enable(struct regulator_dev *rdev)
+{
+ struct tps80031_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps80031_dev(rdev);
+ int ret;
+
+ if (ri->ext_ctrl_flag & TPS80031_EXT_PWR_REQ)
+ return 0;
+
+ ret = tps80031_update(parent, TPS80031_SLAVE_ID1, ri->rinfo->state_reg,
+ TPS80031_STATE_ON, TPS80031_STATE_MASK);
+ if (ret < 0) {
+ dev_err(&rdev->dev, "Reg 0x%02x update failed, err = %d\n",
+ ri->rinfo->state_reg, ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int tps80031_reg_disable(struct regulator_dev *rdev)
+{
+ struct tps80031_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps80031_dev(rdev);
+ int ret;
+
+ if (ri->ext_ctrl_flag & TPS80031_EXT_PWR_REQ)
+ return 0;
+
+ ret = tps80031_update(parent, TPS80031_SLAVE_ID1, ri->rinfo->state_reg,
+ TPS80031_STATE_OFF, TPS80031_STATE_MASK);
+ if (ret < 0)
+ dev_err(&rdev->dev, "Reg 0x%02x update failed, err = %d\n",
+ ri->rinfo->state_reg, ret);
+ return ret;
+}
+
+/* DCDC voltages for the selector of 58 to 63 */
+static int tps80031_dcdc_voltages[4][5] = {
+ { 1350, 1500, 1800, 1900, 2100},
+ { 1350, 1500, 1800, 1900, 2100},
+ { 2084, 2315, 2778, 2932, 3241},
+ { 4167, 2315, 2778, 2932, 3241},
+};
+
+static int tps80031_dcdc_list_voltage(struct regulator_dev *rdev, unsigned sel)
+{
+ struct tps80031_regulator *ri = rdev_get_drvdata(rdev);
+ int volt_index = ri->device_flags & 0x3;
+
+ if (sel == 0)
+ return 0;
+ else if (sel < 58)
+ return regulator_list_voltage_linear(rdev, sel - 1);
+ else
+ return tps80031_dcdc_voltages[volt_index][sel - 58] * 1000;
+}
+
+static int tps80031_dcdc_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned vsel)
+{
+ struct tps80031_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps80031_dev(rdev);
+ int ret;
+ u8 reg_val;
+
+ if (ri->rinfo->force_reg) {
+ ret = tps80031_read(parent, ri->rinfo->volt_id,
+ ri->rinfo->force_reg, &reg_val);
+ if (ret < 0) {
+ dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n",
+ ri->rinfo->force_reg, ret);
+ return ret;
+ }
+ if (!(reg_val & SMPS_CMD_MASK)) {
+ ret = tps80031_update(parent, ri->rinfo->volt_id,
+ ri->rinfo->force_reg, vsel, SMPS_VSEL_MASK);
+ if (ret < 0)
+ dev_err(ri->dev,
+ "reg 0x%02x update failed, e = %d\n",
+ ri->rinfo->force_reg, ret);
+ return ret;
+ }
+ }
+ ret = tps80031_update(parent, ri->rinfo->volt_id,
+ ri->rinfo->volt_reg, vsel, SMPS_VSEL_MASK);
+ if (ret < 0)
+ dev_err(ri->dev, "reg 0x%02x update failed, e = %d\n",
+ ri->rinfo->volt_reg, ret);
+ return ret;
+}
+
+static int tps80031_dcdc_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct tps80031_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps80031_dev(rdev);
+ uint8_t vsel = 0;
+ int ret;
+
+ if (ri->rinfo->force_reg) {
+ ret = tps80031_read(parent, ri->rinfo->volt_id,
+ ri->rinfo->force_reg, &vsel);
+ if (ret < 0) {
+ dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n",
+ ri->rinfo->force_reg, ret);
+ return ret;
+ }
+
+ if (!(vsel & SMPS_CMD_MASK))
+ return vsel & SMPS_VSEL_MASK;
+ }
+ ret = tps80031_read(parent, ri->rinfo->volt_id,
+ ri->rinfo->volt_reg, &vsel);
+ if (ret < 0) {
+ dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n",
+ ri->rinfo->volt_reg, ret);
+ return ret;
+ }
+ return vsel & SMPS_VSEL_MASK;
+}
+
+static int tps80031_ldo_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned sel)
+{
+ struct tps80031_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps80031_dev(rdev);
+ int ret;
+
+ /* Check for valid setting for TPS80031 or TPS80032-ES1.0 */
+ if ((ri->rinfo->desc.id == TPS80031_REGULATOR_LDO2) &&
+ (ri->device_flags & TRACK_MODE_ENABLE)) {
+ unsigned nvsel = (sel) & 0x1F;
+ if (((tps80031_get_chip_info(parent) == TPS80031) ||
+ ((tps80031_get_chip_info(parent) == TPS80032) &&
+ (tps80031_get_pmu_version(parent) == 0x0))) &&
+ ((nvsel == 0x0) || (nvsel >= 0x19 && nvsel <= 0x1F))) {
+ dev_err(ri->dev,
+ "Invalid sel %d in track mode LDO2\n",
+ nvsel);
+ return -EINVAL;
+ }
+ }
+
+ ret = tps80031_write(parent, ri->rinfo->volt_id,
+ ri->rinfo->volt_reg, sel);
+ if (ret < 0)
+ dev_err(ri->dev, "Error in writing reg 0x%02x, e = %d\n",
+ ri->rinfo->volt_reg, ret);
+ return ret;
+}
+
+static int tps80031_ldo_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct tps80031_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps80031_dev(rdev);
+ uint8_t vsel;
+ int ret;
+
+ ret = tps80031_read(parent, ri->rinfo->volt_id,
+ ri->rinfo->volt_reg, &vsel);
+ if (ret < 0) {
+ dev_err(ri->dev, "Error in writing the Voltage register\n");
+ return ret;
+ }
+ return vsel & rdev->desc->vsel_mask;
+}
+
+static int tps80031_vbus_is_enabled(struct regulator_dev *rdev)
+{
+ struct tps80031_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps80031_dev(rdev);
+ int ret = -EIO;
+ uint8_t ctrl1 = 0;
+ uint8_t ctrl3 = 0;
+
+ ret = tps80031_read(parent, TPS80031_SLAVE_ID2,
+ TPS80031_CHARGERUSB_CTRL1, &ctrl1);
+ if (ret < 0) {
+ dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n",
+ TPS80031_CHARGERUSB_CTRL1, ret);
+ return ret;
+ }
+ ret = tps80031_read(parent, TPS80031_SLAVE_ID2,
+ TPS80031_CHARGERUSB_CTRL3, &ctrl3);
+ if (ret < 0) {
+ dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n",
+ TPS80031_CHARGERUSB_CTRL3, ret);
+ return ret;
+ }
+ if ((ctrl1 & OPA_MODE_EN) && (ctrl3 & BOOST_HW_PWR_EN))
+ return 1;
+ return ret;
+}
+
+static int tps80031_vbus_enable(struct regulator_dev *rdev)
+{
+ struct tps80031_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps80031_dev(rdev);
+ int ret;
+
+ ret = tps80031_set_bits(parent, TPS80031_SLAVE_ID2,
+ TPS80031_CHARGERUSB_CTRL1, OPA_MODE_EN);
+ if (ret < 0) {
+ dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n",
+ TPS80031_CHARGERUSB_CTRL1, ret);
+ return ret;
+ }
+
+ ret = tps80031_set_bits(parent, TPS80031_SLAVE_ID2,
+ TPS80031_CHARGERUSB_CTRL3, BOOST_HW_PWR_EN);
+ if (ret < 0) {
+ dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n",
+ TPS80031_CHARGERUSB_CTRL3, ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int tps80031_vbus_disable(struct regulator_dev *rdev)
+{
+ struct tps80031_regulator *ri = rdev_get_drvdata(rdev);
+ struct device *parent = to_tps80031_dev(rdev);
+ int ret = 0;
+
+ if (ri->config_flags & TPS80031_VBUS_DISCHRG_EN_PDN) {
+ ret = tps80031_write(parent, TPS80031_SLAVE_ID2,
+ USB_VBUS_CTRL_SET, VBUS_DISCHRG);
+ if (ret < 0) {
+ dev_err(ri->dev, "reg 0x%02x write failed, e = %d\n",
+ USB_VBUS_CTRL_SET, ret);
+ return ret;
+ }
+ }
+
+ ret = tps80031_clr_bits(parent, TPS80031_SLAVE_ID2,
+ TPS80031_CHARGERUSB_CTRL1, OPA_MODE_EN);
+ if (ret < 0) {
+ dev_err(ri->dev, "reg 0x%02x clearbit failed, e = %d\n",
+ TPS80031_CHARGERUSB_CTRL1, ret);
+ return ret;
+ }
+
+ ret = tps80031_clr_bits(parent, TPS80031_SLAVE_ID2,
+ TPS80031_CHARGERUSB_CTRL3, BOOST_HW_PWR_EN);
+ if (ret < 0) {
+ dev_err(ri->dev, "reg 0x%02x clearbit failed, e = %d\n",
+ TPS80031_CHARGERUSB_CTRL3, ret);
+ return ret;
+ }
+
+ mdelay(DIV_ROUND_UP(ri->rinfo->desc.enable_time, 1000));
+ if (ri->config_flags & TPS80031_VBUS_DISCHRG_EN_PDN) {
+ ret = tps80031_write(parent, TPS80031_SLAVE_ID2,
+ USB_VBUS_CTRL_CLR, VBUS_DISCHRG);
+ if (ret < 0) {
+ dev_err(ri->dev, "reg 0x%02x write failed, e = %d\n",
+ USB_VBUS_CTRL_CLR, ret);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static struct regulator_ops tps80031_dcdc_ops = {
+ .list_voltage = tps80031_dcdc_list_voltage,
+ .set_voltage_sel = tps80031_dcdc_set_voltage_sel,
+ .get_voltage_sel = tps80031_dcdc_get_voltage_sel,
+ .enable = tps80031_reg_enable,
+ .disable = tps80031_reg_disable,
+ .is_enabled = tps80031_reg_is_enabled,
+};
+
+static struct regulator_ops tps80031_ldo_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .set_voltage_sel = tps80031_ldo_set_voltage_sel,
+ .get_voltage_sel = tps80031_ldo_get_voltage_sel,
+ .enable = tps80031_reg_enable,
+ .disable = tps80031_reg_disable,
+ .is_enabled = tps80031_reg_is_enabled,
+};
+
+static struct regulator_ops tps80031_vbus_sw_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = tps80031_vbus_enable,
+ .disable = tps80031_vbus_disable,
+ .is_enabled = tps80031_vbus_is_enabled,
+};
+
+static struct regulator_ops tps80031_vbus_hw_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+static struct regulator_ops tps80031_ext_reg_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = tps80031_reg_enable,
+ .disable = tps80031_reg_disable,
+ .is_enabled = tps80031_reg_is_enabled,
+};
+
+/* Non-exiting default definition for some register */
+#define TPS80031_SMPS3_CFG_FORCE 0
+#define TPS80031_SMPS4_CFG_FORCE 0
+
+#define TPS80031_VBUS_CFG_TRANS 0
+#define TPS80031_VBUS_CFG_STATE 0
+
+#define TPS80031_REG_SMPS(_id, _volt_id, _pbit) \
+{ \
+ .trans_reg = TPS80031_##_id##_CFG_TRANS, \
+ .state_reg = TPS80031_##_id##_CFG_STATE, \
+ .force_reg = TPS80031_##_id##_CFG_FORCE, \
+ .volt_reg = TPS80031_##_id##_CFG_VOLTAGE, \
+ .volt_id = TPS80031_SLAVE_##_volt_id, \
+ .preq_bit = _pbit, \
+ .desc = { \
+ .name = "tps80031_"#_id, \
+ .id = TPS80031_REGULATOR_##_id, \
+ .n_voltages = 63, \
+ .ops = &tps80031_dcdc_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .enable_time = 500, \
+ }, \
+}
+
+#define TPS80031_REG_LDO(_id, _preq_bit) \
+{ \
+ .trans_reg = TPS80031_##_id##_CFG_TRANS, \
+ .state_reg = TPS80031_##_id##_CFG_STATE, \
+ .volt_reg = TPS80031_##_id##_CFG_VOLTAGE, \
+ .volt_id = TPS80031_SLAVE_ID1, \
+ .preq_bit = _preq_bit, \
+ .desc = { \
+ .owner = THIS_MODULE, \
+ .name = "tps80031_"#_id, \
+ .id = TPS80031_REGULATOR_##_id, \
+ .ops = &tps80031_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .min_uV = 1000000, \
+ .uV_step = 100000, \
+ .linear_min_sel = 1, \
+ .n_voltages = 25, \
+ .vsel_mask = LDO_VSEL_MASK, \
+ .enable_time = 500, \
+ }, \
+}
+
+#define TPS80031_REG_FIXED(_id, max_mV, _ops, _delay, _pbit) \
+{ \
+ .trans_reg = TPS80031_##_id##_CFG_TRANS, \
+ .state_reg = TPS80031_##_id##_CFG_STATE, \
+ .volt_id = TPS80031_SLAVE_ID1, \
+ .preq_bit = _pbit, \
+ .desc = { \
+ .name = "tps80031_"#_id, \
+ .id = TPS80031_REGULATOR_##_id, \
+ .min_uV = max_mV * 1000, \
+ .n_voltages = 1, \
+ .ops = &_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .enable_time = _delay, \
+ }, \
+}
+
+static struct tps80031_regulator_info tps80031_rinfo[TPS80031_REGULATOR_MAX] = {
+ TPS80031_REG_SMPS(VIO, ID0, 4),
+ TPS80031_REG_SMPS(SMPS1, ID0, 0),
+ TPS80031_REG_SMPS(SMPS2, ID0, 1),
+ TPS80031_REG_SMPS(SMPS3, ID1, 2),
+ TPS80031_REG_SMPS(SMPS4, ID1, 3),
+ TPS80031_REG_LDO(VANA, -1),
+ TPS80031_REG_LDO(LDO1, 8),
+ TPS80031_REG_LDO(LDO2, 9),
+ TPS80031_REG_LDO(LDO3, 10),
+ TPS80031_REG_LDO(LDO4, 11),
+ TPS80031_REG_LDO(LDO5, 12),
+ TPS80031_REG_LDO(LDO6, 13),
+ TPS80031_REG_LDO(LDO7, 14),
+ TPS80031_REG_LDO(LDOLN, 15),
+ TPS80031_REG_LDO(LDOUSB, 5),
+ TPS80031_REG_FIXED(VBUS, 5000, tps80031_vbus_hw_ops, 100000, -1),
+ TPS80031_REG_FIXED(REGEN1, 3300, tps80031_ext_reg_ops, 0, 16),
+ TPS80031_REG_FIXED(REGEN2, 3300, tps80031_ext_reg_ops, 0, 17),
+ TPS80031_REG_FIXED(SYSEN, 3300, tps80031_ext_reg_ops, 0, 18),
+};
+
+static int tps80031_power_req_config(struct device *parent,
+ struct tps80031_regulator *ri,
+ struct tps80031_regulator_platform_data *tps80031_pdata)
+{
+ int ret = 0;
+
+ if (ri->rinfo->preq_bit < 0)
+ goto skip_pwr_req_config;
+
+ ret = tps80031_ext_power_req_config(parent, ri->ext_ctrl_flag,
+ ri->rinfo->preq_bit, ri->rinfo->state_reg,
+ ri->rinfo->trans_reg);
+ if (ret < 0) {
+ dev_err(ri->dev, "ext powerreq config failed, err = %d\n", ret);
+ return ret;
+ }
+
+skip_pwr_req_config:
+ if (tps80031_pdata->ext_ctrl_flag & TPS80031_PWR_ON_ON_SLEEP) {
+ ret = tps80031_update(parent, TPS80031_SLAVE_ID1,
+ ri->rinfo->trans_reg, TPS80031_TRANS_SLEEP_ON,
+ TPS80031_TRANS_SLEEP_MASK);
+ if (ret < 0) {
+ dev_err(ri->dev, "Reg 0x%02x update failed, e %d\n",
+ ri->rinfo->trans_reg, ret);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static int tps80031_regulator_config(struct device *parent,
+ struct tps80031_regulator *ri,
+ struct tps80031_regulator_platform_data *tps80031_pdata)
+{
+ int ret = 0;
+
+ switch (ri->rinfo->desc.id) {
+ case TPS80031_REGULATOR_LDOUSB:
+ if (ri->config_flags & (TPS80031_USBLDO_INPUT_VSYS |
+ TPS80031_USBLDO_INPUT_PMID)) {
+ unsigned val = 0;
+ if (ri->config_flags & TPS80031_USBLDO_INPUT_VSYS)
+ val = MISC2_LDOUSB_IN_VSYS;
+ else
+ val = MISC2_LDOUSB_IN_PMID;
+
+ ret = tps80031_update(parent, TPS80031_SLAVE_ID1,
+ TPS80031_MISC2, val,
+ MISC2_LDOUSB_IN_MASK);
+ if (ret < 0) {
+ dev_err(ri->dev,
+ "LDOUSB config failed, e= %d\n", ret);
+ return ret;
+ }
+ }
+ break;
+
+ case TPS80031_REGULATOR_LDO3:
+ if (ri->config_flags & TPS80031_LDO3_OUTPUT_VIB) {
+ ret = tps80031_update(parent, TPS80031_SLAVE_ID1,
+ TPS80031_MISC2, MISC2_LDO3_SEL_VIB_VAL,
+ MISC2_LDO3_SEL_VIB_MASK);
+ if (ret < 0) {
+ dev_err(ri->dev,
+ "LDO3 config failed, e = %d\n", ret);
+ return ret;
+ }
+ }
+ break;
+
+ case TPS80031_REGULATOR_VBUS:
+ /* Provide SW control Ops if VBUS is SW control */
+ if (!(ri->config_flags & TPS80031_VBUS_SW_ONLY))
+ ri->rinfo->desc.ops = &tps80031_vbus_sw_ops;
+ break;
+ default:
+ break;
+ }
+
+ /* Configure Active state to ON, SLEEP to OFF and OFF_state to OFF */
+ ret = tps80031_update(parent, TPS80031_SLAVE_ID1, ri->rinfo->trans_reg,
+ TPS80031_TRANS_ACTIVE_ON | TPS80031_TRANS_SLEEP_OFF |
+ TPS80031_TRANS_OFF_OFF, TPS80031_TRANS_ACTIVE_MASK |
+ TPS80031_TRANS_SLEEP_MASK | TPS80031_TRANS_OFF_MASK);
+ if (ret < 0) {
+ dev_err(ri->dev, "trans reg update failed, e %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int check_smps_mode_mult(struct device *parent,
+ struct tps80031_regulator *ri)
+{
+ int mult_offset;
+ int ret;
+ u8 smps_offset;
+ u8 smps_mult;
+
+ ret = tps80031_read(parent, TPS80031_SLAVE_ID1,
+ TPS80031_SMPS_OFFSET, &smps_offset);
+ if (ret < 0) {
+ dev_err(parent, "Error in reading smps offset register\n");
+ return ret;
+ }
+
+ ret = tps80031_read(parent, TPS80031_SLAVE_ID1,
+ TPS80031_SMPS_MULT, &smps_mult);
+ if (ret < 0) {
+ dev_err(parent, "Error in reading smps mult register\n");
+ return ret;
+ }
+
+ switch (ri->rinfo->desc.id) {
+ case TPS80031_REGULATOR_VIO:
+ mult_offset = SMPS_MULTOFFSET_VIO;
+ break;
+ case TPS80031_REGULATOR_SMPS1:
+ mult_offset = SMPS_MULTOFFSET_SMPS1;
+ break;
+ case TPS80031_REGULATOR_SMPS2:
+ mult_offset = SMPS_MULTOFFSET_SMPS2;
+ break;
+ case TPS80031_REGULATOR_SMPS3:
+ mult_offset = SMPS_MULTOFFSET_SMPS3;
+ break;
+ case TPS80031_REGULATOR_SMPS4:
+ mult_offset = SMPS_MULTOFFSET_SMPS4;
+ break;
+ case TPS80031_REGULATOR_LDO2:
+ ri->device_flags = smps_mult & BIT(5) ? TRACK_MODE_ENABLE : 0;
+ /* TRACK mode the ldo2 varies from 600mV to 1300mV */
+ if (ri->device_flags & TRACK_MODE_ENABLE) {
+ ri->rinfo->desc.min_uV = 600000;
+ ri->rinfo->desc.uV_step = 12500;
+ ri->rinfo->desc.n_voltages = 57;
+ ri->rinfo->desc.vsel_mask = LDO_TRACK_VSEL_MASK;
+ }
+ return 0;
+ default:
+ return 0;
+ }
+
+ ri->device_flags = (smps_offset & mult_offset) ? DCDC_OFFSET_EN : 0;
+ ri->device_flags |= (smps_mult & mult_offset) ? DCDC_EXTENDED_EN : 0;
+ switch (ri->device_flags) {
+ case 0:
+ ri->rinfo->desc.min_uV = 607700;
+ ri->rinfo->desc.uV_step = 12660;
+ break;
+ case DCDC_OFFSET_EN:
+ ri->rinfo->desc.min_uV = 700000;
+ ri->rinfo->desc.uV_step = 12500;
+ break;
+ case DCDC_EXTENDED_EN:
+ ri->rinfo->desc.min_uV = 1852000;
+ ri->rinfo->desc.uV_step = 38600;
+ break;
+ case DCDC_OFFSET_EN | DCDC_EXTENDED_EN:
+ ri->rinfo->desc.min_uV = 2161000;
+ ri->rinfo->desc.uV_step = 38600;
+ break;
+ }
+ return 0;
+}
+
+static int tps80031_regulator_probe(struct platform_device *pdev)
+{
+ struct tps80031_platform_data *pdata;
+ struct tps80031_regulator_platform_data *tps_pdata;
+ struct tps80031_regulator *ri;
+ struct tps80031_regulator *pmic;
+ struct regulator_dev *rdev;
+ struct regulator_config config = { };
+ int ret;
+ int num;
+
+ pdata = dev_get_platdata(pdev->dev.parent);
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data\n");
+ return -EINVAL;
+ }
+
+ pmic = devm_kzalloc(&pdev->dev,
+ TPS80031_REGULATOR_MAX * sizeof(*pmic), GFP_KERNEL);
+ if (!pmic) {
+ dev_err(&pdev->dev, "mem alloc for pmic failed\n");
+ return -ENOMEM;
+ }
+
+ for (num = 0; num < TPS80031_REGULATOR_MAX; ++num) {
+ tps_pdata = pdata->regulator_pdata[num];
+ ri = &pmic[num];
+ ri->rinfo = &tps80031_rinfo[num];
+ ri->dev = &pdev->dev;
+
+ check_smps_mode_mult(pdev->dev.parent, ri);
+ config.dev = &pdev->dev;
+ config.init_data = NULL;
+ config.driver_data = ri;
+ if (tps_pdata) {
+ config.init_data = tps_pdata->reg_init_data;
+ ri->config_flags = tps_pdata->config_flags;
+ ri->ext_ctrl_flag = tps_pdata->ext_ctrl_flag;
+ ret = tps80031_regulator_config(pdev->dev.parent,
+ ri, tps_pdata);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "regulator config failed, e %d\n", ret);
+ goto fail;
+ }
+
+ ret = tps80031_power_req_config(pdev->dev.parent,
+ ri, tps_pdata);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "pwr_req config failed, err %d\n", ret);
+ goto fail;
+ }
+ }
+ rdev = regulator_register(&ri->rinfo->desc, &config);
+ if (IS_ERR_OR_NULL(rdev)) {
+ dev_err(&pdev->dev,
+ "register regulator failed %s\n",
+ ri->rinfo->desc.name);
+ ret = PTR_ERR(rdev);
+ goto fail;
+ }
+ ri->rdev = rdev;
+ }
+
+ platform_set_drvdata(pdev, pmic);
+ return 0;
+fail:
+ while (--num >= 0) {
+ ri = &pmic[num];
+ regulator_unregister(ri->rdev);
+ }
+ return ret;
+}
+
+static int tps80031_regulator_remove(struct platform_device *pdev)
+{
+ struct tps80031_regulator *pmic = platform_get_drvdata(pdev);
+ struct tps80031_regulator *ri = NULL;
+ int num;
+
+ for (num = 0; num < TPS80031_REGULATOR_MAX; ++num) {
+ ri = &pmic[num];
+ regulator_unregister(ri->rdev);
+ }
+ return 0;
+}
+
+static struct platform_driver tps80031_regulator_driver = {
+ .driver = {
+ .name = "tps80031-pmic",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps80031_regulator_probe,
+ .remove = tps80031_regulator_remove,
+};
+
+static int __init tps80031_regulator_init(void)
+{
+ return platform_driver_register(&tps80031_regulator_driver);
+}
+subsys_initcall(tps80031_regulator_init);
+
+static void __exit tps80031_regulator_exit(void)
+{
+ platform_driver_unregister(&tps80031_regulator_driver);
+}
+module_exit(tps80031_regulator_exit);
+
+MODULE_ALIAS("platform:tps80031-regulator");
+MODULE_DESCRIPTION("Regulator Driver for TI TPS80031/TPS80032 PMIC");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c
index 7eb986a4074..493c8c6a241 100644
--- a/drivers/regulator/twl-regulator.c
+++ b/drivers/regulator/twl-regulator.c
@@ -1116,7 +1116,7 @@ static const struct of_device_id twl_of_match[] __devinitconst = {
};
MODULE_DEVICE_TABLE(of, twl_of_match);
-static int __devinit twlreg_probe(struct platform_device *pdev)
+static int twlreg_probe(struct platform_device *pdev)
{
int i, id;
struct twlreg_info *info;
@@ -1241,7 +1241,7 @@ static int __devinit twlreg_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit twlreg_remove(struct platform_device *pdev)
+static int twlreg_remove(struct platform_device *pdev)
{
struct regulator_dev *rdev = platform_get_drvdata(pdev);
struct twlreg_info *info = rdev->reg_data;
@@ -1255,7 +1255,7 @@ MODULE_ALIAS("platform:twl_reg");
static struct platform_driver twlreg_driver = {
.probe = twlreg_probe,
- .remove = __devexit_p(twlreg_remove),
+ .remove = twlreg_remove,
/* NOTE: short name, to work around driver model truncation of
* "twl_regulator.12" (and friends) to "twl_regulator.1".
*/
diff --git a/drivers/regulator/vexpress.c b/drivers/regulator/vexpress.c
new file mode 100644
index 00000000000..4668c7f8133
--- /dev/null
+++ b/drivers/regulator/vexpress.c
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2012 ARM Limited
+ */
+
+#define DRVNAME "vexpress-regulator"
+#define pr_fmt(fmt) DRVNAME ": " fmt
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/vexpress.h>
+
+struct vexpress_regulator {
+ struct regulator_desc desc;
+ struct regulator_dev *regdev;
+ struct vexpress_config_func *func;
+};
+
+static int vexpress_regulator_get_voltage(struct regulator_dev *regdev)
+{
+ struct vexpress_regulator *reg = rdev_get_drvdata(regdev);
+ u32 uV;
+ int err = vexpress_config_read(reg->func, 0, &uV);
+
+ return err ? err : uV;
+}
+
+static int vexpress_regulator_set_voltage(struct regulator_dev *regdev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ struct vexpress_regulator *reg = rdev_get_drvdata(regdev);
+
+ return vexpress_config_write(reg->func, 0, min_uV);
+}
+
+static struct regulator_ops vexpress_regulator_ops_ro = {
+ .get_voltage = vexpress_regulator_get_voltage,
+};
+
+static struct regulator_ops vexpress_regulator_ops = {
+ .get_voltage = vexpress_regulator_get_voltage,
+ .set_voltage = vexpress_regulator_set_voltage,
+};
+
+static int vexpress_regulator_probe(struct platform_device *pdev)
+{
+ int err;
+ struct vexpress_regulator *reg;
+ struct regulator_init_data *init_data;
+ struct regulator_config config = { };
+
+ reg = devm_kzalloc(&pdev->dev, sizeof(*reg), GFP_KERNEL);
+ if (!reg) {
+ err = -ENOMEM;
+ goto error_kzalloc;
+ }
+
+ reg->func = vexpress_config_func_get_by_dev(&pdev->dev);
+ if (!reg->func) {
+ err = -ENXIO;
+ goto error_get_func;
+ }
+
+ reg->desc.name = dev_name(&pdev->dev);
+ reg->desc.type = REGULATOR_VOLTAGE;
+ reg->desc.owner = THIS_MODULE;
+ reg->desc.continuous_voltage_range = true;
+
+ init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node);
+ if (!init_data) {
+ err = -EINVAL;
+ goto error_get_regulator_init_data;
+ }
+
+ init_data->constraints.apply_uV = 0;
+ if (init_data->constraints.min_uV && init_data->constraints.max_uV)
+ reg->desc.ops = &vexpress_regulator_ops;
+ else
+ reg->desc.ops = &vexpress_regulator_ops_ro;
+
+ config.dev = &pdev->dev;
+ config.init_data = init_data;
+ config.driver_data = reg;
+ config.of_node = pdev->dev.of_node;
+
+ reg->regdev = regulator_register(&reg->desc, &config);
+ if (IS_ERR(reg->regdev)) {
+ err = PTR_ERR(reg->regdev);
+ goto error_regulator_register;
+ }
+
+ platform_set_drvdata(pdev, reg);
+
+ return 0;
+
+error_regulator_register:
+error_get_regulator_init_data:
+ vexpress_config_func_put(reg->func);
+error_get_func:
+error_kzalloc:
+ return err;
+}
+
+static int vexpress_regulator_remove(struct platform_device *pdev)
+{
+ struct vexpress_regulator *reg = platform_get_drvdata(pdev);
+
+ vexpress_config_func_put(reg->func);
+ regulator_unregister(reg->regdev);
+
+ return 0;
+}
+
+static struct of_device_id vexpress_regulator_of_match[] = {
+ { .compatible = "arm,vexpress-volt", },
+ { }
+};
+
+static struct platform_driver vexpress_regulator_driver = {
+ .probe = vexpress_regulator_probe,
+ .remove = vexpress_regulator_remove,
+ .driver = {
+ .name = DRVNAME,
+ .owner = THIS_MODULE,
+ .of_match_table = vexpress_regulator_of_match,
+ },
+};
+
+module_platform_driver(vexpress_regulator_driver);
+
+MODULE_AUTHOR("Pawel Moll <pawel.moll@arm.com>");
+MODULE_DESCRIPTION("Versatile Express regulator");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:vexpress-regulator");
diff --git a/drivers/regulator/virtual.c b/drivers/regulator/virtual.c
index c038e742253..01c66e9712a 100644
--- a/drivers/regulator/virtual.c
+++ b/drivers/regulator/virtual.c
@@ -285,7 +285,7 @@ static const struct attribute_group regulator_virtual_attr_group = {
.attrs = regulator_virtual_attributes,
};
-static int __devinit regulator_virtual_probe(struct platform_device *pdev)
+static int regulator_virtual_probe(struct platform_device *pdev)
{
char *reg_id = pdev->dev.platform_data;
struct virtual_consumer_data *drvdata;
@@ -321,7 +321,7 @@ static int __devinit regulator_virtual_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit regulator_virtual_remove(struct platform_device *pdev)
+static int regulator_virtual_remove(struct platform_device *pdev)
{
struct virtual_consumer_data *drvdata = platform_get_drvdata(pdev);
@@ -337,7 +337,7 @@ static int __devexit regulator_virtual_remove(struct platform_device *pdev)
static struct platform_driver regulator_virtual_consumer_driver = {
.probe = regulator_virtual_probe,
- .remove = __devexit_p(regulator_virtual_remove),
+ .remove = regulator_virtual_remove,
.driver = {
.name = "reg-virt-consumer",
.owner = THIS_MODULE,
diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c
index 782c228a19b..0af6898bcd7 100644
--- a/drivers/regulator/wm831x-dcdc.c
+++ b/drivers/regulator/wm831x-dcdc.c
@@ -223,7 +223,7 @@ static int wm831x_buckv_map_voltage(struct regulator_dev *rdev,
if (min_uV < 600000)
vsel = 0;
else if (min_uV <= 1800000)
- vsel = ((min_uV - 600000) / 12500) + 8;
+ vsel = DIV_ROUND_UP(min_uV - 600000, 12500) + 8;
else
return -EINVAL;
@@ -290,7 +290,7 @@ static int wm831x_buckv_set_voltage_sel(struct regulator_dev *rdev,
if (vsel > dcdc->dvs_vsel) {
ret = wm831x_set_bits(wm831x, dvs_reg,
WM831X_DC1_DVS_VSEL_MASK,
- dcdc->dvs_vsel);
+ vsel);
if (ret == 0)
dcdc->dvs_vsel = vsel;
else
@@ -387,7 +387,7 @@ static struct regulator_ops wm831x_buckv_ops = {
* Set up DVS control. We just log errors since we can still run
* (with reduced performance) if we fail.
*/
-static __devinit void wm831x_buckv_dvs_init(struct wm831x_dcdc *dcdc,
+static void wm831x_buckv_dvs_init(struct wm831x_dcdc *dcdc,
struct wm831x_buckv_pdata *pdata)
{
struct wm831x *wm831x = dcdc->wm831x;
@@ -448,7 +448,7 @@ static __devinit void wm831x_buckv_dvs_init(struct wm831x_dcdc *dcdc,
}
}
-static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
+static int wm831x_buckv_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
@@ -562,7 +562,7 @@ err:
return ret;
}
-static __devexit int wm831x_buckv_remove(struct platform_device *pdev)
+static int wm831x_buckv_remove(struct platform_device *pdev)
{
struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
struct wm831x *wm831x = dcdc->wm831x;
@@ -582,7 +582,7 @@ static __devexit int wm831x_buckv_remove(struct platform_device *pdev)
static struct platform_driver wm831x_buckv_driver = {
.probe = wm831x_buckv_probe,
- .remove = __devexit_p(wm831x_buckv_remove),
+ .remove = wm831x_buckv_remove,
.driver = {
.name = "wm831x-buckv",
.owner = THIS_MODULE,
@@ -623,7 +623,7 @@ static struct regulator_ops wm831x_buckp_ops = {
.set_suspend_mode = wm831x_dcdc_set_suspend_mode,
};
-static __devinit int wm831x_buckp_probe(struct platform_device *pdev)
+static int wm831x_buckp_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
@@ -710,7 +710,7 @@ err:
return ret;
}
-static __devexit int wm831x_buckp_remove(struct platform_device *pdev)
+static int wm831x_buckp_remove(struct platform_device *pdev)
{
struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
@@ -725,7 +725,7 @@ static __devexit int wm831x_buckp_remove(struct platform_device *pdev)
static struct platform_driver wm831x_buckp_driver = {
.probe = wm831x_buckp_probe,
- .remove = __devexit_p(wm831x_buckp_remove),
+ .remove = wm831x_buckp_remove,
.driver = {
.name = "wm831x-buckp",
.owner = THIS_MODULE,
@@ -771,7 +771,7 @@ static struct regulator_ops wm831x_boostp_ops = {
.disable = regulator_disable_regmap,
};
-static __devinit int wm831x_boostp_probe(struct platform_device *pdev)
+static int wm831x_boostp_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
@@ -845,7 +845,7 @@ err:
return ret;
}
-static __devexit int wm831x_boostp_remove(struct platform_device *pdev)
+static int wm831x_boostp_remove(struct platform_device *pdev)
{
struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
@@ -860,7 +860,7 @@ static __devexit int wm831x_boostp_remove(struct platform_device *pdev)
static struct platform_driver wm831x_boostp_driver = {
.probe = wm831x_boostp_probe,
- .remove = __devexit_p(wm831x_boostp_remove),
+ .remove = wm831x_boostp_remove,
.driver = {
.name = "wm831x-boostp",
.owner = THIS_MODULE,
@@ -883,7 +883,7 @@ static struct regulator_ops wm831x_epe_ops = {
.get_status = wm831x_dcdc_get_status,
};
-static __devinit int wm831x_epe_probe(struct platform_device *pdev)
+static int wm831x_epe_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
@@ -936,7 +936,7 @@ err:
return ret;
}
-static __devexit int wm831x_epe_remove(struct platform_device *pdev)
+static int wm831x_epe_remove(struct platform_device *pdev)
{
struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
@@ -948,7 +948,7 @@ static __devexit int wm831x_epe_remove(struct platform_device *pdev)
static struct platform_driver wm831x_epe_driver = {
.probe = wm831x_epe_probe,
- .remove = __devexit_p(wm831x_epe_remove),
+ .remove = wm831x_epe_remove,
.driver = {
.name = "wm831x-epe",
.owner = THIS_MODULE,
@@ -993,4 +993,5 @@ MODULE_DESCRIPTION("WM831x DC-DC convertor driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:wm831x-buckv");
MODULE_ALIAS("platform:wm831x-buckp");
+MODULE_ALIAS("platform:wm831x-boostp");
MODULE_ALIAS("platform:wm831x-epe");
diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c
index 2646a1902b3..68586ee3e1c 100644
--- a/drivers/regulator/wm831x-isink.c
+++ b/drivers/regulator/wm831x-isink.c
@@ -148,7 +148,7 @@ static irqreturn_t wm831x_isink_irq(int irq, void *data)
}
-static __devinit int wm831x_isink_probe(struct platform_device *pdev)
+static int wm831x_isink_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
@@ -221,7 +221,7 @@ err:
return ret;
}
-static __devexit int wm831x_isink_remove(struct platform_device *pdev)
+static int wm831x_isink_remove(struct platform_device *pdev)
{
struct wm831x_isink *isink = platform_get_drvdata(pdev);
@@ -236,7 +236,7 @@ static __devexit int wm831x_isink_remove(struct platform_device *pdev)
static struct platform_driver wm831x_isink_driver = {
.probe = wm831x_isink_probe,
- .remove = __devexit_p(wm831x_isink_remove),
+ .remove = wm831x_isink_remove,
.driver = {
.name = "wm831x-isink",
.owner = THIS_MODULE,
diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c
index c2dc03993dc..1ec379a9a95 100644
--- a/drivers/regulator/wm831x-ldo.c
+++ b/drivers/regulator/wm831x-ldo.c
@@ -247,7 +247,7 @@ static struct regulator_ops wm831x_gp_ldo_ops = {
.disable = regulator_disable_regmap,
};
-static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev)
+static int wm831x_gp_ldo_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
@@ -334,7 +334,7 @@ err:
return ret;
}
-static __devexit int wm831x_gp_ldo_remove(struct platform_device *pdev)
+static int wm831x_gp_ldo_remove(struct platform_device *pdev)
{
struct wm831x_ldo *ldo = platform_get_drvdata(pdev);
@@ -349,7 +349,7 @@ static __devexit int wm831x_gp_ldo_remove(struct platform_device *pdev)
static struct platform_driver wm831x_gp_ldo_driver = {
.probe = wm831x_gp_ldo_probe,
- .remove = __devexit_p(wm831x_gp_ldo_remove),
+ .remove = wm831x_gp_ldo_remove,
.driver = {
.name = "wm831x-ldo",
.owner = THIS_MODULE,
@@ -504,7 +504,7 @@ static struct regulator_ops wm831x_aldo_ops = {
.disable = regulator_disable_regmap,
};
-static __devinit int wm831x_aldo_probe(struct platform_device *pdev)
+static int wm831x_aldo_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
@@ -590,7 +590,7 @@ err:
return ret;
}
-static __devexit int wm831x_aldo_remove(struct platform_device *pdev)
+static int wm831x_aldo_remove(struct platform_device *pdev)
{
struct wm831x_ldo *ldo = platform_get_drvdata(pdev);
@@ -603,7 +603,7 @@ static __devexit int wm831x_aldo_remove(struct platform_device *pdev)
static struct platform_driver wm831x_aldo_driver = {
.probe = wm831x_aldo_probe,
- .remove = __devexit_p(wm831x_aldo_remove),
+ .remove = wm831x_aldo_remove,
.driver = {
.name = "wm831x-aldo",
.owner = THIS_MODULE,
@@ -660,7 +660,7 @@ static struct regulator_ops wm831x_alive_ldo_ops = {
.disable = regulator_disable_regmap,
};
-static __devinit int wm831x_alive_ldo_probe(struct platform_device *pdev)
+static int wm831x_alive_ldo_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
@@ -737,7 +737,7 @@ err:
return ret;
}
-static __devexit int wm831x_alive_ldo_remove(struct platform_device *pdev)
+static int wm831x_alive_ldo_remove(struct platform_device *pdev)
{
struct wm831x_ldo *ldo = platform_get_drvdata(pdev);
@@ -748,7 +748,7 @@ static __devexit int wm831x_alive_ldo_remove(struct platform_device *pdev)
static struct platform_driver wm831x_alive_ldo_driver = {
.probe = wm831x_alive_ldo_probe,
- .remove = __devexit_p(wm831x_alive_ldo_remove),
+ .remove = wm831x_alive_ldo_remove,
.driver = {
.name = "wm831x-alive-ldo",
.owner = THIS_MODULE,
diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c
index 27c746ef063..c6a32ea80b9 100644
--- a/drivers/regulator/wm8400-regulator.c
+++ b/drivers/regulator/wm8400-regulator.c
@@ -226,7 +226,7 @@ static struct regulator_desc regulators[] = {
},
};
-static int __devinit wm8400_regulator_probe(struct platform_device *pdev)
+static int wm8400_regulator_probe(struct platform_device *pdev)
{
struct wm8400 *wm8400 = container_of(pdev, struct wm8400, regulators[pdev->id]);
struct regulator_config config = { };
@@ -246,7 +246,7 @@ static int __devinit wm8400_regulator_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit wm8400_regulator_remove(struct platform_device *pdev)
+static int wm8400_regulator_remove(struct platform_device *pdev)
{
struct regulator_dev *rdev = platform_get_drvdata(pdev);
@@ -261,7 +261,7 @@ static struct platform_driver wm8400_regulator_driver = {
.name = "wm8400-regulator",
},
.probe = wm8400_regulator_probe,
- .remove = __devexit_p(wm8400_regulator_remove),
+ .remove = wm8400_regulator_remove,
};
/**
diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c
index 86bb48db149..6ff87234264 100644
--- a/drivers/regulator/wm8994-regulator.c
+++ b/drivers/regulator/wm8994-regulator.c
@@ -99,7 +99,7 @@ static const struct regulator_desc wm8994_ldo_desc[] = {
},
};
-static __devinit int wm8994_ldo_probe(struct platform_device *pdev)
+static int wm8994_ldo_probe(struct platform_device *pdev)
{
struct wm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent);
struct wm8994_pdata *pdata = wm8994->dev->platform_data;
@@ -142,7 +142,7 @@ err:
return ret;
}
-static __devexit int wm8994_ldo_remove(struct platform_device *pdev)
+static int wm8994_ldo_remove(struct platform_device *pdev)
{
struct wm8994_ldo *ldo = platform_get_drvdata(pdev);
@@ -155,7 +155,7 @@ static __devexit int wm8994_ldo_remove(struct platform_device *pdev)
static struct platform_driver wm8994_ldo_driver = {
.probe = wm8994_ldo_probe,
- .remove = __devexit_p(wm8994_ldo_remove),
+ .remove = wm8994_ldo_remove,
.driver = {
.name = "wm8994-ldo",
.owner = THIS_MODULE,
diff --git a/drivers/remoteproc/remoteproc_elf_loader.c b/drivers/remoteproc/remoteproc_elf_loader.c
index e1f89d64973..0d36f94ab51 100644
--- a/drivers/remoteproc/remoteproc_elf_loader.c
+++ b/drivers/remoteproc/remoteproc_elf_loader.c
@@ -66,13 +66,13 @@ rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw)
return -EINVAL;
}
- /* We assume the firmware has the same endianess as the host */
+ /* We assume the firmware has the same endianness as the host */
# ifdef __LITTLE_ENDIAN
if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
# else /* BIG ENDIAN */
if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
# endif
- dev_err(dev, "Unsupported firmware endianess\n");
+ dev_err(dev, "Unsupported firmware endianness\n");
return -EINVAL;
}
diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c
index 26c81f23360..afb7cfa85cc 100644
--- a/drivers/rtc/rtc-isl1208.c
+++ b/drivers/rtc/rtc-isl1208.c
@@ -118,7 +118,7 @@ isl1208_i2c_set_regs(struct i2c_client *client, u8 reg, u8 const buf[],
return ret;
}
-/* simple check to see wether we have a isl1208 */
+/* simple check to see whether we have a isl1208 */
static int
isl1208_i2c_validate_client(struct i2c_client *client)
{
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index 77823d21d31..a7a2a998fa9 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -186,7 +186,7 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR);
rtc_tm->tm_sec = readb(base + S3C2410_RTCSEC);
- /* the only way to work out wether the system was mid-update
+ /* the only way to work out whether the system was mid-update
* when we read it is to check the second counter, and if it
* is zero, then we re-try the entire read
*/
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 0595c763daf..29225e1c159 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -349,6 +349,16 @@ static int dasd_state_basic_to_ready(struct dasd_device *device)
return rc;
}
+static inline
+int _wait_for_empty_queues(struct dasd_device *device)
+{
+ if (device->block)
+ return list_empty(&device->ccw_queue) &&
+ list_empty(&device->block->ccw_queue);
+ else
+ return list_empty(&device->ccw_queue);
+}
+
/*
* Remove device from block device layer. Destroy dirty buffers.
* Forget format information. Check if the target level is basic
@@ -1841,6 +1851,13 @@ static void __dasd_device_check_expire(struct dasd_device *device)
cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist);
if ((cqr->status == DASD_CQR_IN_IO && cqr->expires != 0) &&
(time_after_eq(jiffies, cqr->expires + cqr->starttime))) {
+ if (test_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) {
+ /*
+ * IO in safe offline processing should not
+ * run out of retries
+ */
+ cqr->retries++;
+ }
if (device->discipline->term_IO(cqr) != 0) {
/* Hmpf, try again in 5 sec */
dev_err(&device->cdev->dev,
@@ -3024,11 +3041,11 @@ void dasd_generic_remove(struct ccw_device *cdev)
cdev->handler = NULL;
- dasd_remove_sysfs_files(cdev);
device = dasd_device_from_cdev(cdev);
if (IS_ERR(device))
return;
- if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags)) {
+ if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags) &&
+ !test_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) {
/* Already doing offline processing */
dasd_put_device(device);
return;
@@ -3048,6 +3065,8 @@ void dasd_generic_remove(struct ccw_device *cdev)
*/
if (block)
dasd_free_block(block);
+
+ dasd_remove_sysfs_files(cdev);
}
/*
@@ -3126,16 +3145,13 @@ int dasd_generic_set_offline(struct ccw_device *cdev)
{
struct dasd_device *device;
struct dasd_block *block;
- int max_count, open_count;
+ int max_count, open_count, rc;
+ rc = 0;
device = dasd_device_from_cdev(cdev);
if (IS_ERR(device))
return PTR_ERR(device);
- if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags)) {
- /* Already doing offline processing */
- dasd_put_device(device);
- return 0;
- }
+
/*
* We must make sure that this device is currently not in use.
* The open_count is increased for every opener, that includes
@@ -3159,6 +3175,54 @@ int dasd_generic_set_offline(struct ccw_device *cdev)
return -EBUSY;
}
}
+
+ if (test_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) {
+ /*
+ * safe offline allready running
+ * could only be called by normal offline so safe_offline flag
+ * needs to be removed to run normal offline and kill all I/O
+ */
+ if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags)) {
+ /* Already doing normal offline processing */
+ dasd_put_device(device);
+ return -EBUSY;
+ } else
+ clear_bit(DASD_FLAG_SAFE_OFFLINE, &device->flags);
+
+ } else
+ if (test_bit(DASD_FLAG_OFFLINE, &device->flags)) {
+ /* Already doing offline processing */
+ dasd_put_device(device);
+ return -EBUSY;
+ }
+
+ /*
+ * if safe_offline called set safe_offline_running flag and
+ * clear safe_offline so that a call to normal offline
+ * can overrun safe_offline processing
+ */
+ if (test_and_clear_bit(DASD_FLAG_SAFE_OFFLINE, &device->flags) &&
+ !test_and_set_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) {
+ /*
+ * If we want to set the device safe offline all IO operations
+ * should be finished before continuing the offline process
+ * so sync bdev first and then wait for our queues to become
+ * empty
+ */
+ /* sync blockdev and partitions */
+ rc = fsync_bdev(device->block->bdev);
+ if (rc != 0)
+ goto interrupted;
+
+ /* schedule device tasklet and wait for completion */
+ dasd_schedule_device_bh(device);
+ rc = wait_event_interruptible(shutdown_waitq,
+ _wait_for_empty_queues(device));
+ if (rc != 0)
+ goto interrupted;
+ }
+
+ set_bit(DASD_FLAG_OFFLINE, &device->flags);
dasd_set_target_state(device, DASD_STATE_NEW);
/* dasd_delete_device destroys the device reference. */
block = device->block;
@@ -3170,6 +3234,14 @@ int dasd_generic_set_offline(struct ccw_device *cdev)
if (block)
dasd_free_block(block);
return 0;
+
+interrupted:
+ /* interrupted by signal */
+ clear_bit(DASD_FLAG_SAFE_OFFLINE, &device->flags);
+ clear_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags);
+ clear_bit(DASD_FLAG_OFFLINE, &device->flags);
+ dasd_put_device(device);
+ return rc;
}
int dasd_generic_last_path_gone(struct dasd_device *device)
@@ -3489,15 +3561,6 @@ char *dasd_get_sense(struct irb *irb)
}
EXPORT_SYMBOL_GPL(dasd_get_sense);
-static inline int _wait_for_empty_queues(struct dasd_device *device)
-{
- if (device->block)
- return list_empty(&device->ccw_queue) &&
- list_empty(&device->block->ccw_queue);
- else
- return list_empty(&device->ccw_queue);
-}
-
void dasd_generic_shutdown(struct ccw_device *cdev)
{
struct dasd_device *device;
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index b2b8c18eece..c196827c228 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -952,6 +952,39 @@ static DEVICE_ATTR(raw_track_access, 0644, dasd_use_raw_show,
dasd_use_raw_store);
static ssize_t
+dasd_safe_offline_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ccw_device *cdev = to_ccwdev(dev);
+ struct dasd_device *device;
+ int rc;
+
+ device = dasd_device_from_cdev(cdev);
+ if (IS_ERR(device)) {
+ rc = PTR_ERR(device);
+ goto out;
+ }
+
+ if (test_bit(DASD_FLAG_OFFLINE, &device->flags) ||
+ test_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) {
+ /* Already doing offline processing */
+ dasd_put_device(device);
+ rc = -EBUSY;
+ goto out;
+ }
+
+ set_bit(DASD_FLAG_SAFE_OFFLINE, &device->flags);
+ dasd_put_device(device);
+
+ rc = ccw_device_set_offline(cdev);
+
+out:
+ return rc ? rc : count;
+}
+
+static DEVICE_ATTR(safe_offline, 0200, NULL, dasd_safe_offline_store);
+
+static ssize_t
dasd_discipline_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -1320,6 +1353,7 @@ static struct attribute * dasd_attrs[] = {
&dev_attr_expires.attr,
&dev_attr_reservation_policy.attr,
&dev_attr_last_known_reservation_state.attr,
+ &dev_attr_safe_offline.attr,
NULL,
};
@@ -1344,7 +1378,7 @@ dasd_get_feature(struct ccw_device *cdev, int feature)
/*
* Set / reset given feature.
- * Flag indicates wether to set (!=0) or the reset (=0) the feature.
+ * Flag indicates whether to set (!=0) or the reset (=0) the feature.
*/
int
dasd_set_feature(struct ccw_device *cdev, int feature, int flag)
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 108332b44d9..806fe912d6e 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1026,7 +1026,7 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
{
void *conf_data;
int conf_len, conf_data_saved;
- int rc;
+ int rc, path_err;
__u8 lpm, opm;
struct dasd_eckd_private *private, path_private;
struct dasd_path *path_data;
@@ -1037,6 +1037,7 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
path_data = &device->path_data;
opm = ccw_device_get_path_mask(device->cdev);
conf_data_saved = 0;
+ path_err = 0;
/* get configuration data per operational path */
for (lpm = 0x80; lpm; lpm>>= 1) {
if (!(lpm & opm))
@@ -1122,7 +1123,8 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
"the same device, path %02X leads to "
"device %s instead of %s\n", lpm,
print_path_uid, print_device_uid);
- return -EINVAL;
+ path_err = -EINVAL;
+ continue;
}
path_private.conf_data = NULL;
@@ -1142,7 +1144,7 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
kfree(conf_data);
}
- return 0;
+ return path_err;
}
static int verify_fcx_max_data(struct dasd_device *device, __u8 lpm)
@@ -3847,7 +3849,7 @@ dasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page)
len = 0;
while (from <= to) {
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" CCW %p: %08X %08X DAT:",
from, ((int *) from)[0], ((int *) from)[1]);
@@ -3908,23 +3910,23 @@ static void dasd_eckd_dump_sense_ccw(struct dasd_device *device,
return;
}
/* dump the sense data */
- len = sprintf(page, KERN_ERR PRINTK_HEADER
+ len = sprintf(page, PRINTK_HEADER
" I/O status report for device %s:\n",
dev_name(&device->cdev->dev));
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X "
"CS:%02X RC:%d\n",
req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw),
scsw_actl(&irb->scsw), scsw_stctl(&irb->scsw),
scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw),
req ? req->intrc : 0);
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" device %s: Failing CCW: %p\n",
dev_name(&device->cdev->dev),
(void *) (addr_t) irb->scsw.cmd.cpa);
if (irb->esw.esw0.erw.cons) {
for (sl = 0; sl < 4; sl++) {
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" Sense(hex) %2d-%2d:",
(8 * sl), ((8 * sl) + 7));
@@ -3937,23 +3939,23 @@ static void dasd_eckd_dump_sense_ccw(struct dasd_device *device,
if (irb->ecw[27] & DASD_SENSE_BIT_0) {
/* 24 Byte Sense Data */
- sprintf(page + len, KERN_ERR PRINTK_HEADER
+ sprintf(page + len, PRINTK_HEADER
" 24 Byte: %x MSG %x, "
"%s MSGb to SYSOP\n",
irb->ecw[7] >> 4, irb->ecw[7] & 0x0f,
irb->ecw[1] & 0x10 ? "" : "no");
} else {
/* 32 Byte Sense Data */
- sprintf(page + len, KERN_ERR PRINTK_HEADER
+ sprintf(page + len, PRINTK_HEADER
" 32 Byte: Format: %x "
"Exception class %x\n",
irb->ecw[6] & 0x0f, irb->ecw[22] >> 4);
}
} else {
- sprintf(page + len, KERN_ERR PRINTK_HEADER
+ sprintf(page + len, PRINTK_HEADER
" SORRY - NO VALID SENSE AVAILABLE\n");
}
- printk("%s", page);
+ printk(KERN_ERR "%s", page);
if (req) {
/* req == NULL for unsolicited interrupts */
@@ -3962,10 +3964,10 @@ static void dasd_eckd_dump_sense_ccw(struct dasd_device *device,
first = req->cpaddr;
for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);
to = min(first + 6, last);
- len = sprintf(page, KERN_ERR PRINTK_HEADER
+ len = sprintf(page, PRINTK_HEADER
" Related CP in req: %p\n", req);
dasd_eckd_dump_ccw_range(first, to, page + len);
- printk("%s", page);
+ printk(KERN_ERR "%s", page);
/* print failing CCW area (maximum 4) */
/* scsw->cda is either valid or zero */
@@ -3975,7 +3977,7 @@ static void dasd_eckd_dump_sense_ccw(struct dasd_device *device,
irb->scsw.cmd.cpa; /* failing CCW */
if (from < fail - 2) {
from = fail - 2; /* there is a gap - print header */
- len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n");
+ len += sprintf(page, PRINTK_HEADER "......\n");
}
to = min(fail + 1, last);
len += dasd_eckd_dump_ccw_range(from, to, page + len);
@@ -3984,11 +3986,11 @@ static void dasd_eckd_dump_sense_ccw(struct dasd_device *device,
from = max(from, ++to);
if (from < last - 1) {
from = last - 1; /* there is a gap - print header */
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
+ len += sprintf(page + len, PRINTK_HEADER "......\n");
}
len += dasd_eckd_dump_ccw_range(from, last, page + len);
if (len > 0)
- printk("%s", page);
+ printk(KERN_ERR "%s", page);
}
free_page((unsigned long) page);
}
@@ -4012,10 +4014,10 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
return;
}
/* dump the sense data */
- len = sprintf(page, KERN_ERR PRINTK_HEADER
+ len = sprintf(page, PRINTK_HEADER
" I/O status report for device %s:\n",
dev_name(&device->cdev->dev));
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X "
"CS:%02X fcxs:%02X schxs:%02X RC:%d\n",
req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw),
@@ -4023,7 +4025,7 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw),
irb->scsw.tm.fcxs, irb->scsw.tm.schxs,
req ? req->intrc : 0);
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" device %s: Failing TCW: %p\n",
dev_name(&device->cdev->dev),
(void *) (addr_t) irb->scsw.tm.tcw);
@@ -4035,43 +4037,42 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
(struct tcw *)(unsigned long)irb->scsw.tm.tcw);
if (tsb) {
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" tsb->length %d\n", tsb->length);
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" tsb->flags %x\n", tsb->flags);
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" tsb->dcw_offset %d\n", tsb->dcw_offset);
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" tsb->count %d\n", tsb->count);
residual = tsb->count - 28;
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" residual %d\n", residual);
switch (tsb->flags & 0x07) {
case 1: /* tsa_iostat */
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" tsb->tsa.iostat.dev_time %d\n",
tsb->tsa.iostat.dev_time);
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" tsb->tsa.iostat.def_time %d\n",
tsb->tsa.iostat.def_time);
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" tsb->tsa.iostat.queue_time %d\n",
tsb->tsa.iostat.queue_time);
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" tsb->tsa.iostat.dev_busy_time %d\n",
tsb->tsa.iostat.dev_busy_time);
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" tsb->tsa.iostat.dev_act_time %d\n",
tsb->tsa.iostat.dev_act_time);
sense = tsb->tsa.iostat.sense;
break;
case 2: /* ts_ddpc */
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" tsb->tsa.ddpc.rc %d\n", tsb->tsa.ddpc.rc);
for (sl = 0; sl < 2; sl++) {
- len += sprintf(page + len,
- KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" tsb->tsa.ddpc.rcq %2d-%2d: ",
(8 * sl), ((8 * sl) + 7));
rcq = tsb->tsa.ddpc.rcq;
@@ -4084,15 +4085,14 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
sense = tsb->tsa.ddpc.sense;
break;
case 3: /* tsa_intrg */
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
- " tsb->tsa.intrg.: not supportet yet \n");
+ len += sprintf(page + len, PRINTK_HEADER
+ " tsb->tsa.intrg.: not supportet yet\n");
break;
}
if (sense) {
for (sl = 0; sl < 4; sl++) {
- len += sprintf(page + len,
- KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" Sense(hex) %2d-%2d:",
(8 * sl), ((8 * sl) + 7));
for (sct = 0; sct < 8; sct++) {
@@ -4104,27 +4104,27 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
if (sense[27] & DASD_SENSE_BIT_0) {
/* 24 Byte Sense Data */
- sprintf(page + len, KERN_ERR PRINTK_HEADER
+ sprintf(page + len, PRINTK_HEADER
" 24 Byte: %x MSG %x, "
"%s MSGb to SYSOP\n",
sense[7] >> 4, sense[7] & 0x0f,
sense[1] & 0x10 ? "" : "no");
} else {
/* 32 Byte Sense Data */
- sprintf(page + len, KERN_ERR PRINTK_HEADER
+ sprintf(page + len, PRINTK_HEADER
" 32 Byte: Format: %x "
"Exception class %x\n",
sense[6] & 0x0f, sense[22] >> 4);
}
} else {
- sprintf(page + len, KERN_ERR PRINTK_HEADER
+ sprintf(page + len, PRINTK_HEADER
" SORRY - NO VALID SENSE AVAILABLE\n");
}
} else {
- sprintf(page + len, KERN_ERR PRINTK_HEADER
+ sprintf(page + len, PRINTK_HEADER
" SORRY - NO TSB DATA AVAILABLE\n");
}
- printk("%s", page);
+ printk(KERN_ERR "%s", page);
free_page((unsigned long) page);
}
@@ -4161,9 +4161,7 @@ static int dasd_eckd_restore_device(struct dasd_device *device)
private = (struct dasd_eckd_private *) device->private;
/* Read Configuration Data */
- rc = dasd_eckd_read_conf(device);
- if (rc)
- goto out_err;
+ dasd_eckd_read_conf(device);
dasd_eckd_get_uid(device, &temp_uid);
/* Generate device unique id */
@@ -4183,9 +4181,7 @@ static int dasd_eckd_restore_device(struct dasd_device *device)
dasd_eckd_validate_server(device, DASD_CQR_FLAGS_FAILFAST);
/* RE-Read Configuration Data */
- rc = dasd_eckd_read_conf(device);
- if (rc)
- goto out_err;
+ dasd_eckd_read_conf(device);
/* Read Feature Codes */
dasd_eckd_read_features(device);
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c
index fb7f3bdc660..eb748507c7f 100644
--- a/drivers/s390/block/dasd_fba.c
+++ b/drivers/s390/block/dasd_fba.c
@@ -479,19 +479,19 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
"No memory to dump sense data");
return;
}
- len = sprintf(page, KERN_ERR PRINTK_HEADER
+ len = sprintf(page, PRINTK_HEADER
" I/O status report for device %s:\n",
dev_name(&device->cdev->dev));
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" in req: %p CS: 0x%02X DS: 0x%02X\n", req,
irb->scsw.cmd.cstat, irb->scsw.cmd.dstat);
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" device %s: Failing CCW: %p\n",
dev_name(&device->cdev->dev),
(void *) (addr_t) irb->scsw.cmd.cpa);
if (irb->esw.esw0.erw.cons) {
for (sl = 0; sl < 4; sl++) {
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" Sense(hex) %2d-%2d:",
(8 * sl), ((8 * sl) + 7));
@@ -502,7 +502,7 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
len += sprintf(page + len, "\n");
}
} else {
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" SORRY - NO VALID SENSE AVAILABLE\n");
}
printk(KERN_ERR "%s", page);
@@ -512,10 +512,9 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
act = req->cpaddr;
for (last = act; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);
end = min(act + 8, last);
- len = sprintf(page, KERN_ERR PRINTK_HEADER
- " Related CP in req: %p\n", req);
+ len = sprintf(page, PRINTK_HEADER " Related CP in req: %p\n", req);
while (act <= end) {
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" CCW %p: %08X %08X DAT:",
act, ((int *) act)[0], ((int *) act)[1]);
for (count = 0; count < 32 && count < act->count;
@@ -533,11 +532,11 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
len = 0;
if (act < ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2) {
act = ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2;
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
+ len += sprintf(page + len, PRINTK_HEADER "......\n");
}
end = min((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa + 2, last);
while (act <= end) {
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" CCW %p: %08X %08X DAT:",
act, ((int *) act)[0], ((int *) act)[1]);
for (count = 0; count < 32 && count < act->count;
@@ -552,10 +551,10 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
/* print last CCWs */
if (act < last - 2) {
act = last - 2;
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
+ len += sprintf(page + len, PRINTK_HEADER "......\n");
}
while (act <= last) {
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ len += sprintf(page + len, PRINTK_HEADER
" CCW %p: %08X %08X DAT:",
act, ((int *) act)[0], ((int *) act)[1]);
for (count = 0; count < 32 && count < act->count;
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index 7ff93eea673..899e3f5a56e 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -516,6 +516,8 @@ struct dasd_block {
#define DASD_FLAG_IS_RESERVED 7 /* The device is reserved */
#define DASD_FLAG_LOCK_STOLEN 8 /* The device lock was stolen */
#define DASD_FLAG_SUSPENDED 9 /* The device was suspended */
+#define DASD_FLAG_SAFE_OFFLINE 10 /* safe offline processing requested*/
+#define DASD_FLAG_SAFE_OFFLINE_RUNNING 11 /* safe offline running */
void dasd_put_device_wake(struct dasd_device *);
diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c
index 8252f37d04e..03c0e044455 100644
--- a/drivers/s390/block/dasd_ioctl.c
+++ b/drivers/s390/block/dasd_ioctl.c
@@ -19,6 +19,7 @@
#include <linux/slab.h>
#include <asm/compat.h>
#include <asm/ccwdev.h>
+#include <asm/schid.h>
#include <asm/cmb.h>
#include <asm/uaccess.h>
@@ -308,11 +309,12 @@ static int dasd_ioctl_information(struct dasd_block *block,
unsigned int cmd, void __user *argp)
{
struct dasd_information2_t *dasd_info;
- unsigned long flags;
- int rc;
+ struct subchannel_id sch_id;
+ struct ccw_dev_id dev_id;
struct dasd_device *base;
struct ccw_device *cdev;
- struct ccw_dev_id dev_id;
+ unsigned long flags;
+ int rc;
base = block->base;
if (!base->discipline || !base->discipline->fill_info)
@@ -330,9 +332,10 @@ static int dasd_ioctl_information(struct dasd_block *block,
cdev = base->cdev;
ccw_device_get_id(cdev, &dev_id);
+ ccw_device_get_schid(cdev, &sch_id);
dasd_info->devno = dev_id.devno;
- dasd_info->schid = _ccw_device_get_subchannel_number(base->cdev);
+ dasd_info->schid = sch_id.sch_no;
dasd_info->cu_type = cdev->id.cu_type;
dasd_info->cu_model = cdev->id.cu_model;
dasd_info->dev_type = cdev->id.dev_type;
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index d7e97ae9ef6..25bcd4c0ed8 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -1,5 +1,5 @@
/*
- * Copyright IBM Corp. 1999, 2009
+ * Copyright IBM Corp. 1999,2012
*
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
@@ -103,6 +103,7 @@ extern u64 sclp_facilities;
#define SCLP_HAS_CHP_RECONFIG (sclp_facilities & 0x2000000000000000ULL)
#define SCLP_HAS_CPU_INFO (sclp_facilities & 0x0800000000000000ULL)
#define SCLP_HAS_CPU_RECONFIG (sclp_facilities & 0x0400000000000000ULL)
+#define SCLP_HAS_PCI_RECONFIG (sclp_facilities & 0x0000000040000000ULL)
struct gds_subvector {
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c
index 71ea923c322..c44d13f607b 100644
--- a/drivers/s390/char/sclp_cmd.c
+++ b/drivers/s390/char/sclp_cmd.c
@@ -1,5 +1,5 @@
/*
- * Copyright IBM Corp. 2007, 2009
+ * Copyright IBM Corp. 2007,2012
*
* Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>,
* Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
@@ -12,6 +12,7 @@
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/err.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/mm.h>
@@ -19,10 +20,11 @@
#include <linux/memory.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <asm/ctl_reg.h>
#include <asm/chpid.h>
-#include <asm/sclp.h>
#include <asm/setup.h>
-#include <asm/ctl_reg.h>
+#include <asm/page.h>
+#include <asm/sclp.h>
#include "sclp.h"
@@ -400,17 +402,15 @@ out:
static int sclp_assign_storage(u16 rn)
{
- unsigned long long start, address;
+ unsigned long long start;
int rc;
rc = do_assign_storage(0x000d0001, rn);
if (rc)
- goto out;
- start = address = rn2addr(rn);
- for (; address < start + rzm; address += PAGE_SIZE)
- page_set_storage_key(address, PAGE_DEFAULT_KEY, 0);
-out:
- return rc;
+ return rc;
+ start = rn2addr(rn);
+ storage_key_init_range(start, start + rzm);
+ return 0;
}
static int sclp_unassign_storage(u16 rn)
@@ -702,6 +702,67 @@ __initcall(sclp_detect_standby_memory);
#endif /* CONFIG_MEMORY_HOTPLUG */
/*
+ * PCI I/O adapter configuration related functions.
+ */
+#define SCLP_CMDW_CONFIGURE_PCI 0x001a0001
+#define SCLP_CMDW_DECONFIGURE_PCI 0x001b0001
+
+#define SCLP_RECONFIG_PCI_ATPYE 2
+
+struct pci_cfg_sccb {
+ struct sccb_header header;
+ u8 atype; /* adapter type */
+ u8 reserved1;
+ u16 reserved2;
+ u32 aid; /* adapter identifier */
+} __packed;
+
+static int do_pci_configure(sclp_cmdw_t cmd, u32 fid)
+{
+ struct pci_cfg_sccb *sccb;
+ int rc;
+
+ if (!SCLP_HAS_PCI_RECONFIG)
+ return -EOPNOTSUPP;
+
+ sccb = (struct pci_cfg_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!sccb)
+ return -ENOMEM;
+
+ sccb->header.length = PAGE_SIZE;
+ sccb->atype = SCLP_RECONFIG_PCI_ATPYE;
+ sccb->aid = fid;
+ rc = do_sync_request(cmd, sccb);
+ if (rc)
+ goto out;
+ switch (sccb->header.response_code) {
+ case 0x0020:
+ case 0x0120:
+ break;
+ default:
+ pr_warn("configure PCI I/O adapter failed: cmd=0x%08x response=0x%04x\n",
+ cmd, sccb->header.response_code);
+ rc = -EIO;
+ break;
+ }
+out:
+ free_page((unsigned long) sccb);
+ return rc;
+}
+
+int sclp_pci_configure(u32 fid)
+{
+ return do_pci_configure(SCLP_CMDW_CONFIGURE_PCI, fid);
+}
+EXPORT_SYMBOL(sclp_pci_configure);
+
+int sclp_pci_deconfigure(u32 fid)
+{
+ return do_pci_configure(SCLP_CMDW_DECONFIGURE_PCI, fid);
+}
+EXPORT_SYMBOL(sclp_pci_deconfigure);
+
+/*
* Channel path configuration related functions.
*/
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index 731470e6849..84846c2b96d 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -65,10 +65,18 @@ static void __ccwgroup_remove_cdev_refs(struct ccwgroup_device *gdev)
}
}
-static int ccwgroup_set_online(struct ccwgroup_device *gdev)
+/**
+ * ccwgroup_set_online() - enable a ccwgroup device
+ * @gdev: target ccwgroup device
+ *
+ * This function attempts to put the ccwgroup device into the online state.
+ * Returns:
+ * %0 on success and a negative error value on failure.
+ */
+int ccwgroup_set_online(struct ccwgroup_device *gdev)
{
struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
- int ret = 0;
+ int ret = -EINVAL;
if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
return -EAGAIN;
@@ -84,11 +92,20 @@ out:
atomic_set(&gdev->onoff, 0);
return ret;
}
+EXPORT_SYMBOL(ccwgroup_set_online);
-static int ccwgroup_set_offline(struct ccwgroup_device *gdev)
+/**
+ * ccwgroup_set_offline() - disable a ccwgroup device
+ * @gdev: target ccwgroup device
+ *
+ * This function attempts to put the ccwgroup device into the offline state.
+ * Returns:
+ * %0 on success and a negative error value on failure.
+ */
+int ccwgroup_set_offline(struct ccwgroup_device *gdev)
{
struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
- int ret = 0;
+ int ret = -EINVAL;
if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
return -EAGAIN;
@@ -104,6 +121,7 @@ out:
atomic_set(&gdev->onoff, 0);
return ret;
}
+EXPORT_SYMBOL(ccwgroup_set_offline);
static ssize_t ccwgroup_online_store(struct device *dev,
struct device_attribute *attr,
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 4d51a7c4eb8..68e80e2734a 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -1,7 +1,7 @@
/*
* S/390 common I/O routines -- channel subsystem call
*
- * Copyright IBM Corp. 1999, 2010
+ * Copyright IBM Corp. 1999,2012
* Author(s): Ingo Adlung (adlung@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
* Arnd Bergmann (arndb@de.ibm.com)
@@ -14,6 +14,7 @@
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/device.h>
+#include <linux/pci.h>
#include <asm/cio.h>
#include <asm/chpid.h>
@@ -260,26 +261,45 @@ __get_chpid_from_lir(void *data)
return (u16) (lir->indesc[0]&0x000000ff);
}
-struct chsc_sei_area {
- struct chsc_header request;
+struct chsc_sei_nt0_area {
+ u8 flags;
+ u8 vf; /* validity flags */
+ u8 rs; /* reporting source */
+ u8 cc; /* content code */
+ u16 fla; /* full link address */
+ u16 rsid; /* reporting source id */
u32 reserved1;
u32 reserved2;
- u32 reserved3;
- struct chsc_header response;
- u32 reserved4;
- u8 flags;
- u8 vf; /* validity flags */
- u8 rs; /* reporting source */
- u8 cc; /* content code */
- u16 fla; /* full link address */
- u16 rsid; /* reporting source id */
- u32 reserved5;
- u32 reserved6;
- u8 ccdf[4096 - 16 - 24]; /* content-code dependent field */
/* ccdf has to be big enough for a link-incident record */
-} __attribute__ ((packed));
-
-static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area)
+ u8 ccdf[PAGE_SIZE - 24 - 16]; /* content-code dependent field */
+} __packed;
+
+struct chsc_sei_nt2_area {
+ u8 flags; /* p and v bit */
+ u8 reserved1;
+ u8 reserved2;
+ u8 cc; /* content code */
+ u32 reserved3[13];
+ u8 ccdf[PAGE_SIZE - 24 - 56]; /* content-code dependent field */
+} __packed;
+
+#define CHSC_SEI_NT0 0ULL
+#define CHSC_SEI_NT2 (1ULL << 61)
+
+struct chsc_sei {
+ struct chsc_header request;
+ u32 reserved1;
+ u64 ntsm; /* notification type mask */
+ struct chsc_header response;
+ u32 reserved2;
+ union {
+ struct chsc_sei_nt0_area nt0_area;
+ struct chsc_sei_nt2_area nt2_area;
+ u8 nt_area[PAGE_SIZE - 24];
+ } u;
+} __packed;
+
+static void chsc_process_sei_link_incident(struct chsc_sei_nt0_area *sei_area)
{
struct chp_id chpid;
int id;
@@ -298,7 +318,7 @@ static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area)
}
}
-static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
+static void chsc_process_sei_res_acc(struct chsc_sei_nt0_area *sei_area)
{
struct chp_link link;
struct chp_id chpid;
@@ -330,7 +350,7 @@ static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
s390_process_res_acc(&link);
}
-static void chsc_process_sei_chp_avail(struct chsc_sei_area *sei_area)
+static void chsc_process_sei_chp_avail(struct chsc_sei_nt0_area *sei_area)
{
struct channel_path *chp;
struct chp_id chpid;
@@ -366,7 +386,7 @@ struct chp_config_data {
u8 pc;
};
-static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
+static void chsc_process_sei_chp_config(struct chsc_sei_nt0_area *sei_area)
{
struct chp_config_data *data;
struct chp_id chpid;
@@ -398,7 +418,7 @@ static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
}
}
-static void chsc_process_sei_scm_change(struct chsc_sei_area *sei_area)
+static void chsc_process_sei_scm_change(struct chsc_sei_nt0_area *sei_area)
{
int ret;
@@ -412,13 +432,26 @@ static void chsc_process_sei_scm_change(struct chsc_sei_area *sei_area)
" failed (rc=%d).\n", ret);
}
-static void chsc_process_sei(struct chsc_sei_area *sei_area)
+static void chsc_process_sei_nt2(struct chsc_sei_nt2_area *sei_area)
{
- /* Check if we might have lost some information. */
- if (sei_area->flags & 0x40) {
- CIO_CRW_EVENT(2, "chsc: event overflow\n");
- css_schedule_eval_all();
+#ifdef CONFIG_PCI
+ switch (sei_area->cc) {
+ case 1:
+ zpci_event_error(sei_area->ccdf);
+ break;
+ case 2:
+ zpci_event_availability(sei_area->ccdf);
+ break;
+ default:
+ CIO_CRW_EVENT(2, "chsc: unhandled sei content code %d\n",
+ sei_area->cc);
+ break;
}
+#endif
+}
+
+static void chsc_process_sei_nt0(struct chsc_sei_nt0_area *sei_area)
+{
/* which kind of information was stored? */
switch (sei_area->cc) {
case 1: /* link incident*/
@@ -443,9 +476,51 @@ static void chsc_process_sei(struct chsc_sei_area *sei_area)
}
}
+static int __chsc_process_crw(struct chsc_sei *sei, u64 ntsm)
+{
+ do {
+ memset(sei, 0, sizeof(*sei));
+ sei->request.length = 0x0010;
+ sei->request.code = 0x000e;
+ sei->ntsm = ntsm;
+
+ if (chsc(sei))
+ break;
+
+ if (sei->response.code == 0x0001) {
+ CIO_CRW_EVENT(2, "chsc: sei successful\n");
+
+ /* Check if we might have lost some information. */
+ if (sei->u.nt0_area.flags & 0x40) {
+ CIO_CRW_EVENT(2, "chsc: event overflow\n");
+ css_schedule_eval_all();
+ }
+
+ switch (sei->ntsm) {
+ case CHSC_SEI_NT0:
+ chsc_process_sei_nt0(&sei->u.nt0_area);
+ return 1;
+ case CHSC_SEI_NT2:
+ chsc_process_sei_nt2(&sei->u.nt2_area);
+ return 1;
+ default:
+ CIO_CRW_EVENT(2, "chsc: unhandled nt (nt=%08Lx)\n",
+ sei->ntsm);
+ return 0;
+ }
+ } else {
+ CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n",
+ sei->response.code);
+ break;
+ }
+ } while (sei->u.nt0_area.flags & 0x80);
+
+ return 0;
+}
+
static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow)
{
- struct chsc_sei_area *sei_area;
+ struct chsc_sei *sei;
if (overflow) {
css_schedule_eval_all();
@@ -459,25 +534,18 @@ static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow)
return;
/* Access to sei_page is serialized through machine check handler
* thread, so no need for locking. */
- sei_area = sei_page;
+ sei = sei_page;
CIO_TRACE_EVENT(2, "prcss");
- do {
- memset(sei_area, 0, sizeof(*sei_area));
- sei_area->request.length = 0x0010;
- sei_area->request.code = 0x000e;
- if (chsc(sei_area))
- break;
- if (sei_area->response.code == 0x0001) {
- CIO_CRW_EVENT(4, "chsc: sei successful\n");
- chsc_process_sei(sei_area);
- } else {
- CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n",
- sei_area->response.code);
- break;
- }
- } while (sei_area->flags & 0x80);
+ /*
+ * The ntsm does not allow to select NT0 and NT2 together. We need to
+ * first check for NT2, than additionally for NT0...
+ */
+#ifdef CONFIG_PCI
+ if (!__chsc_process_crw(sei, CHSC_SEI_NT2))
+#endif
+ __chsc_process_crw(sei, CHSC_SEI_NT0);
}
void chsc_chp_online(struct chp_id chpid)
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index fd3143c291c..6995cff4463 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -2036,16 +2036,6 @@ void ccw_driver_unregister(struct ccw_driver *cdriver)
driver_unregister(&cdriver->driver);
}
-/* Helper func for qdio. */
-struct subchannel_id
-ccw_device_get_subchannel_id(struct ccw_device *cdev)
-{
- struct subchannel *sch;
-
- sch = to_subchannel(cdev->dev.parent);
- return sch->schid;
-}
-
static void ccw_device_todo(struct work_struct *work)
{
struct ccw_device_private *priv;
@@ -2138,4 +2128,3 @@ EXPORT_SYMBOL(ccw_device_set_offline);
EXPORT_SYMBOL(ccw_driver_register);
EXPORT_SYMBOL(ccw_driver_unregister);
EXPORT_SYMBOL(get_ccwdev_by_busid);
-EXPORT_SYMBOL_GPL(ccw_device_get_subchannel_id);
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index 6bace694239..2e575cff984 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -142,9 +142,7 @@ int ccw_device_notify(struct ccw_device *, int);
void ccw_device_set_disconnected(struct ccw_device *cdev);
void ccw_device_set_notoper(struct ccw_device *cdev);
-/* qdio needs this. */
void ccw_device_set_timeout(struct ccw_device *, int);
-extern struct subchannel_id ccw_device_get_subchannel_id(struct ccw_device *);
/* Channel measurement facility related */
void retry_set_schib(struct ccw_device *cdev);
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index ec7fb6d3b47..c77b6e06bf6 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -755,14 +755,18 @@ int ccw_device_tm_intrg(struct ccw_device *cdev)
}
EXPORT_SYMBOL(ccw_device_tm_intrg);
-// FIXME: these have to go:
-
-int
-_ccw_device_get_subchannel_number(struct ccw_device *cdev)
+/**
+ * ccw_device_get_schid - obtain a subchannel id
+ * @cdev: device to obtain the id for
+ * @schid: where to fill in the values
+ */
+void ccw_device_get_schid(struct ccw_device *cdev, struct subchannel_id *schid)
{
- return cdev->private->schid.sch_no;
-}
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ *schid = sch->schid;
+}
+EXPORT_SYMBOL_GPL(ccw_device_get_schid);
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(ccw_device_set_options_mask);
@@ -777,5 +781,4 @@ EXPORT_SYMBOL(ccw_device_start_timeout_key);
EXPORT_SYMBOL(ccw_device_start_key);
EXPORT_SYMBOL(ccw_device_get_ciw);
EXPORT_SYMBOL(ccw_device_get_path_mask);
-EXPORT_SYMBOL(_ccw_device_get_subchannel_number);
EXPORT_SYMBOL_GPL(ccw_device_get_chp_desc);
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index 368368fe04b..908d287f66c 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -234,7 +234,7 @@ static int pgid_cmp(struct pgid *p1, struct pgid *p2)
* Determine pathgroup state from PGID data.
*/
static void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
- int *mismatch, int *reserved, u8 *reset)
+ int *mismatch, u8 *reserved, u8 *reset)
{
struct pgid *pgid = &cdev->private->pgid[0];
struct pgid *first = NULL;
@@ -248,7 +248,7 @@ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
if ((cdev->private->pgid_valid_mask & lpm) == 0)
continue;
if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE)
- *reserved = 1;
+ *reserved |= lpm;
if (pgid_is_reset(pgid)) {
*reset |= lpm;
continue;
@@ -316,14 +316,14 @@ static void snid_done(struct ccw_device *cdev, int rc)
struct subchannel *sch = to_subchannel(cdev->dev.parent);
struct pgid *pgid;
int mismatch = 0;
- int reserved = 0;
+ u8 reserved = 0;
u8 reset = 0;
u8 donepm;
if (rc)
goto out;
pgid_analyze(cdev, &pgid, &mismatch, &reserved, &reset);
- if (reserved)
+ if (reserved == cdev->private->pgid_valid_mask)
rc = -EUSERS;
else if (mismatch)
rc = -EOPNOTSUPP;
@@ -336,7 +336,7 @@ static void snid_done(struct ccw_device *cdev, int rc)
}
out:
CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x "
- "todo=%02x mism=%d rsvd=%d reset=%02x\n", id->ssid,
+ "todo=%02x mism=%d rsvd=%02x reset=%02x\n", id->ssid,
id->devno, rc, cdev->private->pgid_valid_mask, sch->vpm,
cdev->private->pgid_todo_mask, mismatch, reserved, reset);
switch (rc) {
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index e06fa03ea1e..1671d3461f2 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -129,7 +129,6 @@ static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state,
int rc, tmp_count = count, tmp_start = start, nr = q->nr, retried = 0;
unsigned int ccq = 0;
- BUG_ON(!q->irq_ptr->sch_token);
qperf_inc(q, eqbs);
if (!q->is_input_q)
@@ -147,7 +146,6 @@ again:
}
if (rc == 2) {
- BUG_ON(tmp_count == count);
qperf_inc(q, eqbs_partial);
DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS part:%02x",
tmp_count);
@@ -189,8 +187,6 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start,
if (!count)
return 0;
-
- BUG_ON(!q->irq_ptr->sch_token);
qperf_inc(q, sqbs);
if (!q->is_input_q)
@@ -199,7 +195,7 @@ again:
ccq = do_sqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count);
rc = qdio_check_ccq(q, ccq);
if (!rc) {
- WARN_ON(tmp_count);
+ WARN_ON_ONCE(tmp_count);
return count - tmp_count;
}
@@ -224,9 +220,6 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
unsigned char __state = 0;
int i;
- BUG_ON(bufnr > QDIO_MAX_BUFFERS_MASK);
- BUG_ON(count > QDIO_MAX_BUFFERS_PER_Q);
-
if (is_qebsm(q))
return qdio_do_eqbs(q, state, bufnr, count, auto_ack);
@@ -258,9 +251,6 @@ static inline int set_buf_states(struct qdio_q *q, int bufnr,
{
int i;
- BUG_ON(bufnr > QDIO_MAX_BUFFERS_MASK);
- BUG_ON(count > QDIO_MAX_BUFFERS_PER_Q);
-
if (is_qebsm(q))
return qdio_do_sqbs(q, state, bufnr, count);
@@ -345,7 +335,6 @@ again:
/* hipersocket busy condition */
if (unlikely(*busy_bit)) {
- WARN_ON(queue_type(q) != QDIO_IQDIO_QFMT || cc != 2);
retries++;
if (!start_time) {
@@ -559,7 +548,7 @@ static int get_inbound_buffer_frontier(struct qdio_q *q)
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in nop");
break;
default:
- BUG();
+ WARN_ON_ONCE(1);
}
out:
return q->first_to_check;
@@ -678,12 +667,10 @@ static inline void qdio_handle_aobs(struct qdio_q *q, int start, int count)
if (aob == NULL)
continue;
- BUG_ON(q->u.out.sbal_state == NULL);
q->u.out.sbal_state[b].flags |=
QDIO_OUTBUF_STATE_FLAG_PENDING;
q->u.out.aobs[b] = NULL;
} else if (state == SLSB_P_OUTPUT_EMPTY) {
- BUG_ON(q->u.out.sbal_state == NULL);
q->u.out.sbal_state[b].aob = NULL;
}
b = next_buf(b);
@@ -703,12 +690,11 @@ static inline unsigned long qdio_aob_for_buffer(struct qdio_output_q *q,
q->aobs[bufnr] = aob;
}
if (q->aobs[bufnr]) {
- BUG_ON(q->sbal_state == NULL);
q->sbal_state[bufnr].flags = QDIO_OUTBUF_STATE_FLAG_NONE;
q->sbal_state[bufnr].aob = q->aobs[bufnr];
q->aobs[bufnr]->user1 = (u64) q->sbal_state[bufnr].user;
phys_aob = virt_to_phys(q->aobs[bufnr]);
- BUG_ON(phys_aob & 0xFF);
+ WARN_ON_ONCE(phys_aob & 0xFF);
}
out:
@@ -809,8 +795,6 @@ static int get_outbound_buffer_frontier(struct qdio_q *q)
goto out;
switch (state) {
- case SLSB_P_OUTPUT_PENDING:
- BUG();
case SLSB_P_OUTPUT_EMPTY:
/* the adapter got it */
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr,
@@ -840,7 +824,7 @@ static int get_outbound_buffer_frontier(struct qdio_q *q)
case SLSB_P_OUTPUT_HALTED:
break;
default:
- BUG();
+ WARN_ON_ONCE(1);
}
out:
@@ -912,7 +896,7 @@ retry:
static void __qdio_outbound_processing(struct qdio_q *q)
{
qperf_inc(q, tasklet_outbound);
- BUG_ON(atomic_read(&q->nr_buf_used) < 0);
+ WARN_ON_ONCE(atomic_read(&q->nr_buf_used) < 0);
if (qdio_outbound_q_moved(q))
qdio_kick_handler(q);
@@ -1138,16 +1122,10 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
irq_ptr->perf_stat.qdio_int++;
if (IS_ERR(irb)) {
- switch (PTR_ERR(irb)) {
- case -EIO:
- DBF_ERROR("%4x IO error", irq_ptr->schid.sch_no);
- qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
- wake_up(&cdev->private->wait_q);
- return;
- default:
- WARN_ON(1);
- return;
- }
+ DBF_ERROR("%4x IO error", irq_ptr->schid.sch_no);
+ qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
+ wake_up(&cdev->private->wait_q);
+ return;
}
qdio_irq_check_sense(irq_ptr, irb);
cstat = irb->scsw.cmd.cstat;
@@ -1173,7 +1151,7 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
case QDIO_IRQ_STATE_STOPPED:
break;
default:
- WARN_ON(1);
+ WARN_ON_ONCE(1);
}
wake_up(&cdev->private->wait_q);
}
@@ -1227,7 +1205,7 @@ int qdio_shutdown(struct ccw_device *cdev, int how)
if (!irq_ptr)
return -ENODEV;
- BUG_ON(irqs_disabled());
+ WARN_ON_ONCE(irqs_disabled());
DBF_EVENT("qshutdown:%4x", cdev->private->schid.sch_no);
mutex_lock(&irq_ptr->setup_mutex);
@@ -1358,7 +1336,6 @@ int qdio_allocate(struct qdio_initialize *init_data)
irq_ptr->qdr = (struct qdr *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!irq_ptr->qdr)
goto out_rel;
- WARN_ON((unsigned long)irq_ptr->qdr & 0xfff);
if (qdio_allocate_qs(irq_ptr, init_data->no_input_qs,
init_data->no_output_qs))
@@ -1597,9 +1574,7 @@ static int handle_inbound(struct qdio_q *q, unsigned int callflags,
set:
count = set_buf_states(q, bufnr, SLSB_CU_INPUT_EMPTY, count);
-
used = atomic_add_return(count, &q->nr_buf_used) - count;
- BUG_ON(used + count > QDIO_MAX_BUFFERS_PER_Q);
if (need_siga_in(q))
return qdio_siga_input(q);
@@ -1624,7 +1599,6 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags,
count = set_buf_states(q, bufnr, SLSB_CU_OUTPUT_PRIMED, count);
used = atomic_add_return(count, &q->nr_buf_used);
- BUG_ON(used > QDIO_MAX_BUFFERS_PER_Q);
if (used == QDIO_MAX_BUFFERS_PER_Q)
qperf_inc(q, outbound_queue_full);
@@ -1678,7 +1652,6 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags,
{
struct qdio_irq *irq_ptr;
-
if (bufnr >= QDIO_MAX_BUFFERS_PER_Q || count > QDIO_MAX_BUFFERS_PER_Q)
return -EINVAL;
@@ -1721,8 +1694,6 @@ int qdio_start_irq(struct ccw_device *cdev, int nr)
return -ENODEV;
q = irq_ptr->input_qs[nr];
- WARN_ON(queue_irqs_enabled(q));
-
clear_nonshared_ind(irq_ptr);
qdio_stop_polling(q);
clear_bit(QDIO_QUEUE_IRQS_DISABLED, &q->u.in.queue_irq_state);
@@ -1769,7 +1740,6 @@ int qdio_get_next_buffers(struct ccw_device *cdev, int nr, int *bufnr,
if (!irq_ptr)
return -ENODEV;
q = irq_ptr->input_qs[nr];
- WARN_ON(queue_irqs_enabled(q));
/*
* Cannot rely on automatic sync after interrupt since queues may
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c
index 6c973db1498..16ecd35b8e5 100644
--- a/drivers/s390/cio/qdio_setup.c
+++ b/drivers/s390/cio/qdio_setup.c
@@ -140,10 +140,8 @@ static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr,
q->sl = (struct sl *)((char *)q->slib + PAGE_SIZE / 2);
/* fill in sbal */
- for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++) {
+ for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++)
q->sbal[j] = *sbals_array++;
- BUG_ON((unsigned long)q->sbal[j] & 0xff);
- }
/* fill in slib */
if (i > 0) {
@@ -434,9 +432,8 @@ int qdio_setup_irq(struct qdio_initialize *init_data)
irq_ptr->int_parm = init_data->int_parm;
irq_ptr->nr_input_qs = init_data->no_input_qs;
irq_ptr->nr_output_qs = init_data->no_output_qs;
-
- irq_ptr->schid = ccw_device_get_subchannel_id(init_data->cdev);
irq_ptr->cdev = init_data->cdev;
+ ccw_device_get_schid(irq_ptr->cdev, &irq_ptr->schid);
setup_queues(irq_ptr, init_data);
setup_qib(irq_ptr, init_data);
@@ -483,7 +480,7 @@ void qdio_print_subchannel_info(struct qdio_irq *irq_ptr,
char s[80];
snprintf(s, 80, "qdio: %s %s on SC %x using "
- "AI:%d QEBSM:%d PCI:%d TDD:%d SIGA:%s%s%s%s%s\n",
+ "AI:%d QEBSM:%d PRI:%d TDD:%d SIGA:%s%s%s%s%s\n",
dev_name(&cdev->dev),
(irq_ptr->qib.qfmt == QDIO_QETH_QFMT) ? "OSA" :
((irq_ptr->qib.qfmt == QDIO_ZFCP_QFMT) ? "ZFCP" : "HS"),
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index 2e060088fa8..bdb394b066f 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -73,7 +73,6 @@ static void put_indicator(u32 *addr)
void tiqdio_add_input_queues(struct qdio_irq *irq_ptr)
{
mutex_lock(&tiq_list_lock);
- BUG_ON(irq_ptr->nr_input_qs < 1);
list_add_rcu(&irq_ptr->input_qs[0]->entry, &tiq_list);
mutex_unlock(&tiq_list_lock);
xchg(irq_ptr->dsci, 1 << 7);
@@ -83,7 +82,6 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
{
struct qdio_q *q;
- BUG_ON(irq_ptr->nr_input_qs < 1);
q = irq_ptr->input_qs[0];
/* if establish triggered an error */
if (!q || !q->entry.prev || !q->entry.next)
diff --git a/drivers/s390/crypto/zcrypt_msgtype50.c b/drivers/s390/crypto/zcrypt_msgtype50.c
index 035b6dc31b7..7c522f338bd 100644
--- a/drivers/s390/crypto/zcrypt_msgtype50.c
+++ b/drivers/s390/crypto/zcrypt_msgtype50.c
@@ -241,84 +241,70 @@ static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev,
struct ap_message *ap_msg,
struct ica_rsa_modexpo_crt *crt)
{
- int mod_len, short_len, long_len, long_offset, limit;
+ int mod_len, short_len;
unsigned char *p, *q, *dp, *dq, *u, *inp;
mod_len = crt->inputdatalength;
short_len = mod_len / 2;
- long_len = mod_len / 2 + 8;
/*
- * CEX2A cannot handle p, dp, or U > 128 bytes.
- * If we have one of these, we need to do extra checking.
- * For CEX3A the limit is 256 bytes.
+ * CEX2A and CEX3A w/o FW update can handle requests up to
+ * 256 byte modulus (2k keys).
+ * CEX3A with FW update and CEX4A cards are able to handle
+ * 512 byte modulus (4k keys).
*/
- if (zdev->max_mod_size == CEX3A_MAX_MOD_SIZE)
- limit = 256;
- else
- limit = 128;
-
- if (long_len > limit) {
- /*
- * zcrypt_rsa_crt already checked for the leading
- * zeroes of np_prime, bp_key and u_mult_inc.
- */
- long_offset = long_len - limit;
- long_len = limit;
- } else
- long_offset = 0;
-
- /*
- * Instead of doing extra work for p, dp, U > 64 bytes, we'll just use
- * the larger message structure.
- */
- if (long_len <= 64) {
+ if (mod_len <= 128) { /* up to 1024 bit key size */
struct type50_crb1_msg *crb1 = ap_msg->message;
memset(crb1, 0, sizeof(*crb1));
ap_msg->length = sizeof(*crb1);
crb1->header.msg_type_code = TYPE50_TYPE_CODE;
crb1->header.msg_len = sizeof(*crb1);
crb1->keyblock_type = TYPE50_CRB1_FMT;
- p = crb1->p + sizeof(crb1->p) - long_len;
+ p = crb1->p + sizeof(crb1->p) - short_len;
q = crb1->q + sizeof(crb1->q) - short_len;
- dp = crb1->dp + sizeof(crb1->dp) - long_len;
+ dp = crb1->dp + sizeof(crb1->dp) - short_len;
dq = crb1->dq + sizeof(crb1->dq) - short_len;
- u = crb1->u + sizeof(crb1->u) - long_len;
+ u = crb1->u + sizeof(crb1->u) - short_len;
inp = crb1->message + sizeof(crb1->message) - mod_len;
- } else if (long_len <= 128) {
+ } else if (mod_len <= 256) { /* up to 2048 bit key size */
struct type50_crb2_msg *crb2 = ap_msg->message;
memset(crb2, 0, sizeof(*crb2));
ap_msg->length = sizeof(*crb2);
crb2->header.msg_type_code = TYPE50_TYPE_CODE;
crb2->header.msg_len = sizeof(*crb2);
crb2->keyblock_type = TYPE50_CRB2_FMT;
- p = crb2->p + sizeof(crb2->p) - long_len;
+ p = crb2->p + sizeof(crb2->p) - short_len;
q = crb2->q + sizeof(crb2->q) - short_len;
- dp = crb2->dp + sizeof(crb2->dp) - long_len;
+ dp = crb2->dp + sizeof(crb2->dp) - short_len;
dq = crb2->dq + sizeof(crb2->dq) - short_len;
- u = crb2->u + sizeof(crb2->u) - long_len;
+ u = crb2->u + sizeof(crb2->u) - short_len;
inp = crb2->message + sizeof(crb2->message) - mod_len;
- } else {
- /* long_len >= 256 */
+ } else if ((mod_len <= 512) && /* up to 4096 bit key size */
+ (zdev->max_mod_size == CEX3A_MAX_MOD_SIZE)) { /* >= CEX3A */
struct type50_crb3_msg *crb3 = ap_msg->message;
memset(crb3, 0, sizeof(*crb3));
ap_msg->length = sizeof(*crb3);
crb3->header.msg_type_code = TYPE50_TYPE_CODE;
crb3->header.msg_len = sizeof(*crb3);
crb3->keyblock_type = TYPE50_CRB3_FMT;
- p = crb3->p + sizeof(crb3->p) - long_len;
+ p = crb3->p + sizeof(crb3->p) - short_len;
q = crb3->q + sizeof(crb3->q) - short_len;
- dp = crb3->dp + sizeof(crb3->dp) - long_len;
+ dp = crb3->dp + sizeof(crb3->dp) - short_len;
dq = crb3->dq + sizeof(crb3->dq) - short_len;
- u = crb3->u + sizeof(crb3->u) - long_len;
+ u = crb3->u + sizeof(crb3->u) - short_len;
inp = crb3->message + sizeof(crb3->message) - mod_len;
- }
+ } else
+ return -EINVAL;
- if (copy_from_user(p, crt->np_prime + long_offset, long_len) ||
+ /*
+ * correct the offset of p, bp and mult_inv according zcrypt.h
+ * block size right aligned (skip the first byte)
+ */
+ if (copy_from_user(p, crt->np_prime + MSGTYPE_ADJUSTMENT, short_len) ||
copy_from_user(q, crt->nq_prime, short_len) ||
- copy_from_user(dp, crt->bp_key + long_offset, long_len) ||
+ copy_from_user(dp, crt->bp_key + MSGTYPE_ADJUSTMENT, short_len) ||
copy_from_user(dq, crt->bq_key, short_len) ||
- copy_from_user(u, crt->u_mult_inv + long_offset, long_len) ||
+ copy_from_user(u, crt->u_mult_inv + MSGTYPE_ADJUSTMENT, short_len) ||
copy_from_user(inp, crt->inputdata, mod_len))
return -EFAULT;
diff --git a/drivers/s390/crypto/zcrypt_msgtype50.h b/drivers/s390/crypto/zcrypt_msgtype50.h
index e56dc72c773..0a66e4aeeb5 100644
--- a/drivers/s390/crypto/zcrypt_msgtype50.h
+++ b/drivers/s390/crypto/zcrypt_msgtype50.h
@@ -33,6 +33,8 @@
#define MSGTYPE50_CRB2_MAX_MSG_SIZE 0x390 /*sizeof(struct type50_crb2_msg)*/
#define MSGTYPE50_CRB3_MAX_MSG_SIZE 0x710 /*sizeof(struct type50_crb3_msg)*/
+#define MSGTYPE_ADJUSTMENT 0x08 /*type04 extension (not needed in type50)*/
+
int zcrypt_msgtype50_init(void);
void zcrypt_msgtype50_exit(void);
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 74bf1aa7af4..142f632e2a2 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -603,6 +603,7 @@ config SCSI_ARCMSR
source "drivers/scsi/megaraid/Kconfig.megaraid"
source "drivers/scsi/mpt2sas/Kconfig"
+source "drivers/scsi/mpt3sas/Kconfig"
source "drivers/scsi/ufs/Kconfig"
config SCSI_HPTIOP
@@ -1812,6 +1813,7 @@ config SCSI_VIRTIO
This is the virtual HBA driver for virtio. If the kernel will
be used in a virtual machine, say Y or M.
+source "drivers/scsi/csiostor/Kconfig"
endif # SCSI_LOWLEVEL
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 888f73a4aae..b607ba4f563 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -90,6 +90,7 @@ obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx/
obj-$(CONFIG_SCSI_QLA_ISCSI) += libiscsi.o qla4xxx/
obj-$(CONFIG_SCSI_LPFC) += lpfc/
obj-$(CONFIG_SCSI_BFA_FC) += bfa/
+obj-$(CONFIG_SCSI_CHELSIO_FCOE) += csiostor/
obj-$(CONFIG_SCSI_PAS16) += pas16.o
obj-$(CONFIG_SCSI_T128) += t128.o
obj-$(CONFIG_SCSI_DMX3191D) += dmx3191d.o
@@ -106,6 +107,7 @@ obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o
obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/
obj-$(CONFIG_MEGARAID_SAS) += megaraid/
obj-$(CONFIG_SCSI_MPT2SAS) += mpt2sas/
+obj-$(CONFIG_SCSI_MPT3SAS) += mpt3sas/
obj-$(CONFIG_SCSI_UFSHCD) += ufs/
obj-$(CONFIG_SCSI_ACARD) += atp870u.o
obj-$(CONFIG_SCSI_SUNESP) += esp_scsi.o sun_esp.o
diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c
index d79457ac8be..681434e2dfe 100644
--- a/drivers/scsi/aacraid/aachba.c
+++ b/drivers/scsi/aacraid/aachba.c
@@ -132,11 +132,13 @@ struct inquiry_data {
* M O D U L E G L O B A L S
*/
-static unsigned long aac_build_sg(struct scsi_cmnd* scsicmd, struct sgmap* sgmap);
-static unsigned long aac_build_sg64(struct scsi_cmnd* scsicmd, struct sgmap64* psg);
-static unsigned long aac_build_sgraw(struct scsi_cmnd* scsicmd, struct sgmapraw* psg);
-static unsigned long aac_build_sgraw2(struct scsi_cmnd *scsicmd, struct aac_raw_io2 *rio2, int sg_max);
-static int aac_convert_sgraw2(struct aac_raw_io2 *rio2, int pages, int nseg, int nseg_new);
+static long aac_build_sg(struct scsi_cmnd *scsicmd, struct sgmap *sgmap);
+static long aac_build_sg64(struct scsi_cmnd *scsicmd, struct sgmap64 *psg);
+static long aac_build_sgraw(struct scsi_cmnd *scsicmd, struct sgmapraw *psg);
+static long aac_build_sgraw2(struct scsi_cmnd *scsicmd,
+ struct aac_raw_io2 *rio2, int sg_max);
+static int aac_convert_sgraw2(struct aac_raw_io2 *rio2,
+ int pages, int nseg, int nseg_new);
static int aac_send_srb_fib(struct scsi_cmnd* scsicmd);
#ifdef AAC_DETAILED_STATUS_INFO
static char *aac_get_status_string(u32 status);
@@ -971,6 +973,7 @@ static int aac_read_raw_io(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u3
{
struct aac_dev *dev = fib->dev;
u16 fibsize, command;
+ long ret;
aac_fib_init(fib);
if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE2 && !dev->sync_mode) {
@@ -982,7 +985,10 @@ static int aac_read_raw_io(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u3
readcmd2->byteCount = cpu_to_le32(count<<9);
readcmd2->cid = cpu_to_le16(scmd_id(cmd));
readcmd2->flags = cpu_to_le16(RIO2_IO_TYPE_READ);
- aac_build_sgraw2(cmd, readcmd2, dev->scsi_host_ptr->sg_tablesize);
+ ret = aac_build_sgraw2(cmd, readcmd2,
+ dev->scsi_host_ptr->sg_tablesize);
+ if (ret < 0)
+ return ret;
command = ContainerRawIo2;
fibsize = sizeof(struct aac_raw_io2) +
((le32_to_cpu(readcmd2->sgeCnt)-1) * sizeof(struct sge_ieee1212));
@@ -996,7 +1002,9 @@ static int aac_read_raw_io(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u3
readcmd->flags = cpu_to_le16(RIO_TYPE_READ);
readcmd->bpTotal = 0;
readcmd->bpComplete = 0;
- aac_build_sgraw(cmd, &readcmd->sg);
+ ret = aac_build_sgraw(cmd, &readcmd->sg);
+ if (ret < 0)
+ return ret;
command = ContainerRawIo;
fibsize = sizeof(struct aac_raw_io) +
((le32_to_cpu(readcmd->sg.count)-1) * sizeof(struct sgentryraw));
@@ -1019,6 +1027,8 @@ static int aac_read_block64(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u
{
u16 fibsize;
struct aac_read64 *readcmd;
+ long ret;
+
aac_fib_init(fib);
readcmd = (struct aac_read64 *) fib_data(fib);
readcmd->command = cpu_to_le32(VM_CtHostRead64);
@@ -1028,7 +1038,9 @@ static int aac_read_block64(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u
readcmd->pad = 0;
readcmd->flags = 0;
- aac_build_sg64(cmd, &readcmd->sg);
+ ret = aac_build_sg64(cmd, &readcmd->sg);
+ if (ret < 0)
+ return ret;
fibsize = sizeof(struct aac_read64) +
((le32_to_cpu(readcmd->sg.count) - 1) *
sizeof (struct sgentry64));
@@ -1050,6 +1062,8 @@ static int aac_read_block(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u32
{
u16 fibsize;
struct aac_read *readcmd;
+ long ret;
+
aac_fib_init(fib);
readcmd = (struct aac_read *) fib_data(fib);
readcmd->command = cpu_to_le32(VM_CtBlockRead);
@@ -1057,7 +1071,9 @@ static int aac_read_block(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u32
readcmd->block = cpu_to_le32((u32)(lba&0xffffffff));
readcmd->count = cpu_to_le32(count * 512);
- aac_build_sg(cmd, &readcmd->sg);
+ ret = aac_build_sg(cmd, &readcmd->sg);
+ if (ret < 0)
+ return ret;
fibsize = sizeof(struct aac_read) +
((le32_to_cpu(readcmd->sg.count) - 1) *
sizeof (struct sgentry));
@@ -1079,6 +1095,7 @@ static int aac_write_raw_io(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u
{
struct aac_dev *dev = fib->dev;
u16 fibsize, command;
+ long ret;
aac_fib_init(fib);
if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE2 && !dev->sync_mode) {
@@ -1093,7 +1110,10 @@ static int aac_write_raw_io(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u
(((aac_cache & 5) != 5) || !fib->dev->cache_protected)) ?
cpu_to_le16(RIO2_IO_TYPE_WRITE|RIO2_IO_SUREWRITE) :
cpu_to_le16(RIO2_IO_TYPE_WRITE);
- aac_build_sgraw2(cmd, writecmd2, dev->scsi_host_ptr->sg_tablesize);
+ ret = aac_build_sgraw2(cmd, writecmd2,
+ dev->scsi_host_ptr->sg_tablesize);
+ if (ret < 0)
+ return ret;
command = ContainerRawIo2;
fibsize = sizeof(struct aac_raw_io2) +
((le32_to_cpu(writecmd2->sgeCnt)-1) * sizeof(struct sge_ieee1212));
@@ -1110,7 +1130,9 @@ static int aac_write_raw_io(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u
cpu_to_le16(RIO_TYPE_WRITE);
writecmd->bpTotal = 0;
writecmd->bpComplete = 0;
- aac_build_sgraw(cmd, &writecmd->sg);
+ ret = aac_build_sgraw(cmd, &writecmd->sg);
+ if (ret < 0)
+ return ret;
command = ContainerRawIo;
fibsize = sizeof(struct aac_raw_io) +
((le32_to_cpu(writecmd->sg.count)-1) * sizeof (struct sgentryraw));
@@ -1133,6 +1155,8 @@ static int aac_write_block64(struct fib * fib, struct scsi_cmnd * cmd, u64 lba,
{
u16 fibsize;
struct aac_write64 *writecmd;
+ long ret;
+
aac_fib_init(fib);
writecmd = (struct aac_write64 *) fib_data(fib);
writecmd->command = cpu_to_le32(VM_CtHostWrite64);
@@ -1142,7 +1166,9 @@ static int aac_write_block64(struct fib * fib, struct scsi_cmnd * cmd, u64 lba,
writecmd->pad = 0;
writecmd->flags = 0;
- aac_build_sg64(cmd, &writecmd->sg);
+ ret = aac_build_sg64(cmd, &writecmd->sg);
+ if (ret < 0)
+ return ret;
fibsize = sizeof(struct aac_write64) +
((le32_to_cpu(writecmd->sg.count) - 1) *
sizeof (struct sgentry64));
@@ -1164,6 +1190,8 @@ static int aac_write_block(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u3
{
u16 fibsize;
struct aac_write *writecmd;
+ long ret;
+
aac_fib_init(fib);
writecmd = (struct aac_write *) fib_data(fib);
writecmd->command = cpu_to_le32(VM_CtBlockWrite);
@@ -1173,7 +1201,9 @@ static int aac_write_block(struct fib * fib, struct scsi_cmnd * cmd, u64 lba, u3
writecmd->sg.count = cpu_to_le32(1);
/* ->stable is not used - it did mean which type of write */
- aac_build_sg(cmd, &writecmd->sg);
+ ret = aac_build_sg(cmd, &writecmd->sg);
+ if (ret < 0)
+ return ret;
fibsize = sizeof(struct aac_write) +
((le32_to_cpu(writecmd->sg.count) - 1) *
sizeof (struct sgentry));
@@ -1235,8 +1265,11 @@ static int aac_scsi_64(struct fib * fib, struct scsi_cmnd * cmd)
{
u16 fibsize;
struct aac_srb * srbcmd = aac_scsi_common(fib, cmd);
+ long ret;
- aac_build_sg64(cmd, (struct sgmap64*) &srbcmd->sg);
+ ret = aac_build_sg64(cmd, (struct sgmap64 *) &srbcmd->sg);
+ if (ret < 0)
+ return ret;
srbcmd->count = cpu_to_le32(scsi_bufflen(cmd));
memset(srbcmd->cdb, 0, sizeof(srbcmd->cdb));
@@ -1263,8 +1296,11 @@ static int aac_scsi_32(struct fib * fib, struct scsi_cmnd * cmd)
{
u16 fibsize;
struct aac_srb * srbcmd = aac_scsi_common(fib, cmd);
+ long ret;
- aac_build_sg(cmd, (struct sgmap*)&srbcmd->sg);
+ ret = aac_build_sg(cmd, (struct sgmap *)&srbcmd->sg);
+ if (ret < 0)
+ return ret;
srbcmd->count = cpu_to_le32(scsi_bufflen(cmd));
memset(srbcmd->cdb, 0, sizeof(srbcmd->cdb));
@@ -2870,7 +2906,7 @@ static int aac_send_srb_fib(struct scsi_cmnd* scsicmd)
return -1;
}
-static unsigned long aac_build_sg(struct scsi_cmnd* scsicmd, struct sgmap* psg)
+static long aac_build_sg(struct scsi_cmnd *scsicmd, struct sgmap *psg)
{
struct aac_dev *dev;
unsigned long byte_count = 0;
@@ -2883,7 +2919,8 @@ static unsigned long aac_build_sg(struct scsi_cmnd* scsicmd, struct sgmap* psg)
psg->sg[0].count = 0;
nseg = scsi_dma_map(scsicmd);
- BUG_ON(nseg < 0);
+ if (nseg < 0)
+ return nseg;
if (nseg) {
struct scatterlist *sg;
int i;
@@ -2912,7 +2949,7 @@ static unsigned long aac_build_sg(struct scsi_cmnd* scsicmd, struct sgmap* psg)
}
-static unsigned long aac_build_sg64(struct scsi_cmnd* scsicmd, struct sgmap64* psg)
+static long aac_build_sg64(struct scsi_cmnd *scsicmd, struct sgmap64 *psg)
{
struct aac_dev *dev;
unsigned long byte_count = 0;
@@ -2927,7 +2964,8 @@ static unsigned long aac_build_sg64(struct scsi_cmnd* scsicmd, struct sgmap64* p
psg->sg[0].count = 0;
nseg = scsi_dma_map(scsicmd);
- BUG_ON(nseg < 0);
+ if (nseg < 0)
+ return nseg;
if (nseg) {
struct scatterlist *sg;
int i;
@@ -2957,7 +2995,7 @@ static unsigned long aac_build_sg64(struct scsi_cmnd* scsicmd, struct sgmap64* p
return byte_count;
}
-static unsigned long aac_build_sgraw(struct scsi_cmnd* scsicmd, struct sgmapraw* psg)
+static long aac_build_sgraw(struct scsi_cmnd *scsicmd, struct sgmapraw *psg)
{
unsigned long byte_count = 0;
int nseg;
@@ -2972,7 +3010,8 @@ static unsigned long aac_build_sgraw(struct scsi_cmnd* scsicmd, struct sgmapraw*
psg->sg[0].flags = 0;
nseg = scsi_dma_map(scsicmd);
- BUG_ON(nseg < 0);
+ if (nseg < 0)
+ return nseg;
if (nseg) {
struct scatterlist *sg;
int i;
@@ -3005,13 +3044,15 @@ static unsigned long aac_build_sgraw(struct scsi_cmnd* scsicmd, struct sgmapraw*
return byte_count;
}
-static unsigned long aac_build_sgraw2(struct scsi_cmnd *scsicmd, struct aac_raw_io2 *rio2, int sg_max)
+static long aac_build_sgraw2(struct scsi_cmnd *scsicmd,
+ struct aac_raw_io2 *rio2, int sg_max)
{
unsigned long byte_count = 0;
int nseg;
nseg = scsi_dma_map(scsicmd);
- BUG_ON(nseg < 0);
+ if (nseg < 0)
+ return nseg;
if (nseg) {
struct scatterlist *sg;
int i, conformable = 0;
diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h
index 9e933a88a8b..742f5d7eb0f 100644
--- a/drivers/scsi/aacraid/aacraid.h
+++ b/drivers/scsi/aacraid/aacraid.h
@@ -12,7 +12,7 @@
*----------------------------------------------------------------------------*/
#ifndef AAC_DRIVER_BUILD
-# define AAC_DRIVER_BUILD 29800
+# define AAC_DRIVER_BUILD 29801
# define AAC_DRIVER_BRANCH "-ms"
#endif
#define MAXIMUM_NUM_CONTAINERS 32
diff --git a/drivers/scsi/be2iscsi/be.h b/drivers/scsi/be2iscsi/be.h
index a50b6a9030e..f1733dfa3ae 100644
--- a/drivers/scsi/be2iscsi/be.h
+++ b/drivers/scsi/be2iscsi/be.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2011 Emulex
+ * Copyright (C) 2005 - 2012 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -28,7 +28,7 @@
/* BladeEngine Generation numbers */
#define BE_GEN2 2
#define BE_GEN3 3
-
+#define BE_GEN4 4
struct be_dma_mem {
void *va;
dma_addr_t dma;
@@ -84,9 +84,12 @@ static inline void queue_tail_inc(struct be_queue_info *q)
/*ISCSI */
struct be_eq_obj {
+ bool todo_mcc_cq;
+ bool todo_cq;
struct be_queue_info q;
struct beiscsi_hba *phba;
struct be_queue_info *cq;
+ struct work_struct work_cqs; /* Work Item */
struct blk_iopoll iopoll;
};
diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c
index 07d2cb126d9..5c87768c109 100644
--- a/drivers/scsi/be2iscsi/be_cmds.c
+++ b/drivers/scsi/be2iscsi/be_cmds.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2011 Emulex
+ * Copyright (C) 2005 - 2012 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -56,7 +56,7 @@ int beiscsi_pci_soft_reset(struct beiscsi_hba *phba)
writel(pconline0, (void *)pci_online0_offset);
writel(pconline1, (void *)pci_online1_offset);
- sreset = BE2_SET_RESET;
+ sreset |= BE2_SET_RESET;
writel(sreset, (void *)pci_reset_offset);
i = 0;
@@ -133,6 +133,87 @@ unsigned int alloc_mcc_tag(struct beiscsi_hba *phba)
return tag;
}
+/*
+ * beiscsi_mccq_compl()- Wait for completion of MBX
+ * @phba: Driver private structure
+ * @tag: Tag for the MBX Command
+ * @wrb: the WRB used for the MBX Command
+ * @cmd_hdr: IOCTL Hdr for the MBX Cmd
+ *
+ * Waits for MBX completion with the passed TAG.
+ *
+ * return
+ * Success: 0
+ * Failure: Non-Zero
+ **/
+int beiscsi_mccq_compl(struct beiscsi_hba *phba,
+ uint32_t tag, struct be_mcc_wrb **wrb,
+ void *cmd_hdr)
+{
+ int rc = 0;
+ uint32_t mcc_tag_response;
+ uint16_t status = 0, addl_status = 0, wrb_num = 0;
+ struct be_mcc_wrb *temp_wrb;
+ struct be_cmd_req_hdr *ioctl_hdr;
+ struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
+
+ if (beiscsi_error(phba))
+ return -EIO;
+
+ /* wait for the mccq completion */
+ rc = wait_event_interruptible_timeout(
+ phba->ctrl.mcc_wait[tag],
+ phba->ctrl.mcc_numtag[tag],
+ msecs_to_jiffies(
+ BEISCSI_HOST_MBX_TIMEOUT));
+
+ if (rc <= 0) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_INIT | BEISCSI_LOG_EH |
+ BEISCSI_LOG_CONFIG,
+ "BC_%d : MBX Cmd Completion timed out\n");
+ rc = -EAGAIN;
+ goto release_mcc_tag;
+ } else
+ rc = 0;
+
+ mcc_tag_response = phba->ctrl.mcc_numtag[tag];
+ status = (mcc_tag_response & CQE_STATUS_MASK);
+ addl_status = ((mcc_tag_response & CQE_STATUS_ADDL_MASK) >>
+ CQE_STATUS_ADDL_SHIFT);
+
+ if (cmd_hdr) {
+ ioctl_hdr = (struct be_cmd_req_hdr *)cmd_hdr;
+ } else {
+ wrb_num = (mcc_tag_response & CQE_STATUS_WRB_MASK) >>
+ CQE_STATUS_WRB_SHIFT;
+ temp_wrb = (struct be_mcc_wrb *)queue_get_wrb(mccq, wrb_num);
+ ioctl_hdr = embedded_payload(temp_wrb);
+
+ if (wrb)
+ *wrb = temp_wrb;
+ }
+
+ if (status || addl_status) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_INIT | BEISCSI_LOG_EH |
+ BEISCSI_LOG_CONFIG,
+ "BC_%d : MBX Cmd Failed for "
+ "Subsys : %d Opcode : %d with "
+ "Status : %d and Extd_Status : %d\n",
+ ioctl_hdr->subsystem,
+ ioctl_hdr->opcode,
+ status, addl_status);
+ rc = -EAGAIN;
+ }
+
+release_mcc_tag:
+ /* Release the MCC entry */
+ free_mcc_tag(&phba->ctrl, tag);
+
+ return rc;
+}
+
void free_mcc_tag(struct be_ctrl_info *ctrl, unsigned int tag)
{
spin_lock(&ctrl->mbox_lock);
@@ -168,11 +249,24 @@ static inline void be_mcc_compl_use(struct be_mcc_compl *compl)
compl->flags = 0;
}
+/*
+ * be_mcc_compl_process()- Check the MBX comapletion status
+ * @ctrl: Function specific MBX data structure
+ * @compl: Completion status of MBX Command
+ *
+ * Check for the MBX completion status when BMBX method used
+ *
+ * return
+ * Success: Zero
+ * Failure: Non-Zero
+ **/
static int be_mcc_compl_process(struct be_ctrl_info *ctrl,
struct be_mcc_compl *compl)
{
u16 compl_status, extd_status;
+ struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev);
+ struct be_cmd_req_hdr *hdr = embedded_payload(wrb);
be_dws_le_to_cpu(compl, 4);
@@ -184,7 +278,10 @@ static int be_mcc_compl_process(struct be_ctrl_info *ctrl,
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
- "BC_%d : error in cmd completion: status(compl/extd)=%d/%d\n",
+ "BC_%d : error in cmd completion: "
+ "Subsystem : %d Opcode : %d "
+ "status(compl/extd)=%d/%d\n",
+ hdr->subsystem, hdr->opcode,
compl_status, extd_status);
return -EBUSY;
@@ -314,11 +411,24 @@ int beiscsi_process_mcc(struct beiscsi_hba *phba)
return status;
}
-/* Wait till no more pending mcc requests are present */
+/*
+ * be_mcc_wait_compl()- Wait for MBX completion
+ * @phba: driver private structure
+ *
+ * Wait till no more pending mcc requests are present
+ *
+ * return
+ * Success: 0
+ * Failure: Non-Zero
+ *
+ **/
static int be_mcc_wait_compl(struct beiscsi_hba *phba)
{
int i, status;
for (i = 0; i < mcc_timeout; i++) {
+ if (beiscsi_error(phba))
+ return -EIO;
+
status = beiscsi_process_mcc(phba);
if (status)
return status;
@@ -330,51 +440,83 @@ static int be_mcc_wait_compl(struct beiscsi_hba *phba)
if (i == mcc_timeout) {
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
- "BC_%d : mccq poll timed out\n");
-
+ "BC_%d : FW Timed Out\n");
+ phba->fw_timeout = true;
+ beiscsi_ue_detect(phba);
return -EBUSY;
}
return 0;
}
-/* Notify MCC requests and wait for completion */
+/*
+ * be_mcc_notify_wait()- Notify and wait for Compl
+ * @phba: driver private structure
+ *
+ * Notify MCC requests and wait for completion
+ *
+ * return
+ * Success: 0
+ * Failure: Non-Zero
+ **/
int be_mcc_notify_wait(struct beiscsi_hba *phba)
{
be_mcc_notify(phba);
return be_mcc_wait_compl(phba);
}
+/*
+ * be_mbox_db_ready_wait()- Check ready status
+ * @ctrl: Function specific MBX data structure
+ *
+ * Check for the ready status of FW to send BMBX
+ * commands to adapter.
+ *
+ * return
+ * Success: 0
+ * Failure: Non-Zero
+ **/
static int be_mbox_db_ready_wait(struct be_ctrl_info *ctrl)
{
-#define long_delay 2000
void __iomem *db = ctrl->db + MPU_MAILBOX_DB_OFFSET;
- int cnt = 0, wait = 5; /* in usecs */
+ struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev);
+ int wait = 0;
u32 ready;
do {
+
+ if (beiscsi_error(phba))
+ return -EIO;
+
ready = ioread32(db) & MPU_MAILBOX_DB_RDY_MASK;
if (ready)
break;
- if (cnt > 12000000) {
- struct beiscsi_hba *phba = pci_get_drvdata(ctrl->pdev);
+ if (wait > BEISCSI_HOST_MBX_TIMEOUT) {
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
- "BC_%d : mbox_db poll timed out\n");
-
+ "BC_%d : FW Timed Out\n");
+ phba->fw_timeout = true;
+ beiscsi_ue_detect(phba);
return -EBUSY;
}
- if (cnt > 50) {
- wait = long_delay;
- mdelay(long_delay / 1000);
- } else
- udelay(wait);
- cnt += wait;
+ mdelay(1);
+ wait++;
} while (true);
return 0;
}
+/*
+ * be_mbox_notify: Notify adapter of new BMBX command
+ * @ctrl: Function specific MBX data structure
+ *
+ * Ring doorbell to inform adapter of a BMBX command
+ * to process
+ *
+ * return
+ * Success: 0
+ * Failure: Non-Zero
+ **/
int be_mbox_notify(struct be_ctrl_info *ctrl)
{
int status;
@@ -391,13 +533,9 @@ int be_mbox_notify(struct be_ctrl_info *ctrl)
iowrite32(val, db);
status = be_mbox_db_ready_wait(ctrl);
- if (status != 0) {
- beiscsi_log(phba, KERN_ERR,
- BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
- "BC_%d : be_mbox_db_ready_wait failed\n");
-
+ if (status)
return status;
- }
+
val = 0;
val &= ~MPU_MAILBOX_DB_RDY_MASK;
val &= ~MPU_MAILBOX_DB_HI_MASK;
@@ -405,13 +543,9 @@ int be_mbox_notify(struct be_ctrl_info *ctrl)
iowrite32(val, db);
status = be_mbox_db_ready_wait(ctrl);
- if (status != 0) {
- beiscsi_log(phba, KERN_ERR,
- BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
- "BC_%d : be_mbox_db_ready_wait failed\n");
-
+ if (status)
return status;
- }
+
if (be_mcc_compl_is_new(compl)) {
status = be_mcc_compl_process(ctrl, &mbox->compl);
be_mcc_compl_use(compl);
@@ -499,7 +633,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;
+ req_hdr->timeout = BEISCSI_FW_MBX_TIMEOUT;
}
static void be_cmd_page_addrs_prepare(struct phys_addr *pages, u32 max_pages,
@@ -649,18 +783,34 @@ int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl,
OPCODE_COMMON_CQ_CREATE, sizeof(*req));
req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
+ if (chip_skh_r(ctrl->pdev)) {
+ req->hdr.version = MBX_CMD_VER2;
+ req->page_size = 1;
+ AMAP_SET_BITS(struct amap_cq_context_v2, coalescwm,
+ ctxt, coalesce_wm);
+ AMAP_SET_BITS(struct amap_cq_context_v2, nodelay,
+ ctxt, no_delay);
+ AMAP_SET_BITS(struct amap_cq_context_v2, count, ctxt,
+ __ilog2_u32(cq->len / 256));
+ AMAP_SET_BITS(struct amap_cq_context_v2, valid, ctxt, 1);
+ AMAP_SET_BITS(struct amap_cq_context_v2, eventable, ctxt, 1);
+ AMAP_SET_BITS(struct amap_cq_context_v2, eqid, ctxt, eq->id);
+ AMAP_SET_BITS(struct amap_cq_context_v2, armed, ctxt, 1);
+ } else {
+ AMAP_SET_BITS(struct amap_cq_context, coalescwm,
+ ctxt, coalesce_wm);
+ AMAP_SET_BITS(struct amap_cq_context, nodelay, ctxt, no_delay);
+ AMAP_SET_BITS(struct amap_cq_context, count, ctxt,
+ __ilog2_u32(cq->len / 256));
+ AMAP_SET_BITS(struct amap_cq_context, valid, ctxt, 1);
+ AMAP_SET_BITS(struct amap_cq_context, solevent, ctxt, sol_evts);
+ AMAP_SET_BITS(struct amap_cq_context, eventable, ctxt, 1);
+ AMAP_SET_BITS(struct amap_cq_context, eqid, ctxt, eq->id);
+ AMAP_SET_BITS(struct amap_cq_context, armed, ctxt, 1);
+ AMAP_SET_BITS(struct amap_cq_context, func, ctxt,
+ PCI_FUNC(ctrl->pdev->devfn));
+ }
- AMAP_SET_BITS(struct amap_cq_context, coalescwm, ctxt, coalesce_wm);
- AMAP_SET_BITS(struct amap_cq_context, nodelay, ctxt, no_delay);
- AMAP_SET_BITS(struct amap_cq_context, count, ctxt,
- __ilog2_u32(cq->len / 256));
- AMAP_SET_BITS(struct amap_cq_context, valid, ctxt, 1);
- AMAP_SET_BITS(struct amap_cq_context, solevent, ctxt, sol_evts);
- AMAP_SET_BITS(struct amap_cq_context, eventable, ctxt, 1);
- AMAP_SET_BITS(struct amap_cq_context, eqid, ctxt, eq->id);
- AMAP_SET_BITS(struct amap_cq_context, armed, ctxt, 1);
- AMAP_SET_BITS(struct amap_cq_context, func, ctxt,
- PCI_FUNC(ctrl->pdev->devfn));
be_dws_cpu_to_le(ctxt, sizeof(req->context));
be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem);
diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h
index 2c8f98df128..23397d51ac5 100644
--- a/drivers/scsi/be2iscsi/be_cmds.h
+++ b/drivers/scsi/be2iscsi/be_cmds.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2011 Emulex
+ * Copyright (C) 2005 - 2012 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -57,6 +57,16 @@ struct be_mcc_wrb {
#define CQE_STATUS_COMPL_SHIFT 0 /* bits 0 - 15 */
#define CQE_STATUS_EXTD_MASK 0xFFFF
#define CQE_STATUS_EXTD_SHIFT 16 /* bits 0 - 15 */
+#define CQE_STATUS_ADDL_MASK 0xFF00
+#define CQE_STATUS_MASK 0xFF
+#define CQE_STATUS_ADDL_SHIFT 0x08
+#define CQE_STATUS_WRB_MASK 0xFF0000
+#define CQE_STATUS_WRB_SHIFT 16
+#define BEISCSI_HOST_MBX_TIMEOUT (110 * 1000)
+#define BEISCSI_FW_MBX_TIMEOUT 100
+
+/* MBOX Command VER */
+#define MBX_CMD_VER2 0x02
struct be_mcc_compl {
u32 status; /* dword 0 */
@@ -183,7 +193,8 @@ struct be_cmd_req_hdr {
u8 domain; /* dword 0 */
u32 timeout; /* dword 1 */
u32 request_length; /* dword 2 */
- u32 rsvd0; /* dword 3 */
+ u8 version; /* dword 3 */
+ u8 rsvd0[3]; /* dword 3 */
};
struct be_cmd_resp_hdr {
@@ -483,10 +494,28 @@ struct amap_cq_context {
u8 rsvd5[32]; /* dword 3 */
} __packed;
+struct amap_cq_context_v2 {
+ u8 rsvd0[12]; /* dword 0 */
+ u8 coalescwm[2]; /* dword 0 */
+ u8 nodelay; /* dword 0 */
+ u8 rsvd1[12]; /* dword 0 */
+ u8 count[2]; /* dword 0 */
+ u8 valid; /* dword 0 */
+ u8 rsvd2; /* dword 0 */
+ u8 eventable; /* dword 0 */
+ u8 eqid[16]; /* dword 1 */
+ u8 rsvd3[15]; /* dword 1 */
+ u8 armed; /* dword 1 */
+ u8 cqecount[16];/* dword 2 */
+ u8 rsvd4[16]; /* dword 2 */
+ u8 rsvd5[32]; /* dword 3 */
+};
+
struct be_cmd_req_cq_create {
struct be_cmd_req_hdr hdr;
u16 num_pages;
- u16 rsvd0;
+ u8 page_size;
+ u8 rsvd0;
u8 context[sizeof(struct amap_cq_context) / 8];
struct phys_addr pages[4];
} __packed;
@@ -663,6 +692,9 @@ unsigned int be_cmd_get_initname(struct beiscsi_hba *phba);
unsigned int be_cmd_get_port_speed(struct beiscsi_hba *phba);
void free_mcc_tag(struct be_ctrl_info *ctrl, unsigned int tag);
+
+int beiscsi_mccq_compl(struct beiscsi_hba *phba,
+ uint32_t tag, struct be_mcc_wrb **wrb, void *cmd_va);
/*ISCSI Functuions */
int be_cmd_fw_initialize(struct be_ctrl_info *ctrl);
@@ -804,6 +836,59 @@ struct amap_sol_cqe_ring {
u8 valid; /* dword 3 */
} __packed;
+struct amap_sol_cqe_v2 {
+ u8 hw_sts[8]; /* dword 0 */
+ u8 i_sts[8]; /* dword 0 */
+ u8 wrb_index[16]; /* dword 0 */
+ u8 i_exp_cmd_sn[32]; /* dword 1 */
+ u8 code[6]; /* dword 2 */
+ u8 cmd_cmpl; /* dword 2 */
+ u8 rsvd0; /* dword 2 */
+ u8 i_cmd_wnd[8]; /* dword 2 */
+ u8 cid[13]; /* dword 2 */
+ u8 u; /* dword 2 */
+ u8 o; /* dword 2 */
+ u8 s; /* dword 2 */
+ u8 i_res_cnt[31]; /* dword 3 */
+ u8 valid; /* dword 3 */
+} __packed;
+
+struct common_sol_cqe {
+ u32 exp_cmdsn;
+ u32 res_cnt;
+ u16 wrb_index;
+ u16 cid;
+ u8 hw_sts;
+ u8 cmd_wnd;
+ u8 res_flag; /* the s feild of structure */
+ u8 i_resp; /* for skh if cmd_complete is set then i_sts is response */
+ u8 i_flags; /* for skh or the u and o feilds */
+ u8 i_sts; /* for skh if cmd_complete is not-set then i_sts is status */
+};
+
+/*** iSCSI ack/driver message completions ***/
+struct amap_it_dmsg_cqe {
+ u8 ack_num[32]; /* DWORD 0 */
+ u8 pdu_bytes_rcvd[32]; /* DWORD 1 */
+ u8 code[6]; /* DWORD 2 */
+ u8 cid[10]; /* DWORD 2 */
+ u8 wrb_idx[8]; /* DWORD 2 */
+ u8 rsvd0[8]; /* DWORD 2*/
+ u8 rsvd1[31]; /* DWORD 3*/
+ u8 valid; /* DWORD 3 */
+} __packed;
+
+struct amap_it_dmsg_cqe_v2 {
+ u8 ack_num[32]; /* DWORD 0 */
+ u8 pdu_bytes_rcvd[32]; /* DWORD 1 */
+ u8 code[6]; /* DWORD 2 */
+ u8 rsvd0[10]; /* DWORD 2 */
+ u8 wrb_idx[16]; /* DWORD 2 */
+ u8 rsvd1[16]; /* DWORD 3 */
+ u8 cid[13]; /* DWORD 3 */
+ u8 rsvd2[2]; /* DWORD 3 */
+ u8 valid; /* DWORD 3 */
+} __packed;
/**
@@ -992,8 +1077,6 @@ struct be_cmd_get_all_if_id_req {
#define CONNECTION_UPLOAD_ABORT_WITH_SEQ 4 /* Abortive upload with reset,
* sequence number by driver */
-/* Returns byte size of given field with a structure. */
-
/* Returns the number of items in the field array. */
#define BE_NUMBER_OF_FIELD(_type_, _field_) \
(FIELD_SIZEOF(_type_, _field_)/sizeof((((_type_ *)0)->_field_[0])))\
diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c
index aedb0d9a9da..214d691adb5 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.c
+++ b/drivers/scsi/be2iscsi/be_iscsi.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2011 Emulex
+ * Copyright (C) 2005 - 2012 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -531,9 +531,9 @@ static int be2iscsi_get_if_param(struct beiscsi_hba *phba,
break;
case ISCSI_NET_PARAM_IPV4_BOOTPROTO:
if (!if_info.dhcp_state)
- len = sprintf(buf, "static");
+ len = sprintf(buf, "static\n");
else
- len = sprintf(buf, "dhcp");
+ len = sprintf(buf, "dhcp\n");
break;
case ISCSI_NET_PARAM_IPV4_SUBNET:
len = sprintf(buf, "%pI4\n", &if_info.ip_addr.subnet_mask);
@@ -541,7 +541,7 @@ static int be2iscsi_get_if_param(struct beiscsi_hba *phba,
case ISCSI_NET_PARAM_VLAN_ENABLED:
len = sprintf(buf, "%s\n",
(if_info.vlan_priority == BEISCSI_VLAN_DISABLE)
- ? "Disabled" : "Enabled");
+ ? "Disabled\n" : "Enabled\n");
break;
case ISCSI_NET_PARAM_VLAN_ID:
if (if_info.vlan_priority == BEISCSI_VLAN_DISABLE)
@@ -586,7 +586,7 @@ int be2iscsi_iface_get_param(struct iscsi_iface *iface,
len = be2iscsi_get_if_param(phba, iface, param, buf);
break;
case ISCSI_NET_PARAM_IFACE_ENABLE:
- len = sprintf(buf, "enabled");
+ len = sprintf(buf, "enabled\n");
break;
case ISCSI_NET_PARAM_IPV4_GW:
memset(&gateway, 0, sizeof(gateway));
@@ -690,11 +690,9 @@ int beiscsi_set_param(struct iscsi_cls_conn *cls_conn,
static int beiscsi_get_initname(char *buf, struct beiscsi_hba *phba)
{
int rc;
- unsigned int tag, wrb_num;
- unsigned short status, extd_status;
+ unsigned int tag;
struct be_mcc_wrb *wrb;
struct be_cmd_hba_name *resp;
- struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
tag = be_cmd_get_initname(phba);
if (!tag) {
@@ -702,26 +700,16 @@ static int beiscsi_get_initname(char *buf, struct beiscsi_hba *phba)
"BS_%d : Getting Initiator Name Failed\n");
return -EBUSY;
- } else
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
-
- wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
- extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
- status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
+ }
- if (status || extd_status) {
+ rc = beiscsi_mccq_compl(phba, tag, &wrb, NULL);
+ if (rc) {
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
- "BS_%d : MailBox Command Failed with "
- "status = %d extd_status = %d\n",
- status, extd_status);
-
- free_mcc_tag(&phba->ctrl, tag);
- return -EAGAIN;
+ "BS_%d : Initiator Name MBX Failed\n");
+ return rc;
}
- wrb = queue_get_wrb(mccq, wrb_num);
- free_mcc_tag(&phba->ctrl, tag);
+
resp = embedded_payload(wrb);
rc = sprintf(buf, "%s\n", resp->initiator_name);
return rc;
@@ -731,7 +719,6 @@ static int beiscsi_get_initname(char *buf, struct beiscsi_hba *phba)
* beiscsi_get_port_state - Get the Port State
* @shost : pointer to scsi_host structure
*
- * returns number of bytes
*/
static void beiscsi_get_port_state(struct Scsi_Host *shost)
{
@@ -750,13 +737,12 @@ static void beiscsi_get_port_state(struct Scsi_Host *shost)
*/
static int beiscsi_get_port_speed(struct Scsi_Host *shost)
{
- unsigned int tag, wrb_num;
- unsigned short status, extd_status;
+ int rc;
+ unsigned int tag;
struct be_mcc_wrb *wrb;
struct be_cmd_ntwk_link_status_resp *resp;
struct beiscsi_hba *phba = iscsi_host_priv(shost);
struct iscsi_cls_host *ihost = shost->shost_data;
- struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
tag = be_cmd_get_port_speed(phba);
if (!tag) {
@@ -764,26 +750,14 @@ static int beiscsi_get_port_speed(struct Scsi_Host *shost)
"BS_%d : Getting Port Speed Failed\n");
return -EBUSY;
- } else
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
-
- wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
- extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
- status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
-
- if (status || extd_status) {
+ }
+ rc = beiscsi_mccq_compl(phba, tag, &wrb, NULL);
+ if (rc) {
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
- "BS_%d : MailBox Command Failed with "
- "status = %d extd_status = %d\n",
- status, extd_status);
-
- free_mcc_tag(&phba->ctrl, tag);
- return -EAGAIN;
+ "BS_%d : Port Speed MBX Failed\n");
+ return rc;
}
- wrb = queue_get_wrb(mccq, wrb_num);
- free_mcc_tag(&phba->ctrl, tag);
resp = embedded_payload(wrb);
switch (resp->mac_speed) {
@@ -937,6 +911,14 @@ static void beiscsi_set_params_for_offld(struct beiscsi_conn *beiscsi_conn,
session->initial_r2t_en);
AMAP_SET_BITS(struct amap_beiscsi_offload_params, imd, params,
session->imm_data_en);
+ AMAP_SET_BITS(struct amap_beiscsi_offload_params,
+ data_seq_inorder, params,
+ session->dataseq_inorder_en);
+ AMAP_SET_BITS(struct amap_beiscsi_offload_params,
+ pdu_seq_inorder, params,
+ session->pdu_inorder_en);
+ AMAP_SET_BITS(struct amap_beiscsi_offload_params, max_r2t, params,
+ session->max_r2t);
AMAP_SET_BITS(struct amap_beiscsi_offload_params, exp_statsn, params,
(conn->exp_statsn - 1));
}
@@ -1027,12 +1009,10 @@ static int beiscsi_open_conn(struct iscsi_endpoint *ep,
{
struct beiscsi_endpoint *beiscsi_ep = ep->dd_data;
struct beiscsi_hba *phba = beiscsi_ep->phba;
- struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
struct be_mcc_wrb *wrb;
struct tcp_connect_and_offload_out *ptcpcnct_out;
- unsigned short status, extd_status;
struct be_dma_mem nonemb_cmd;
- unsigned int tag, wrb_num;
+ unsigned int tag;
int ret = -ENOMEM;
beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
@@ -1084,35 +1064,26 @@ static int beiscsi_open_conn(struct iscsi_endpoint *ep,
pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
return -EAGAIN;
- } else {
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
}
- wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
- extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
- status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
- if (status || extd_status) {
+
+ ret = beiscsi_mccq_compl(phba, tag, &wrb, NULL);
+ if (ret) {
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
- "BS_%d : mgmt_open_connection Failed"
- " status = %d extd_status = %d\n",
- status, extd_status);
+ "BS_%d : mgmt_open_connection Failed");
- free_mcc_tag(&phba->ctrl, tag);
pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
goto free_ep;
- } else {
- wrb = queue_get_wrb(mccq, wrb_num);
- free_mcc_tag(&phba->ctrl, tag);
-
- ptcpcnct_out = embedded_payload(wrb);
- beiscsi_ep = ep->dd_data;
- beiscsi_ep->fw_handle = ptcpcnct_out->connection_handle;
- beiscsi_ep->cid_vld = 1;
- beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
- "BS_%d : mgmt_open_connection Success\n");
}
+
+ ptcpcnct_out = embedded_payload(wrb);
+ beiscsi_ep = ep->dd_data;
+ beiscsi_ep->fw_handle = ptcpcnct_out->connection_handle;
+ beiscsi_ep->cid_vld = 1;
+ beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
+ "BS_%d : mgmt_open_connection Success\n");
+
pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
return 0;
@@ -1150,8 +1121,8 @@ beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr,
if (phba->state != BE_ADAPTER_UP) {
ret = -EBUSY;
- beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
- "BS_%d : The Adapter state is Not UP\n");
+ beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG,
+ "BS_%d : The Adapter Port state is Down!!!\n");
return ERR_PTR(ret);
}
@@ -1216,11 +1187,9 @@ static int beiscsi_close_conn(struct beiscsi_endpoint *beiscsi_ep, int flag)
beiscsi_ep->ep_cid);
ret = -EAGAIN;
- } else {
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
- free_mcc_tag(&phba->ctrl, tag);
}
+
+ ret = beiscsi_mccq_compl(phba, tag, NULL, NULL);
return ret;
}
@@ -1281,12 +1250,9 @@ void beiscsi_ep_disconnect(struct iscsi_endpoint *ep)
beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
"BS_%d : mgmt_invalidate_connection Failed for cid=%d\n",
beiscsi_ep->ep_cid);
- } else {
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
- free_mcc_tag(&phba->ctrl, tag);
}
+ beiscsi_mccq_compl(phba, tag, NULL, NULL);
beiscsi_close_conn(beiscsi_ep, tcp_upload_flag);
beiscsi_free_ep(beiscsi_ep);
beiscsi_unbind_conn_to_cid(phba, beiscsi_ep->ep_cid);
diff --git a/drivers/scsi/be2iscsi/be_iscsi.h b/drivers/scsi/be2iscsi/be_iscsi.h
index 8b826fc06bc..38eab723215 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.h
+++ b/drivers/scsi/be2iscsi/be_iscsi.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2011 Emulex
+ * Copyright (C) 2005 - 2012 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c
index ff73f9500b0..48d37dded8f 100644
--- a/drivers/scsi/be2iscsi/be_main.c
+++ b/drivers/scsi/be2iscsi/be_main.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2011 Emulex
+ * Copyright (C) 2005 - 2012 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -47,8 +47,6 @@
static unsigned int be_iopoll_budget = 10;
static unsigned int be_max_phys_size = 64;
static unsigned int enable_msix = 1;
-static unsigned int gcrashmode = 0;
-static unsigned int num_hba = 0;
MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table);
MODULE_DESCRIPTION(DRV_DESC " " BUILD_STR);
@@ -153,11 +151,54 @@ BEISCSI_RW_ATTR(log_enable, 0x00,
"\t\t\t\tIO Path Events : 0x10\n"
"\t\t\t\tConfiguration Path : 0x20\n");
+DEVICE_ATTR(beiscsi_drvr_ver, S_IRUGO, beiscsi_drvr_ver_disp, NULL);
+DEVICE_ATTR(beiscsi_adapter_family, S_IRUGO, beiscsi_adap_family_disp, NULL);
struct device_attribute *beiscsi_attrs[] = {
&dev_attr_beiscsi_log_enable,
+ &dev_attr_beiscsi_drvr_ver,
+ &dev_attr_beiscsi_adapter_family,
NULL,
};
+static char const *cqe_desc[] = {
+ "RESERVED_DESC",
+ "SOL_CMD_COMPLETE",
+ "SOL_CMD_KILLED_DATA_DIGEST_ERR",
+ "CXN_KILLED_PDU_SIZE_EXCEEDS_DSL",
+ "CXN_KILLED_BURST_LEN_MISMATCH",
+ "CXN_KILLED_AHS_RCVD",
+ "CXN_KILLED_HDR_DIGEST_ERR",
+ "CXN_KILLED_UNKNOWN_HDR",
+ "CXN_KILLED_STALE_ITT_TTT_RCVD",
+ "CXN_KILLED_INVALID_ITT_TTT_RCVD",
+ "CXN_KILLED_RST_RCVD",
+ "CXN_KILLED_TIMED_OUT",
+ "CXN_KILLED_RST_SENT",
+ "CXN_KILLED_FIN_RCVD",
+ "CXN_KILLED_BAD_UNSOL_PDU_RCVD",
+ "CXN_KILLED_BAD_WRB_INDEX_ERROR",
+ "CXN_KILLED_OVER_RUN_RESIDUAL",
+ "CXN_KILLED_UNDER_RUN_RESIDUAL",
+ "CMD_KILLED_INVALID_STATSN_RCVD",
+ "CMD_KILLED_INVALID_R2T_RCVD",
+ "CMD_CXN_KILLED_LUN_INVALID",
+ "CMD_CXN_KILLED_ICD_INVALID",
+ "CMD_CXN_KILLED_ITT_INVALID",
+ "CMD_CXN_KILLED_SEQ_OUTOFORDER",
+ "CMD_CXN_KILLED_INVALID_DATASN_RCVD",
+ "CXN_INVALIDATE_NOTIFY",
+ "CXN_INVALIDATE_INDEX_NOTIFY",
+ "CMD_INVALIDATED_NOTIFY",
+ "UNSOL_HDR_NOTIFY",
+ "UNSOL_DATA_NOTIFY",
+ "UNSOL_DATA_DIGEST_ERROR_NOTIFY",
+ "DRIVERMSG_NOTIFY",
+ "CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN",
+ "SOL_CMD_KILLED_DIF_ERR",
+ "CXN_KILLED_SYN_RCVD",
+ "CXN_KILLED_IMM_DATA_RCVD"
+};
+
static int beiscsi_slave_configure(struct scsi_device *sdev)
{
blk_queue_max_segment_size(sdev->request_queue, 65536);
@@ -226,11 +267,9 @@ static int beiscsi_eh_abort(struct scsi_cmnd *sc)
nonemb_cmd.va, nonemb_cmd.dma);
return FAILED;
- } else {
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
- free_mcc_tag(&phba->ctrl, tag);
}
+
+ beiscsi_mccq_compl(phba, tag, NULL, nonemb_cmd.va);
pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
return iscsi_eh_abort(sc);
@@ -301,11 +340,9 @@ static int beiscsi_eh_device_reset(struct scsi_cmnd *sc)
pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
return FAILED;
- } else {
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
- free_mcc_tag(&phba->ctrl, tag);
}
+
+ beiscsi_mccq_compl(phba, tag, NULL, nonemb_cmd.va);
pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
return iscsi_eh_device_reset(sc);
@@ -482,6 +519,7 @@ static DEFINE_PCI_DEVICE_TABLE(beiscsi_pci_id_table) = {
{ PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID1) },
{ PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID2) },
{ PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID3) },
+ { PCI_DEVICE(ELX_VENDOR_ID, OC_SKH_ID1) },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table);
@@ -730,7 +768,7 @@ static irqreturn_t be_isr_mcc(int irq, void *dev_id)
resource_id) / 32] &
EQE_RESID_MASK) >> 16) == mcc->id) {
spin_lock_irqsave(&phba->isr_lock, flags);
- phba->todo_mcc_cq = 1;
+ pbe_eq->todo_mcc_cq = true;
spin_unlock_irqrestore(&phba->isr_lock, flags);
}
AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
@@ -738,8 +776,8 @@ static irqreturn_t be_isr_mcc(int irq, void *dev_id)
eqe = queue_tail_node(eq);
num_eq_processed++;
}
- if (phba->todo_mcc_cq)
- queue_work(phba->wq, &phba->work_cqs);
+ if (pbe_eq->todo_mcc_cq)
+ queue_work(phba->wq, &pbe_eq->work_cqs);
if (num_eq_processed)
hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 1, 1);
@@ -779,29 +817,26 @@ static irqreturn_t be_isr_msix(int irq, void *dev_id)
eqe = queue_tail_node(eq);
num_eq_processed++;
}
- if (num_eq_processed)
- hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 0, 1);
-
- return IRQ_HANDLED;
} else {
while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
& EQE_VALID_MASK) {
spin_lock_irqsave(&phba->isr_lock, flags);
- phba->todo_cq = 1;
+ pbe_eq->todo_cq = true;
spin_unlock_irqrestore(&phba->isr_lock, flags);
AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
queue_tail_inc(eq);
eqe = queue_tail_node(eq);
num_eq_processed++;
}
- if (phba->todo_cq)
- queue_work(phba->wq, &phba->work_cqs);
- if (num_eq_processed)
- hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 1, 1);
-
- return IRQ_HANDLED;
+ if (pbe_eq->todo_cq)
+ queue_work(phba->wq, &pbe_eq->work_cqs);
}
+
+ if (num_eq_processed)
+ hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 0, 1);
+
+ return IRQ_HANDLED;
}
/**
@@ -849,7 +884,7 @@ static irqreturn_t be_isr(int irq, void *dev_id)
resource_id) / 32] &
EQE_RESID_MASK) >> 16) == mcc->id) {
spin_lock_irqsave(&phba->isr_lock, flags);
- phba->todo_mcc_cq = 1;
+ pbe_eq->todo_mcc_cq = true;
spin_unlock_irqrestore(&phba->isr_lock, flags);
num_mcceq_processed++;
} else {
@@ -862,8 +897,8 @@ static irqreturn_t be_isr(int irq, void *dev_id)
eqe = queue_tail_node(eq);
}
if (num_ioeq_processed || num_mcceq_processed) {
- if (phba->todo_mcc_cq)
- queue_work(phba->wq, &phba->work_cqs);
+ if (pbe_eq->todo_mcc_cq)
+ queue_work(phba->wq, &pbe_eq->work_cqs);
if ((num_mcceq_processed) && (!num_ioeq_processed))
hwi_ring_eq_db(phba, eq->id, 0,
@@ -886,11 +921,11 @@ static irqreturn_t be_isr(int irq, void *dev_id)
resource_id) / 32] &
EQE_RESID_MASK) >> 16) != cq->id) {
spin_lock_irqsave(&phba->isr_lock, flags);
- phba->todo_mcc_cq = 1;
+ pbe_eq->todo_mcc_cq = true;
spin_unlock_irqrestore(&phba->isr_lock, flags);
} else {
spin_lock_irqsave(&phba->isr_lock, flags);
- phba->todo_cq = 1;
+ pbe_eq->todo_cq = true;
spin_unlock_irqrestore(&phba->isr_lock, flags);
}
AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
@@ -898,8 +933,8 @@ static irqreturn_t be_isr(int irq, void *dev_id)
eqe = queue_tail_node(eq);
num_ioeq_processed++;
}
- if (phba->todo_cq || phba->todo_mcc_cq)
- queue_work(phba->wq, &phba->work_cqs);
+ if (pbe_eq->todo_cq || pbe_eq->todo_mcc_cq)
+ queue_work(phba->wq, &pbe_eq->work_cqs);
if (num_ioeq_processed) {
hwi_ring_eq_db(phba, eq->id, 0,
@@ -1211,7 +1246,8 @@ free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle)
static void
be_complete_io(struct beiscsi_conn *beiscsi_conn,
- struct iscsi_task *task, struct sol_cqe *psol)
+ struct iscsi_task *task,
+ struct common_sol_cqe *csol_cqe)
{
struct beiscsi_io_task *io_task = task->dd_data;
struct be_status_bhs *sts_bhs =
@@ -1221,20 +1257,14 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn,
u32 resid = 0, exp_cmdsn, max_cmdsn;
u8 rsp, status, flags;
- exp_cmdsn = (psol->
- dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32]
- & SOL_EXP_CMD_SN_MASK);
- max_cmdsn = ((psol->
- dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32]
- & SOL_EXP_CMD_SN_MASK) +
- ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
- / 32] & SOL_CMD_WND_MASK) >> 24) - 1);
- rsp = ((psol->dw[offsetof(struct amap_sol_cqe, i_resp) / 32]
- & SOL_RESP_MASK) >> 16);
- status = ((psol->dw[offsetof(struct amap_sol_cqe, i_sts) / 32]
- & SOL_STS_MASK) >> 8);
- flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32]
- & SOL_FLAGS_MASK) >> 24) | 0x80;
+ exp_cmdsn = csol_cqe->exp_cmdsn;
+ max_cmdsn = (csol_cqe->exp_cmdsn +
+ csol_cqe->cmd_wnd - 1);
+ rsp = csol_cqe->i_resp;
+ status = csol_cqe->i_sts;
+ flags = csol_cqe->i_flags;
+ resid = csol_cqe->res_cnt;
+
if (!task->sc) {
if (io_task->scsi_cmnd)
scsi_dma_unmap(io_task->scsi_cmnd);
@@ -1249,9 +1279,6 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn,
/* bidi not initially supported */
if (flags & (ISCSI_FLAG_CMD_UNDERFLOW | ISCSI_FLAG_CMD_OVERFLOW)) {
- resid = (psol->dw[offsetof(struct amap_sol_cqe, i_res_cnt) /
- 32] & SOL_RES_CNT_MASK);
-
if (!status && (flags & ISCSI_FLAG_CMD_OVERFLOW))
task->sc->result = DID_ERROR << 16;
@@ -1273,13 +1300,8 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn,
min_t(u16, sense_len, SCSI_SENSE_BUFFERSIZE));
}
- if (io_task->cmd_bhs->iscsi_hdr.flags & ISCSI_FLAG_CMD_READ) {
- if (psol->dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32]
- & SOL_RES_CNT_MASK)
- conn->rxdata_octets += (psol->
- dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32]
- & SOL_RES_CNT_MASK);
- }
+ if (io_task->cmd_bhs->iscsi_hdr.flags & ISCSI_FLAG_CMD_READ)
+ conn->rxdata_octets += resid;
unmap:
scsi_dma_unmap(io_task->scsi_cmnd);
iscsi_complete_scsi_task(task, exp_cmdsn, max_cmdsn);
@@ -1287,7 +1309,8 @@ unmap:
static void
be_complete_logout(struct beiscsi_conn *beiscsi_conn,
- struct iscsi_task *task, struct sol_cqe *psol)
+ struct iscsi_task *task,
+ struct common_sol_cqe *csol_cqe)
{
struct iscsi_logout_rsp *hdr;
struct beiscsi_io_task *io_task = task->dd_data;
@@ -1297,18 +1320,11 @@ be_complete_logout(struct beiscsi_conn *beiscsi_conn,
hdr->opcode = ISCSI_OP_LOGOUT_RSP;
hdr->t2wait = 5;
hdr->t2retain = 0;
- hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32]
- & SOL_FLAGS_MASK) >> 24) | 0x80;
- hdr->response = (psol->dw[offsetof(struct amap_sol_cqe, i_resp) /
- 32] & SOL_RESP_MASK);
- hdr->exp_cmdsn = cpu_to_be32(psol->
- dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32]
- & SOL_EXP_CMD_SN_MASK);
- hdr->max_cmdsn = be32_to_cpu((psol->
- dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32]
- & SOL_EXP_CMD_SN_MASK) +
- ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
- / 32] & SOL_CMD_WND_MASK) >> 24) - 1);
+ hdr->flags = csol_cqe->i_flags;
+ hdr->response = csol_cqe->i_resp;
+ hdr->exp_cmdsn = csol_cqe->exp_cmdsn;
+ hdr->max_cmdsn = (csol_cqe->exp_cmdsn + csol_cqe->cmd_wnd - 1);
+
hdr->dlength[0] = 0;
hdr->dlength[1] = 0;
hdr->dlength[2] = 0;
@@ -1319,7 +1335,8 @@ be_complete_logout(struct beiscsi_conn *beiscsi_conn,
static void
be_complete_tmf(struct beiscsi_conn *beiscsi_conn,
- struct iscsi_task *task, struct sol_cqe *psol)
+ struct iscsi_task *task,
+ struct common_sol_cqe *csol_cqe)
{
struct iscsi_tm_rsp *hdr;
struct iscsi_conn *conn = beiscsi_conn->conn;
@@ -1327,16 +1344,12 @@ be_complete_tmf(struct beiscsi_conn *beiscsi_conn,
hdr = (struct iscsi_tm_rsp *)task->hdr;
hdr->opcode = ISCSI_OP_SCSI_TMFUNC_RSP;
- hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32]
- & SOL_FLAGS_MASK) >> 24) | 0x80;
- hdr->response = (psol->dw[offsetof(struct amap_sol_cqe, i_resp) /
- 32] & SOL_RESP_MASK);
- hdr->exp_cmdsn = cpu_to_be32(psol->dw[offsetof(struct amap_sol_cqe,
- i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK);
- hdr->max_cmdsn = be32_to_cpu((psol->dw[offsetof(struct amap_sol_cqe,
- i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK) +
- ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
- / 32] & SOL_CMD_WND_MASK) >> 24) - 1);
+ hdr->flags = csol_cqe->i_flags;
+ hdr->response = csol_cqe->i_resp;
+ hdr->exp_cmdsn = csol_cqe->exp_cmdsn;
+ hdr->max_cmdsn = (csol_cqe->exp_cmdsn +
+ csol_cqe->cmd_wnd - 1);
+
hdr->itt = io_task->libiscsi_itt;
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
}
@@ -1352,15 +1365,24 @@ hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_io_task *io_task;
struct iscsi_conn *conn = beiscsi_conn->conn;
struct iscsi_session *session = conn->session;
+ uint16_t wrb_index, cid;
phwi_ctrlr = phba->phwi_ctrlr;
- pwrb_context = &phwi_ctrlr->wrb_context[((psol->
- dw[offsetof(struct amap_sol_cqe, cid) / 32] &
- SOL_CID_MASK) >> 6) -
- phba->fw_config.iscsi_cid_start];
- pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
- dw[offsetof(struct amap_sol_cqe, wrb_index) /
- 32] & SOL_WRB_INDEX_MASK) >> 16)];
+ if (chip_skh_r(phba->pcidev)) {
+ wrb_index = AMAP_GET_BITS(struct amap_it_dmsg_cqe_v2,
+ wrb_idx, psol);
+ cid = AMAP_GET_BITS(struct amap_it_dmsg_cqe_v2,
+ cid, psol);
+ } else {
+ wrb_index = AMAP_GET_BITS(struct amap_it_dmsg_cqe,
+ wrb_idx, psol);
+ cid = AMAP_GET_BITS(struct amap_it_dmsg_cqe,
+ cid, psol);
+ }
+
+ pwrb_context = &phwi_ctrlr->wrb_context[
+ cid - phba->fw_config.iscsi_cid_start];
+ pwrb_handle = pwrb_context->pwrb_handle_basestd[wrb_index];
task = pwrb_handle->pio_handle;
io_task = task->dd_data;
@@ -1374,26 +1396,78 @@ hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn,
static void
be_complete_nopin_resp(struct beiscsi_conn *beiscsi_conn,
- struct iscsi_task *task, struct sol_cqe *psol)
+ struct iscsi_task *task,
+ struct common_sol_cqe *csol_cqe)
{
struct iscsi_nopin *hdr;
struct iscsi_conn *conn = beiscsi_conn->conn;
struct beiscsi_io_task *io_task = task->dd_data;
hdr = (struct iscsi_nopin *)task->hdr;
- hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32]
- & SOL_FLAGS_MASK) >> 24) | 0x80;
- hdr->exp_cmdsn = cpu_to_be32(psol->dw[offsetof(struct amap_sol_cqe,
- i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK);
- hdr->max_cmdsn = be32_to_cpu((psol->dw[offsetof(struct amap_sol_cqe,
- i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK) +
- ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
- / 32] & SOL_CMD_WND_MASK) >> 24) - 1);
+ hdr->flags = csol_cqe->i_flags;
+ hdr->exp_cmdsn = cpu_to_be32(csol_cqe->exp_cmdsn);
+ hdr->max_cmdsn = be32_to_cpu(hdr->exp_cmdsn +
+ csol_cqe->cmd_wnd - 1);
+
hdr->opcode = ISCSI_OP_NOOP_IN;
hdr->itt = io_task->libiscsi_itt;
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
}
+static void adapter_get_sol_cqe(struct beiscsi_hba *phba,
+ struct sol_cqe *psol,
+ struct common_sol_cqe *csol_cqe)
+{
+ if (chip_skh_r(phba->pcidev)) {
+ csol_cqe->exp_cmdsn = AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ i_exp_cmd_sn, psol);
+ csol_cqe->res_cnt = AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ i_res_cnt, psol);
+ csol_cqe->wrb_index = AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ wrb_index, psol);
+ csol_cqe->cid = AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ cid, psol);
+ csol_cqe->hw_sts = AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ hw_sts, psol);
+ csol_cqe->cmd_wnd = AMAP_GET_BITS(struct amap_sol_cqe,
+ i_cmd_wnd, psol);
+ if (AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ cmd_cmpl, psol))
+ csol_cqe->i_sts = AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ i_sts, psol);
+ else
+ csol_cqe->i_resp = AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ i_sts, psol);
+ if (AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ u, psol))
+ csol_cqe->i_flags = ISCSI_FLAG_CMD_UNDERFLOW;
+
+ if (AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ o, psol))
+ csol_cqe->i_flags |= ISCSI_FLAG_CMD_OVERFLOW;
+ } else {
+ csol_cqe->exp_cmdsn = AMAP_GET_BITS(struct amap_sol_cqe,
+ i_exp_cmd_sn, psol);
+ csol_cqe->res_cnt = AMAP_GET_BITS(struct amap_sol_cqe,
+ i_res_cnt, psol);
+ csol_cqe->cmd_wnd = AMAP_GET_BITS(struct amap_sol_cqe,
+ i_cmd_wnd, psol);
+ csol_cqe->wrb_index = AMAP_GET_BITS(struct amap_sol_cqe,
+ wrb_index, psol);
+ csol_cqe->cid = AMAP_GET_BITS(struct amap_sol_cqe,
+ cid, psol);
+ csol_cqe->hw_sts = AMAP_GET_BITS(struct amap_sol_cqe,
+ hw_sts, psol);
+ csol_cqe->i_resp = AMAP_GET_BITS(struct amap_sol_cqe,
+ i_resp, psol);
+ csol_cqe->i_sts = AMAP_GET_BITS(struct amap_sol_cqe,
+ i_sts, psol);
+ csol_cqe->i_flags = AMAP_GET_BITS(struct amap_sol_cqe,
+ i_flags, psol);
+ }
+}
+
+
static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_hba *phba, struct sol_cqe *psol)
{
@@ -1405,19 +1479,22 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn,
unsigned int type;
struct iscsi_conn *conn = beiscsi_conn->conn;
struct iscsi_session *session = conn->session;
+ struct common_sol_cqe csol_cqe = {0};
phwi_ctrlr = phba->phwi_ctrlr;
- pwrb_context = &phwi_ctrlr->wrb_context[((psol->dw[offsetof
- (struct amap_sol_cqe, cid) / 32]
- & SOL_CID_MASK) >> 6) -
- phba->fw_config.iscsi_cid_start];
- pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
- dw[offsetof(struct amap_sol_cqe, wrb_index) /
- 32] & SOL_WRB_INDEX_MASK) >> 16)];
+
+ /* Copy the elements to a common structure */
+ adapter_get_sol_cqe(phba, psol, &csol_cqe);
+
+ pwrb_context = &phwi_ctrlr->wrb_context[
+ csol_cqe.cid - phba->fw_config.iscsi_cid_start];
+
+ pwrb_handle = pwrb_context->pwrb_handle_basestd[
+ csol_cqe.wrb_index];
+
task = pwrb_handle->pio_handle;
pwrb = pwrb_handle->pwrb;
- type = (pwrb->dw[offsetof(struct amap_iscsi_wrb, type) / 32] &
- WRB_TYPE_MASK) >> 28;
+ type = ((struct beiscsi_io_task *)task->dd_data)->wrb_type;
spin_lock_bh(&session->lock);
switch (type) {
@@ -1425,17 +1502,16 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn,
case HWH_TYPE_IO_RD:
if ((task->hdr->opcode & ISCSI_OPCODE_MASK) ==
ISCSI_OP_NOOP_OUT)
- be_complete_nopin_resp(beiscsi_conn, task, psol);
+ be_complete_nopin_resp(beiscsi_conn, task, &csol_cqe);
else
- be_complete_io(beiscsi_conn, task, psol);
+ be_complete_io(beiscsi_conn, task, &csol_cqe);
break;
case HWH_TYPE_LOGOUT:
if ((task->hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT)
- be_complete_logout(beiscsi_conn, task, psol);
+ be_complete_logout(beiscsi_conn, task, &csol_cqe);
else
- be_complete_tmf(beiscsi_conn, task, psol);
-
+ be_complete_tmf(beiscsi_conn, task, &csol_cqe);
break;
case HWH_TYPE_LOGIN:
@@ -1446,7 +1522,7 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn,
break;
case HWH_TYPE_NOP:
- be_complete_nopin_resp(beiscsi_conn, task, psol);
+ be_complete_nopin_resp(beiscsi_conn, task, &csol_cqe);
break;
default:
@@ -1454,10 +1530,8 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
"BM_%d : In hwi_complete_cmd, unknown type = %d"
"wrb_index 0x%x CID 0x%x\n", type,
- ((psol->dw[offsetof(struct amap_iscsi_wrb,
- type) / 32] & SOL_WRB_INDEX_MASK) >> 16),
- ((psol->dw[offsetof(struct amap_sol_cqe,
- cid) / 32] & SOL_CID_MASK) >> 6));
+ csol_cqe.wrb_index,
+ csol_cqe.cid);
break;
}
@@ -1485,13 +1559,26 @@ hwi_get_async_handle(struct beiscsi_hba *phba,
struct list_head *pbusy_list;
struct async_pdu_handle *pasync_handle = NULL;
unsigned char is_header = 0;
+ unsigned int index, dpl;
+
+ if (chip_skh_r(phba->pcidev)) {
+ dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
+ dpl, pdpdu_cqe);
+ index = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
+ index, pdpdu_cqe);
+ } else {
+ dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
+ dpl, pdpdu_cqe);
+ index = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
+ index, pdpdu_cqe);
+ }
phys_addr.u.a32.address_lo =
- pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, db_addr_lo) / 32] -
- ((pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, dpl) / 32]
- & PDUCQE_DPL_MASK) >> 16);
+ (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe,
+ db_addr_lo) / 32] - dpl);
phys_addr.u.a32.address_hi =
- pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, db_addr_hi) / 32];
+ pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe,
+ db_addr_hi) / 32];
phys_addr.u.a64.address =
*((unsigned long long *)(&phys_addr.u.a64.address));
@@ -1501,14 +1588,12 @@ hwi_get_async_handle(struct beiscsi_hba *phba,
case UNSOL_HDR_NOTIFY:
is_header = 1;
- pbusy_list = hwi_get_async_busy_list(pasync_ctx, 1,
- (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe,
- index) / 32] & PDUCQE_INDEX_MASK));
+ pbusy_list = hwi_get_async_busy_list(pasync_ctx,
+ is_header, index);
break;
case UNSOL_DATA_NOTIFY:
- pbusy_list = hwi_get_async_busy_list(pasync_ctx, 0, (pdpdu_cqe->
- dw[offsetof(struct amap_i_t_dpdu_cqe,
- index) / 32] & PDUCQE_INDEX_MASK));
+ pbusy_list = hwi_get_async_busy_list(pasync_ctx,
+ is_header, index);
break;
default:
pbusy_list = NULL;
@@ -1531,12 +1616,9 @@ hwi_get_async_handle(struct beiscsi_hba *phba,
pasync_handle->cri = (unsigned short)beiscsi_conn->beiscsi_conn_cid -
phba->fw_config.iscsi_cid_start;
pasync_handle->is_header = is_header;
- pasync_handle->buffer_len = ((pdpdu_cqe->
- dw[offsetof(struct amap_i_t_dpdu_cqe, dpl) / 32]
- & PDUCQE_DPL_MASK) >> 16);
+ pasync_handle->buffer_len = dpl;
+ *pcq_index = index;
- *pcq_index = (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe,
- index) / 32] & PDUCQE_INDEX_MASK);
return pasync_handle;
}
@@ -1914,6 +1996,13 @@ static void beiscsi_process_mcc_isr(struct beiscsi_hba *phba)
}
+/**
+ * beiscsi_process_cq()- Process the Completion Queue
+ * @pbe_eq: Event Q on which the Completion has come
+ *
+ * return
+ * Number of Completion Entries processed.
+ **/
static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
{
struct be_queue_info *cq;
@@ -1935,12 +2024,24 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
CQE_VALID_MASK) {
be_dws_le_to_cpu(sol, sizeof(struct sol_cqe));
- cid = ((sol->dw[offsetof(struct amap_sol_cqe, cid)/32] &
- CQE_CID_MASK) >> 6);
- code = (sol->dw[offsetof(struct amap_sol_cqe, code)/32] &
- CQE_CODE_MASK);
- ep = phba->ep_array[cid - phba->fw_config.iscsi_cid_start];
+ code = (sol->dw[offsetof(struct amap_sol_cqe, code) /
+ 32] & CQE_CODE_MASK);
+
+ /* Get the CID */
+ if (chip_skh_r(phba->pcidev)) {
+ if ((code == DRIVERMSG_NOTIFY) ||
+ (code == UNSOL_HDR_NOTIFY) ||
+ (code == UNSOL_DATA_NOTIFY))
+ cid = AMAP_GET_BITS(
+ struct amap_i_t_dpdu_cqe_v2,
+ cid, sol);
+ else
+ cid = AMAP_GET_BITS(struct amap_sol_cqe_v2,
+ cid, sol);
+ } else
+ cid = AMAP_GET_BITS(struct amap_sol_cqe, cid, sol);
+ ep = phba->ep_array[cid - phba->fw_config.iscsi_cid_start];
beiscsi_ep = ep->dd_data;
beiscsi_conn = beiscsi_ep->conn;
@@ -1958,7 +2059,8 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
case DRIVERMSG_NOTIFY:
beiscsi_log(phba, KERN_INFO,
BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
- "BM_%d : Received DRIVERMSG_NOTIFY\n");
+ "BM_%d : Received %s[%d] on CID : %d\n",
+ cqe_desc[code], code, cid);
dmsg = (struct dmsg_cqe *)sol;
hwi_complete_drvr_msgs(beiscsi_conn, phba, sol);
@@ -1966,7 +2068,8 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
case UNSOL_HDR_NOTIFY:
beiscsi_log(phba, KERN_INFO,
BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
- "BM_%d : Received UNSOL_HDR_ NOTIFY\n");
+ "BM_%d : Received %s[%d] on CID : %d\n",
+ cqe_desc[code], code, cid);
hwi_process_default_pdu_ring(beiscsi_conn, phba,
(struct i_t_dpdu_cqe *)sol);
@@ -1974,7 +2077,8 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
case UNSOL_DATA_NOTIFY:
beiscsi_log(phba, KERN_INFO,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
- "BM_%d : Received UNSOL_DATA_NOTIFY\n");
+ "BM_%d : Received %s[%d] on CID : %d\n",
+ cqe_desc[code], code, cid);
hwi_process_default_pdu_ring(beiscsi_conn, phba,
(struct i_t_dpdu_cqe *)sol);
@@ -1984,8 +2088,8 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
case CXN_INVALIDATE_NOTIFY:
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
- "BM_%d : Ignoring CQ Error notification for"
- " cmd/cxn invalidate\n");
+ "BM_%d : Ignoring %s[%d] on CID : %d\n",
+ cqe_desc[code], code, cid);
break;
case SOL_CMD_KILLED_DATA_DIGEST_ERR:
case CMD_KILLED_INVALID_STATSN_RCVD:
@@ -1997,14 +2101,14 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
case CMD_CXN_KILLED_INVALID_DATASN_RCVD:
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
- "BM_%d : CQ Error notification for cmd.. "
- "code %d cid 0x%x\n", code, cid);
+ "BM_%d : Cmd Notification %s[%d] on CID : %d\n",
+ cqe_desc[code], code, cid);
break;
case UNSOL_DATA_DIGEST_ERROR_NOTIFY:
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
- "BM_%d : Digest error on def pdu ring,"
- " dropping..\n");
+ "BM_%d : Dropping %s[%d] on DPDU ring on CID : %d\n",
+ cqe_desc[code], code, cid);
hwi_flush_default_pdu_buffer(phba, beiscsi_conn,
(struct i_t_dpdu_cqe *) sol);
break;
@@ -2017,6 +2121,8 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
case CXN_KILLED_INVALID_ITT_TTT_RCVD:
case CXN_KILLED_TIMED_OUT:
case CXN_KILLED_FIN_RCVD:
+ case CXN_KILLED_RST_SENT:
+ case CXN_KILLED_RST_RCVD:
case CXN_KILLED_BAD_UNSOL_PDU_RCVD:
case CXN_KILLED_BAD_WRB_INDEX_ERROR:
case CXN_KILLED_OVER_RUN_RESIDUAL:
@@ -2024,19 +2130,8 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
case CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN:
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
- "BM_%d : CQ Error %d, reset CID 0x%x...\n",
- code, cid);
- if (beiscsi_conn)
- iscsi_conn_failure(beiscsi_conn->conn,
- ISCSI_ERR_CONN_FAILED);
- break;
- case CXN_KILLED_RST_SENT:
- case CXN_KILLED_RST_RCVD:
- beiscsi_log(phba, KERN_ERR,
- BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
- "BM_%d : CQ Error %d, reset"
- "received/sent on CID 0x%x...\n",
- code, cid);
+ "BM_%d : Event %s[%d] received on CID : %d\n",
+ cqe_desc[code], code, cid);
if (beiscsi_conn)
iscsi_conn_failure(beiscsi_conn->conn,
ISCSI_ERR_CONN_FAILED);
@@ -2044,8 +2139,8 @@ static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
default:
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
- "BM_%d : CQ Error Invalid code= %d "
- "received on CID 0x%x...\n",
+ "BM_%d : Invalid CQE Event Received Code : %d"
+ "CID 0x%x...\n",
code, cid);
break;
}
@@ -2068,30 +2163,30 @@ void beiscsi_process_all_cqs(struct work_struct *work)
unsigned long flags;
struct hwi_controller *phwi_ctrlr;
struct hwi_context_memory *phwi_context;
- struct be_eq_obj *pbe_eq;
- struct beiscsi_hba *phba =
- container_of(work, struct beiscsi_hba, work_cqs);
+ struct beiscsi_hba *phba;
+ struct be_eq_obj *pbe_eq =
+ container_of(work, struct be_eq_obj, work_cqs);
+ phba = pbe_eq->phba;
phwi_ctrlr = phba->phwi_ctrlr;
phwi_context = phwi_ctrlr->phwi_ctxt;
- if (phba->msix_enabled)
- pbe_eq = &phwi_context->be_eq[phba->num_cpus];
- else
- pbe_eq = &phwi_context->be_eq[0];
- if (phba->todo_mcc_cq) {
+ if (pbe_eq->todo_mcc_cq) {
spin_lock_irqsave(&phba->isr_lock, flags);
- phba->todo_mcc_cq = 0;
+ pbe_eq->todo_mcc_cq = false;
spin_unlock_irqrestore(&phba->isr_lock, flags);
beiscsi_process_mcc_isr(phba);
}
- if (phba->todo_cq) {
+ if (pbe_eq->todo_cq) {
spin_lock_irqsave(&phba->isr_lock, flags);
- phba->todo_cq = 0;
+ pbe_eq->todo_cq = false;
spin_unlock_irqrestore(&phba->isr_lock, flags);
beiscsi_process_cq(pbe_eq);
}
+
+ /* rearm EQ for further interrupts */
+ hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1);
}
static int be_iopoll(struct blk_iopoll *iop, int budget)
@@ -2115,6 +2210,101 @@ static int be_iopoll(struct blk_iopoll *iop, int budget)
}
static void
+hwi_write_sgl_v2(struct iscsi_wrb *pwrb, struct scatterlist *sg,
+ unsigned int num_sg, struct beiscsi_io_task *io_task)
+{
+ struct iscsi_sge *psgl;
+ unsigned int sg_len, index;
+ unsigned int sge_len = 0;
+ unsigned long long addr;
+ struct scatterlist *l_sg;
+ unsigned int offset;
+
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, iscsi_bhs_addr_lo, pwrb,
+ io_task->bhs_pa.u.a32.address_lo);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, iscsi_bhs_addr_hi, pwrb,
+ io_task->bhs_pa.u.a32.address_hi);
+
+ l_sg = sg;
+ for (index = 0; (index < num_sg) && (index < 2); index++,
+ sg = sg_next(sg)) {
+ if (index == 0) {
+ sg_len = sg_dma_len(sg);
+ addr = (u64) sg_dma_address(sg);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
+ sge0_addr_lo, pwrb,
+ lower_32_bits(addr));
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
+ sge0_addr_hi, pwrb,
+ upper_32_bits(addr));
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
+ sge0_len, pwrb,
+ sg_len);
+ sge_len = sg_len;
+ } else {
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge1_r2t_offset,
+ pwrb, sge_len);
+ sg_len = sg_dma_len(sg);
+ addr = (u64) sg_dma_address(sg);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
+ sge1_addr_lo, pwrb,
+ lower_32_bits(addr));
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
+ sge1_addr_hi, pwrb,
+ upper_32_bits(addr));
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
+ sge1_len, pwrb,
+ sg_len);
+ }
+ }
+ psgl = (struct iscsi_sge *)io_task->psgl_handle->pfrag;
+ memset(psgl, 0, sizeof(*psgl) * BE2_SGE);
+
+ AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, io_task->bhs_len - 2);
+
+ AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl,
+ io_task->bhs_pa.u.a32.address_hi);
+ AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl,
+ io_task->bhs_pa.u.a32.address_lo);
+
+ if (num_sg == 1) {
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge0_last, pwrb,
+ 1);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge1_last, pwrb,
+ 0);
+ } else if (num_sg == 2) {
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge0_last, pwrb,
+ 0);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge1_last, pwrb,
+ 1);
+ } else {
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge0_last, pwrb,
+ 0);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge1_last, pwrb,
+ 0);
+ }
+
+ sg = l_sg;
+ psgl++;
+ psgl++;
+ offset = 0;
+ for (index = 0; index < num_sg; index++, sg = sg_next(sg), psgl++) {
+ sg_len = sg_dma_len(sg);
+ addr = (u64) sg_dma_address(sg);
+ AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl,
+ lower_32_bits(addr));
+ AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl,
+ upper_32_bits(addr));
+ AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, sg_len);
+ AMAP_SET_BITS(struct amap_iscsi_sge, sge_offset, psgl, offset);
+ AMAP_SET_BITS(struct amap_iscsi_sge, last_sge, psgl, 0);
+ offset += sg_len;
+ }
+ psgl--;
+ AMAP_SET_BITS(struct amap_iscsi_sge, last_sge, psgl, 1);
+}
+
+static void
hwi_write_sgl(struct iscsi_wrb *pwrb, struct scatterlist *sg,
unsigned int num_sg, struct beiscsi_io_task *io_task)
{
@@ -2202,13 +2392,18 @@ hwi_write_sgl(struct iscsi_wrb *pwrb, struct scatterlist *sg,
AMAP_SET_BITS(struct amap_iscsi_sge, last_sge, psgl, 1);
}
+/**
+ * hwi_write_buffer()- Populate the WRB with task info
+ * @pwrb: ptr to the WRB entry
+ * @task: iscsi task which is to be executed
+ **/
static void hwi_write_buffer(struct iscsi_wrb *pwrb, struct iscsi_task *task)
{
struct iscsi_sge *psgl;
- unsigned long long addr;
struct beiscsi_io_task *io_task = task->dd_data;
struct beiscsi_conn *beiscsi_conn = io_task->conn;
struct beiscsi_hba *phba = beiscsi_conn->phba;
+ uint8_t dsp_value = 0;
io_task->bhs_len = sizeof(struct be_nonio_bhs) - 2;
AMAP_SET_BITS(struct amap_iscsi_wrb, iscsi_bhs_addr_lo, pwrb,
@@ -2217,26 +2412,38 @@ static void hwi_write_buffer(struct iscsi_wrb *pwrb, struct iscsi_task *task)
io_task->bhs_pa.u.a32.address_hi);
if (task->data) {
- if (task->data_count) {
- AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1);
- addr = (u64) pci_map_single(phba->pcidev,
- task->data,
- task->data_count, 1);
- } else {
- AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0);
- addr = 0;
- }
+
+ /* Check for the data_count */
+ dsp_value = (task->data_count) ? 1 : 0;
+
+ if (chip_skh_r(phba->pcidev))
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, dsp,
+ pwrb, dsp_value);
+ else
+ AMAP_SET_BITS(struct amap_iscsi_wrb, dsp,
+ pwrb, dsp_value);
+
+ /* Map addr only if there is data_count */
+ if (dsp_value) {
+ io_task->mtask_addr = pci_map_single(phba->pcidev,
+ task->data,
+ task->data_count,
+ PCI_DMA_TODEVICE);
+ io_task->mtask_data_count = task->data_count;
+ } else
+ io_task->mtask_addr = 0;
+
AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_addr_lo, pwrb,
- ((u32)(addr & 0xFFFFFFFF)));
+ lower_32_bits(io_task->mtask_addr));
AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_addr_hi, pwrb,
- ((u32)(addr >> 32)));
+ upper_32_bits(io_task->mtask_addr));
AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_len, pwrb,
task->data_count);
AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_last, pwrb, 1);
} else {
AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0);
- addr = 0;
+ io_task->mtask_addr = 0;
}
psgl = (struct iscsi_sge *)io_task->psgl_handle->pfrag;
@@ -2259,9 +2466,9 @@ static void hwi_write_buffer(struct iscsi_wrb *pwrb, struct iscsi_task *task)
psgl++;
if (task->data) {
AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl,
- ((u32)(addr & 0xFFFFFFFF)));
+ lower_32_bits(io_task->mtask_addr));
AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl,
- ((u32)(addr >> 32)));
+ upper_32_bits(io_task->mtask_addr));
}
AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, 0x106);
}
@@ -2843,7 +3050,7 @@ static int beiscsi_create_eqs(struct beiscsi_hba *phba,
}
return 0;
create_eq_error:
- for (i = 0; i < (phba->num_cpus + 1); i++) {
+ for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) {
eq = &phwi_context->be_eq[i].q;
mem = &eq->dma_mem;
if (mem->va)
@@ -3268,15 +3475,31 @@ err:
return -ENOMEM;
}
-static int find_num_cpus(void)
+/**
+ * find_num_cpus()- Get the CPU online count
+ * @phba: ptr to priv structure
+ *
+ * CPU count is used for creating EQ.
+ **/
+static void find_num_cpus(struct beiscsi_hba *phba)
{
int num_cpus = 0;
num_cpus = num_online_cpus();
- if (num_cpus >= MAX_CPUS)
- num_cpus = MAX_CPUS - 1;
- return num_cpus;
+ switch (phba->generation) {
+ case BE_GEN2:
+ case BE_GEN3:
+ phba->num_cpus = (num_cpus > BEISCSI_MAX_NUM_CPUS) ?
+ BEISCSI_MAX_NUM_CPUS : num_cpus;
+ break;
+ case BE_GEN4:
+ phba->num_cpus = (num_cpus > OC_SKH_MAX_NUM_CPUS) ?
+ OC_SKH_MAX_NUM_CPUS : num_cpus;
+ break;
+ default:
+ phba->num_cpus = 1;
+ }
}
static int hwi_init_port(struct beiscsi_hba *phba)
@@ -3644,12 +3867,9 @@ static void hwi_disable_intr(struct beiscsi_hba *phba)
static int beiscsi_get_boot_info(struct beiscsi_hba *phba)
{
struct be_cmd_get_session_resp *session_resp;
- struct be_mcc_wrb *wrb;
struct be_dma_mem nonemb_cmd;
- unsigned int tag, wrb_num;
- unsigned short status, extd_status;
+ unsigned int tag;
unsigned int s_handle;
- struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
int ret = -ENOMEM;
/* Get the session handle of the boot target */
@@ -3682,25 +3902,16 @@ static int beiscsi_get_boot_info(struct beiscsi_hba *phba)
" Failed\n");
goto boot_freemem;
- } else
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
+ }
- wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
- extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
- status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
- if (status || extd_status) {
+ ret = beiscsi_mccq_compl(phba, tag, NULL, nonemb_cmd.va);
+ if (ret) {
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
- "BM_%d : beiscsi_get_session_info Failed"
- " status = %d extd_status = %d\n",
- status, extd_status);
-
- free_mcc_tag(&phba->ctrl, tag);
+ "BM_%d : beiscsi_get_session_info Failed");
goto boot_freemem;
}
- wrb = queue_get_wrb(mccq, wrb_num);
- free_mcc_tag(&phba->ctrl, tag);
+
session_resp = nonemb_cmd.va ;
memcpy(&phba->boot_sess, &session_resp->session_info,
@@ -3853,6 +4064,11 @@ static void beiscsi_clean_port(struct beiscsi_hba *phba)
kfree(phba->ep_array);
}
+/**
+ * beiscsi_cleanup_task()- Free driver resources of the task
+ * @task: ptr to the iscsi task
+ *
+ **/
static void beiscsi_cleanup_task(struct iscsi_task *task)
{
struct beiscsi_io_task *io_task = task->dd_data;
@@ -3900,6 +4116,13 @@ static void beiscsi_cleanup_task(struct iscsi_task *task)
spin_unlock(&phba->mgmt_sgl_lock);
io_task->psgl_handle = NULL;
}
+ if (io_task->mtask_addr) {
+ pci_unmap_single(phba->pcidev,
+ io_task->mtask_addr,
+ io_task->mtask_data_count,
+ PCI_DMA_TODEVICE);
+ io_task->mtask_addr = 0;
+ }
}
}
}
@@ -3909,8 +4132,6 @@ beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_offload_params *params)
{
struct wrb_handle *pwrb_handle;
- struct iscsi_target_context_update_wrb *pwrb = NULL;
- struct be_mem_descriptor *mem_descr;
struct beiscsi_hba *phba = beiscsi_conn->phba;
struct iscsi_task *task = beiscsi_conn->task;
struct iscsi_session *session = task->conn->session;
@@ -3927,67 +4148,16 @@ beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn,
pwrb_handle = alloc_wrb_handle(phba, (beiscsi_conn->beiscsi_conn_cid -
phba->fw_config.iscsi_cid_start));
- pwrb = (struct iscsi_target_context_update_wrb *)pwrb_handle->pwrb;
- memset(pwrb, 0, sizeof(*pwrb));
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
- max_burst_length, pwrb, params->dw[offsetof
- (struct amap_beiscsi_offload_params,
- max_burst_length) / 32]);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
- max_send_data_segment_length, pwrb,
- params->dw[offsetof(struct amap_beiscsi_offload_params,
- max_send_data_segment_length) / 32]);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
- first_burst_length,
- pwrb,
- params->dw[offsetof(struct amap_beiscsi_offload_params,
- first_burst_length) / 32]);
-
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, erl, pwrb,
- (params->dw[offsetof(struct amap_beiscsi_offload_params,
- erl) / 32] & OFFLD_PARAMS_ERL));
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, dde, pwrb,
- (params->dw[offsetof(struct amap_beiscsi_offload_params,
- dde) / 32] & OFFLD_PARAMS_DDE) >> 2);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, hde, pwrb,
- (params->dw[offsetof(struct amap_beiscsi_offload_params,
- hde) / 32] & OFFLD_PARAMS_HDE) >> 3);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, ir2t, pwrb,
- (params->dw[offsetof(struct amap_beiscsi_offload_params,
- ir2t) / 32] & OFFLD_PARAMS_IR2T) >> 4);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, imd, pwrb,
- (params->dw[offsetof(struct amap_beiscsi_offload_params,
- imd) / 32] & OFFLD_PARAMS_IMD) >> 5);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, stat_sn,
- pwrb,
- (params->dw[offsetof(struct amap_beiscsi_offload_params,
- exp_statsn) / 32] + 1));
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, type, pwrb,
- 0x7);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, wrb_idx,
- pwrb, pwrb_handle->wrb_index);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, ptr2nextwrb,
- pwrb, pwrb_handle->nxt_wrb_index);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
- session_state, pwrb, 0);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, compltonack,
- pwrb, 1);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, notpredblq,
- pwrb, 0);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, mode, pwrb,
- 0);
- mem_descr = phba->init_mem;
- mem_descr += ISCSI_MEM_GLOBAL_HEADER;
-
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
- pad_buffer_addr_hi, pwrb,
- mem_descr->mem_array[0].bus_address.u.a32.address_hi);
- AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
- pad_buffer_addr_lo, pwrb,
- mem_descr->mem_array[0].bus_address.u.a32.address_lo);
+ /* Check for the adapter family */
+ if (chip_skh_r(phba->pcidev))
+ beiscsi_offload_cxn_v2(params, pwrb_handle);
+ else
+ beiscsi_offload_cxn_v0(params, pwrb_handle,
+ phba->init_mem);
- be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_target_context_update_wrb));
+ be_dws_le_to_cpu(pwrb_handle->pwrb,
+ sizeof(struct iscsi_target_context_update_wrb));
doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK;
doorbell |= (pwrb_handle->wrb_index & DB_DEF_PDU_WRB_INDEX_MASK)
@@ -4044,13 +4214,25 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
spin_lock(&phba->io_sgl_lock);
io_task->psgl_handle = alloc_io_sgl_handle(phba);
spin_unlock(&phba->io_sgl_lock);
- if (!io_task->psgl_handle)
+ if (!io_task->psgl_handle) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
+ "BM_%d : Alloc of IO_SGL_ICD Failed"
+ "for the CID : %d\n",
+ beiscsi_conn->beiscsi_conn_cid);
goto free_hndls;
+ }
io_task->pwrb_handle = alloc_wrb_handle(phba,
beiscsi_conn->beiscsi_conn_cid -
phba->fw_config.iscsi_cid_start);
- if (!io_task->pwrb_handle)
+ if (!io_task->pwrb_handle) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
+ "BM_%d : Alloc of WRB_HANDLE Failed"
+ "for the CID : %d\n",
+ beiscsi_conn->beiscsi_conn_cid);
goto free_io_hndls;
+ }
} else {
io_task->scsi_cmnd = NULL;
if ((opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGIN) {
@@ -4059,8 +4241,16 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
io_task->psgl_handle = (struct sgl_handle *)
alloc_mgmt_sgl_handle(phba);
spin_unlock(&phba->mgmt_sgl_lock);
- if (!io_task->psgl_handle)
+ if (!io_task->psgl_handle) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_IO |
+ BEISCSI_LOG_CONFIG,
+ "BM_%d : Alloc of MGMT_SGL_ICD Failed"
+ "for the CID : %d\n",
+ beiscsi_conn->
+ beiscsi_conn_cid);
goto free_hndls;
+ }
beiscsi_conn->login_in_progress = 1;
beiscsi_conn->plogin_sgl_handle =
@@ -4069,8 +4259,16 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
alloc_wrb_handle(phba,
beiscsi_conn->beiscsi_conn_cid -
phba->fw_config.iscsi_cid_start);
- if (!io_task->pwrb_handle)
- goto free_io_hndls;
+ if (!io_task->pwrb_handle) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_IO |
+ BEISCSI_LOG_CONFIG,
+ "BM_%d : Alloc of WRB_HANDLE Failed"
+ "for the CID : %d\n",
+ beiscsi_conn->
+ beiscsi_conn_cid);
+ goto free_mgmt_hndls;
+ }
beiscsi_conn->plogin_wrb_handle =
io_task->pwrb_handle;
@@ -4085,14 +4283,28 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
spin_lock(&phba->mgmt_sgl_lock);
io_task->psgl_handle = alloc_mgmt_sgl_handle(phba);
spin_unlock(&phba->mgmt_sgl_lock);
- if (!io_task->psgl_handle)
+ if (!io_task->psgl_handle) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_IO |
+ BEISCSI_LOG_CONFIG,
+ "BM_%d : Alloc of MGMT_SGL_ICD Failed"
+ "for the CID : %d\n",
+ beiscsi_conn->
+ beiscsi_conn_cid);
goto free_hndls;
+ }
io_task->pwrb_handle =
alloc_wrb_handle(phba,
beiscsi_conn->beiscsi_conn_cid -
phba->fw_config.iscsi_cid_start);
- if (!io_task->pwrb_handle)
+ if (!io_task->pwrb_handle) {
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
+ "BM_%d : Alloc of WRB_HANDLE Failed"
+ "for the CID : %d\n",
+ beiscsi_conn->beiscsi_conn_cid);
goto free_mgmt_hndls;
+ }
}
}
@@ -4124,11 +4336,64 @@ free_hndls:
pci_pool_free(beiscsi_sess->bhs_pool, io_task->cmd_bhs,
io_task->bhs_pa.u.a64.address);
io_task->cmd_bhs = NULL;
- beiscsi_log(phba, KERN_ERR,
- BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
- "BM_%d : Alloc of SGL_ICD Failed\n");
return -ENOMEM;
}
+int beiscsi_iotask_v2(struct iscsi_task *task, struct scatterlist *sg,
+ unsigned int num_sg, unsigned int xferlen,
+ unsigned int writedir)
+{
+
+ struct beiscsi_io_task *io_task = task->dd_data;
+ struct iscsi_conn *conn = task->conn;
+ struct beiscsi_conn *beiscsi_conn = conn->dd_data;
+ struct beiscsi_hba *phba = beiscsi_conn->phba;
+ struct iscsi_wrb *pwrb = NULL;
+ unsigned int doorbell = 0;
+
+ pwrb = io_task->pwrb_handle->pwrb;
+ memset(pwrb, 0, sizeof(*pwrb));
+
+ io_task->cmd_bhs->iscsi_hdr.exp_statsn = 0;
+ io_task->bhs_len = sizeof(struct be_cmd_bhs);
+
+ if (writedir) {
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, type, pwrb,
+ INI_WR_CMD);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, dsp, pwrb, 1);
+ } else {
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, type, pwrb,
+ INI_RD_CMD);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, dsp, pwrb, 0);
+ }
+
+ io_task->wrb_type = AMAP_GET_BITS(struct amap_iscsi_wrb_v2,
+ type, pwrb);
+
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, lun, pwrb,
+ cpu_to_be16(*(unsigned short *)
+ &io_task->cmd_bhs->iscsi_hdr.lun));
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, r2t_exp_dtl, pwrb, xferlen);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, wrb_idx, pwrb,
+ io_task->pwrb_handle->wrb_index);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, cmdsn_itt, pwrb,
+ be32_to_cpu(task->cmdsn));
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sgl_idx, pwrb,
+ io_task->psgl_handle->sgl_index);
+
+ hwi_write_sgl_v2(pwrb, sg, num_sg, io_task);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, ptr2nextwrb, pwrb,
+ io_task->pwrb_handle->nxt_wrb_index);
+
+ be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb));
+
+ doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK;
+ doorbell |= (io_task->pwrb_handle->wrb_index &
+ DB_DEF_PDU_WRB_INDEX_MASK) <<
+ DB_DEF_PDU_WRB_INDEX_SHIFT;
+ doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT;
+ iowrite32(doorbell, phba->db_va + DB_TXULP0_OFFSET);
+ return 0;
+}
static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg,
unsigned int num_sg, unsigned int xferlen,
@@ -4156,6 +4421,9 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg,
AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0);
}
+ io_task->wrb_type = AMAP_GET_BITS(struct amap_iscsi_wrb,
+ type, pwrb);
+
AMAP_SET_BITS(struct amap_iscsi_wrb, lun, pwrb,
cpu_to_be16(*(unsigned short *)
&io_task->cmd_bhs->iscsi_hdr.lun));
@@ -4191,55 +4459,75 @@ static int beiscsi_mtask(struct iscsi_task *task)
struct iscsi_wrb *pwrb = NULL;
unsigned int doorbell = 0;
unsigned int cid;
+ unsigned int pwrb_typeoffset = 0;
cid = beiscsi_conn->beiscsi_conn_cid;
pwrb = io_task->pwrb_handle->pwrb;
memset(pwrb, 0, sizeof(*pwrb));
- AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb,
- be32_to_cpu(task->cmdsn));
- AMAP_SET_BITS(struct amap_iscsi_wrb, wrb_idx, pwrb,
- io_task->pwrb_handle->wrb_index);
- AMAP_SET_BITS(struct amap_iscsi_wrb, sgl_icd_idx, pwrb,
- io_task->psgl_handle->sgl_index);
+
+ if (chip_skh_r(phba->pcidev)) {
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, cmdsn_itt, pwrb,
+ be32_to_cpu(task->cmdsn));
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, wrb_idx, pwrb,
+ io_task->pwrb_handle->wrb_index);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sgl_idx, pwrb,
+ io_task->psgl_handle->sgl_index);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, r2t_exp_dtl, pwrb,
+ task->data_count);
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2, ptr2nextwrb, pwrb,
+ io_task->pwrb_handle->nxt_wrb_index);
+ pwrb_typeoffset = SKH_WRB_TYPE_OFFSET;
+ } else {
+ AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb,
+ be32_to_cpu(task->cmdsn));
+ AMAP_SET_BITS(struct amap_iscsi_wrb, wrb_idx, pwrb,
+ io_task->pwrb_handle->wrb_index);
+ AMAP_SET_BITS(struct amap_iscsi_wrb, sgl_icd_idx, pwrb,
+ io_task->psgl_handle->sgl_index);
+ AMAP_SET_BITS(struct amap_iscsi_wrb, r2t_exp_dtl, pwrb,
+ task->data_count);
+ AMAP_SET_BITS(struct amap_iscsi_wrb, ptr2nextwrb, pwrb,
+ io_task->pwrb_handle->nxt_wrb_index);
+ pwrb_typeoffset = BE_WRB_TYPE_OFFSET;
+ }
+
switch (task->hdr->opcode & ISCSI_OPCODE_MASK) {
case ISCSI_OP_LOGIN:
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
- TGT_DM_CMD);
- AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb, 1);
+ ADAPTER_SET_WRB_TYPE(pwrb, TGT_DM_CMD, pwrb_typeoffset);
hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_NOOP_OUT:
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, 1);
+ ADAPTER_SET_WRB_TYPE(pwrb, TGT_DM_CMD, pwrb_typeoffset);
+ if (chip_skh_r(phba->pcidev))
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
+ dmsg, pwrb, 1);
+ else
+ AMAP_SET_BITS(struct amap_iscsi_wrb,
+ dmsg, pwrb, 1);
} else {
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
- INI_RD_CMD);
- AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
+ ADAPTER_SET_WRB_TYPE(pwrb, INI_RD_CMD, pwrb_typeoffset);
+ if (chip_skh_r(phba->pcidev))
+ AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
+ dmsg, pwrb, 0);
+ else
+ AMAP_SET_BITS(struct amap_iscsi_wrb,
+ dmsg, pwrb, 0);
}
hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_TEXT:
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
- TGT_DM_CMD);
- AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
+ ADAPTER_SET_WRB_TYPE(pwrb, TGT_DM_CMD, pwrb_typeoffset);
hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_SCSI_TMFUNC:
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
- INI_TMF_CMD);
- AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
+ ADAPTER_SET_WRB_TYPE(pwrb, INI_TMF_CMD, pwrb_typeoffset);
hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_LOGOUT:
- AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
- HWH_TYPE_LOGOUT);
+ ADAPTER_SET_WRB_TYPE(pwrb, HWH_TYPE_LOGOUT, pwrb_typeoffset);
hwi_write_buffer(pwrb, task);
break;
@@ -4251,11 +4539,10 @@ static int beiscsi_mtask(struct iscsi_task *task)
return -EINVAL;
}
- AMAP_SET_BITS(struct amap_iscsi_wrb, r2t_exp_dtl, pwrb,
- task->data_count);
- AMAP_SET_BITS(struct amap_iscsi_wrb, ptr2nextwrb, pwrb,
- io_task->pwrb_handle->nxt_wrb_index);
- be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb));
+ /* Set the task type */
+ io_task->wrb_type = (chip_skh_r(phba->pcidev)) ?
+ AMAP_GET_BITS(struct amap_iscsi_wrb_v2, type, pwrb) :
+ AMAP_GET_BITS(struct amap_iscsi_wrb, type, pwrb);
doorbell |= cid & DB_WRB_POST_CID_MASK;
doorbell |= (io_task->pwrb_handle->wrb_index &
@@ -4269,10 +4556,13 @@ static int beiscsi_task_xmit(struct iscsi_task *task)
{
struct beiscsi_io_task *io_task = task->dd_data;
struct scsi_cmnd *sc = task->sc;
+ struct beiscsi_hba *phba = NULL;
struct scatterlist *sg;
int num_sg;
unsigned int writedir = 0, xferlen = 0;
+ phba = ((struct beiscsi_conn *)task->conn->dd_data)->phba;
+
if (!sc)
return beiscsi_mtask(task);
@@ -4295,7 +4585,7 @@ static int beiscsi_task_xmit(struct iscsi_task *task)
else
writedir = 0;
- return beiscsi_iotask(task, sg, num_sg, xferlen, writedir);
+ return phba->iotask_fn(task, sg, num_sg, xferlen, writedir);
}
/**
@@ -4326,20 +4616,24 @@ static int beiscsi_bsg_request(struct bsg_job *job)
beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
"BM_%d : Failed to allocate memory for "
"beiscsi_bsg_request\n");
- return -EIO;
+ return -ENOMEM;
}
tag = mgmt_vendor_specific_fw_cmd(&phba->ctrl, phba, job,
&nonemb_cmd);
if (!tag) {
beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
- "BM_%d : be_cmd_get_mac_addr Failed\n");
+ "BM_%d : MBX Tag Allocation Failed\n");
pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
return -EAGAIN;
- } else
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
+ }
+
+ rc = wait_event_interruptible_timeout(
+ phba->ctrl.mcc_wait[tag],
+ phba->ctrl.mcc_numtag[tag],
+ msecs_to_jiffies(
+ BEISCSI_HOST_MBX_TIMEOUT));
extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
free_mcc_tag(&phba->ctrl, tag);
@@ -4356,11 +4650,13 @@ static int beiscsi_bsg_request(struct bsg_job *job)
nonemb_cmd.va, nonemb_cmd.dma);
if (status || extd_status) {
beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
- "BM_%d : be_cmd_get_mac_addr Failed"
+ "BM_%d : MBX Cmd Failed"
" status = %d extd_status = %d\n",
status, extd_status);
return -EIO;
+ } else {
+ rc = 0;
}
break;
@@ -4380,14 +4676,18 @@ void beiscsi_hba_attrs_init(struct beiscsi_hba *phba)
beiscsi_log_enable_init(phba, beiscsi_log_enable);
}
+/*
+ * beiscsi_quiesce()- Cleanup Driver resources
+ * @phba: Instance Priv structure
+ *
+ * Free the OS and HW resources held by the driver
+ **/
static void beiscsi_quiesce(struct beiscsi_hba *phba)
{
struct hwi_controller *phwi_ctrlr;
struct hwi_context_memory *phwi_context;
struct be_eq_obj *pbe_eq;
unsigned int i, msix_vec;
- u8 *real_offset = 0;
- u32 value = 0;
phwi_ctrlr = phba->phwi_ctrlr;
phwi_context = phwi_ctrlr->phwi_ctxt;
@@ -4411,19 +4711,14 @@ static void beiscsi_quiesce(struct beiscsi_hba *phba)
beiscsi_clean_port(phba);
beiscsi_free_mem(phba);
- real_offset = (u8 *)phba->csr_va + MPU_EP_SEMAPHORE;
- value = readl((void *)real_offset);
-
- if (value & 0x00010000) {
- value &= 0xfffeffff;
- writel(value, (void *)real_offset);
- }
beiscsi_unmap_pci_function(phba);
pci_free_consistent(phba->pcidev,
phba->ctrl.mbox_mem_alloced.size,
phba->ctrl.mbox_mem_alloced.va,
phba->ctrl.mbox_mem_alloced.dma);
+
+ cancel_delayed_work_sync(&phba->beiscsi_hw_check_task);
}
static void beiscsi_remove(struct pci_dev *pcidev)
@@ -4476,6 +4771,25 @@ static void beiscsi_msix_enable(struct beiscsi_hba *phba)
return;
}
+/*
+ * beiscsi_hw_health_check()- Check adapter health
+ * @work: work item to check HW health
+ *
+ * Check if adapter in an unrecoverable state or not.
+ **/
+static void
+beiscsi_hw_health_check(struct work_struct *work)
+{
+ struct beiscsi_hba *phba =
+ container_of(work, struct beiscsi_hba,
+ beiscsi_hw_check_task.work);
+
+ beiscsi_ue_detect(phba);
+
+ schedule_delayed_work(&phba->beiscsi_hw_check_task,
+ msecs_to_jiffies(1000));
+}
+
static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
const struct pci_device_id *id)
{
@@ -4483,9 +4797,7 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
struct hwi_controller *phwi_ctrlr;
struct hwi_context_memory *phwi_context;
struct be_eq_obj *pbe_eq;
- int ret, num_cpus, i;
- u8 *real_offset = 0;
- u32 value = 0;
+ int ret, i;
ret = beiscsi_enable_pci(pcidev);
if (ret < 0) {
@@ -4504,25 +4816,33 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
/* Initialize Driver configuration Paramters */
beiscsi_hba_attrs_init(phba);
+ phba->fw_timeout = false;
+
+
switch (pcidev->device) {
case BE_DEVICE_ID1:
case OC_DEVICE_ID1:
case OC_DEVICE_ID2:
phba->generation = BE_GEN2;
+ phba->iotask_fn = beiscsi_iotask;
break;
case BE_DEVICE_ID2:
case OC_DEVICE_ID3:
phba->generation = BE_GEN3;
+ phba->iotask_fn = beiscsi_iotask;
break;
+ case OC_SKH_ID1:
+ phba->generation = BE_GEN4;
+ phba->iotask_fn = beiscsi_iotask_v2;
default:
phba->generation = 0;
}
if (enable_msix)
- num_cpus = find_num_cpus();
+ find_num_cpus(phba);
else
- num_cpus = 1;
- phba->num_cpus = num_cpus;
+ phba->num_cpus = 1;
+
beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
"BM_%d : num_cpus = %d\n",
phba->num_cpus);
@@ -4540,31 +4860,18 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
goto hba_free;
}
- if (!num_hba) {
- real_offset = (u8 *)phba->csr_va + MPU_EP_SEMAPHORE;
- value = readl((void *)real_offset);
- if (value & 0x00010000) {
- gcrashmode++;
- beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
- "BM_%d : Loading Driver in crashdump mode\n");
- ret = beiscsi_cmd_reset_function(phba);
- if (ret) {
- beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
- "BM_%d : Reset Failed. Aborting Crashdump\n");
- goto hba_free;
- }
- ret = be_chk_reset_complete(phba);
- if (ret) {
- beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
- "BM_%d : Failed to get out of reset."
- "Aborting Crashdump\n");
- goto hba_free;
- }
- } else {
- value |= 0x00010000;
- writel(value, (void *)real_offset);
- num_hba++;
- }
+ ret = beiscsi_cmd_reset_function(phba);
+ if (ret) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : Reset Failed. Aborting Crashdump\n");
+ goto hba_free;
+ }
+ ret = be_chk_reset_complete(phba);
+ if (ret) {
+ beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
+ "BM_%d : Failed to get out of reset."
+ "Aborting Crashdump\n");
+ goto hba_free;
}
spin_lock_init(&phba->io_sgl_lock);
@@ -4596,7 +4903,7 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
phba->ctrl.mcc_alloc_index = phba->ctrl.mcc_free_index = 0;
- snprintf(phba->wq_name, sizeof(phba->wq_name), "beiscsi_q_irq%u",
+ snprintf(phba->wq_name, sizeof(phba->wq_name), "beiscsi_%02x_wq",
phba->shost->host_no);
phba->wq = alloc_workqueue(phba->wq_name, WQ_MEM_RECLAIM, 1);
if (!phba->wq) {
@@ -4606,10 +4913,12 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
goto free_twq;
}
- INIT_WORK(&phba->work_cqs, beiscsi_process_all_cqs);
+ INIT_DELAYED_WORK(&phba->beiscsi_hw_check_task,
+ beiscsi_hw_health_check);
phwi_ctrlr = phba->phwi_ctrlr;
phwi_context = phwi_ctrlr->phwi_ctxt;
+
if (blk_iopoll_enabled) {
for (i = 0; i < phba->num_cpus; i++) {
pbe_eq = &phwi_context->be_eq[i];
@@ -4617,7 +4926,25 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
be_iopoll);
blk_iopoll_enable(&pbe_eq->iopoll);
}
+
+ i = (phba->msix_enabled) ? i : 0;
+ /* Work item for MCC handling */
+ pbe_eq = &phwi_context->be_eq[i];
+ INIT_WORK(&pbe_eq->work_cqs, beiscsi_process_all_cqs);
+ } else {
+ if (phba->msix_enabled) {
+ for (i = 0; i <= phba->num_cpus; i++) {
+ pbe_eq = &phwi_context->be_eq[i];
+ INIT_WORK(&pbe_eq->work_cqs,
+ beiscsi_process_all_cqs);
+ }
+ } else {
+ pbe_eq = &phwi_context->be_eq[0];
+ INIT_WORK(&pbe_eq->work_cqs,
+ beiscsi_process_all_cqs);
+ }
}
+
ret = beiscsi_init_irqs(phba);
if (ret < 0) {
beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
@@ -4637,6 +4964,9 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
"iSCSI boot info.\n");
beiscsi_create_def_ifaces(phba);
+ schedule_delayed_work(&phba->beiscsi_hw_check_task,
+ msecs_to_jiffies(1000));
+
beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
"\n\n\n BM_%d : SUCCESS - DRIVER LOADED\n\n\n");
return 0;
@@ -4652,15 +4982,6 @@ free_twq:
beiscsi_clean_port(phba);
beiscsi_free_mem(phba);
free_port:
- real_offset = (u8 *)phba->csr_va + MPU_EP_SEMAPHORE;
-
- value = readl((void *)real_offset);
-
- if (value & 0x00010000) {
- value &= 0xfffeffff;
- writel(value, (void *)real_offset);
- }
-
pci_free_consistent(phba->pcidev,
phba->ctrl.mbox_mem_alloced.size,
phba->ctrl.mbox_mem_alloced.va,
diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h
index b8912263ef4..5946577d79d 100644
--- a/drivers/scsi/be2iscsi/be_main.h
+++ b/drivers/scsi/be2iscsi/be_main.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2011 Emulex
+ * Copyright (C) 2005 - 2012 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -36,12 +36,13 @@
#include "be.h"
#define DRV_NAME "be2iscsi"
-#define BUILD_STR "4.4.58.0"
+#define BUILD_STR "10.0.272.0"
#define BE_NAME "Emulex OneConnect" \
"Open-iSCSI Driver version" BUILD_STR
#define DRV_DESC BE_NAME " " "Driver"
#define BE_VENDOR_ID 0x19A2
+#define ELX_VENDOR_ID 0x10DF
/* DEVICE ID's for BE2 */
#define BE_DEVICE_ID1 0x212
#define OC_DEVICE_ID1 0x702
@@ -51,6 +52,9 @@
#define BE_DEVICE_ID2 0x222
#define OC_DEVICE_ID3 0x712
+/* DEVICE ID for SKH */
+#define OC_SKH_ID1 0x722
+
#define BE2_IO_DEPTH 1024
#define BE2_MAX_SESSIONS 256
#define BE2_CMDS_PER_CXN 128
@@ -60,7 +64,11 @@
#define BE2_DEFPDU_HDR_SZ 64
#define BE2_DEFPDU_DATA_SZ 8192
-#define MAX_CPUS 31
+#define MAX_CPUS 64
+#define BEISCSI_MAX_NUM_CPUS 7
+#define OC_SKH_MAX_NUM_CPUS 63
+
+
#define BEISCSI_SGLIST_ELEMENTS 30
#define BEISCSI_CMD_PER_LUN 128 /* scsi_host->cmd_per_lun */
@@ -257,6 +265,7 @@ struct invalidate_command_table {
unsigned short cid;
} __packed;
+#define chip_skh_r(pdev) (pdev->device == OC_SKH_ID1)
struct beiscsi_hba {
struct hba_parameters params;
struct hwi_controller *phwi_ctrlr;
@@ -270,12 +279,11 @@ struct beiscsi_hba {
struct be_bus_address pci_pa; /* CSR */
/* PCI representation of our HBA */
struct pci_dev *pcidev;
- unsigned int state;
unsigned short asic_revision;
unsigned int num_cpus;
unsigned int nxt_cqid;
- struct msix_entry msix_entries[MAX_CPUS + 1];
- char *msi_name[MAX_CPUS + 1];
+ struct msix_entry msix_entries[MAX_CPUS];
+ char *msi_name[MAX_CPUS];
bool msix_enabled;
struct be_mem_descriptor *init_mem;
@@ -325,12 +333,14 @@ struct beiscsi_hba {
spinlock_t cid_lock;
} fw_config;
+ unsigned int state;
+ bool fw_timeout;
+ bool ue_detected;
+ struct delayed_work beiscsi_hw_check_task;
+
u8 mac_address[ETH_ALEN];
- unsigned short todo_cq;
- unsigned short todo_mcc_cq;
char wq_name[20];
struct workqueue_struct *wq; /* The actuak work queue */
- struct work_struct work_cqs; /* The work being queued */
struct be_ctrl_info ctrl;
unsigned int generation;
unsigned int interface_handle;
@@ -338,7 +348,10 @@ struct beiscsi_hba {
struct invalidate_command_table inv_tbl[128];
unsigned int attr_log_enable;
-
+ int (*iotask_fn)(struct iscsi_task *,
+ struct scatterlist *sg,
+ uint32_t num_sg, uint32_t xferlen,
+ uint32_t writedir);
};
struct beiscsi_session {
@@ -410,6 +423,9 @@ struct beiscsi_io_task {
struct be_cmd_bhs *cmd_bhs;
struct be_bus_address bhs_pa;
unsigned short bhs_len;
+ dma_addr_t mtask_addr;
+ uint32_t mtask_data_count;
+ uint8_t wrb_type;
};
struct be_nonio_bhs {
@@ -457,6 +473,9 @@ struct beiscsi_offload_params {
#define OFFLD_PARAMS_HDE 0x00000008
#define OFFLD_PARAMS_IR2T 0x00000010
#define OFFLD_PARAMS_IMD 0x00000020
+#define OFFLD_PARAMS_DATA_SEQ_INORDER 0x00000040
+#define OFFLD_PARAMS_PDU_SEQ_INORDER 0x00000080
+#define OFFLD_PARAMS_MAX_R2T 0x00FFFF00
/**
* Pseudo amap definition in which each bit of the actual structure is defined
@@ -471,7 +490,10 @@ struct amap_beiscsi_offload_params {
u8 hde[1];
u8 ir2t[1];
u8 imd[1];
- u8 pad[26];
+ u8 data_seq_inorder[1];
+ u8 pdu_seq_inorder[1];
+ u8 max_r2t[16];
+ u8 pad[8];
u8 exp_statsn[32];
};
@@ -569,6 +591,20 @@ struct amap_i_t_dpdu_cqe {
u8 valid;
} __packed;
+struct amap_i_t_dpdu_cqe_v2 {
+ u8 db_addr_hi[32]; /* DWORD 0 */
+ u8 db_addr_lo[32]; /* DWORD 1 */
+ u8 code[6]; /* DWORD 2 */
+ u8 num_cons; /* DWORD 2*/
+ u8 rsvd0[8]; /* DWORD 2 */
+ u8 dpl[17]; /* DWORD 2 */
+ u8 index[16]; /* DWORD 3 */
+ u8 cid[13]; /* DWORD 3 */
+ u8 rsvd1; /* DWORD 3 */
+ u8 final; /* DWORD 3 */
+ u8 valid; /* DWORD 3 */
+} __packed;
+
#define CQE_VALID_MASK 0x80000000
#define CQE_CODE_MASK 0x0000003F
#define CQE_CID_MASK 0x0000FFC0
@@ -617,6 +653,11 @@ struct iscsi_wrb {
} __packed;
#define WRB_TYPE_MASK 0xF0000000
+#define SKH_WRB_TYPE_OFFSET 27
+#define BE_WRB_TYPE_OFFSET 28
+
+#define ADAPTER_SET_WRB_TYPE(pwrb, wrb_type, type_offset) \
+ (pwrb->dw[0] |= (wrb_type << type_offset))
/**
* Pseudo amap definition in which each bit of the actual structure is defined
@@ -663,12 +704,57 @@ struct amap_iscsi_wrb {
} __packed;
+struct amap_iscsi_wrb_v2 {
+ u8 r2t_exp_dtl[25]; /* DWORD 0 */
+ u8 rsvd0[2]; /* DWORD 0*/
+ u8 type[5]; /* DWORD 0 */
+ u8 ptr2nextwrb[8]; /* DWORD 1 */
+ u8 wrb_idx[8]; /* DWORD 1 */
+ u8 lun[16]; /* DWORD 1 */
+ u8 sgl_idx[16]; /* DWORD 2 */
+ u8 ref_sgl_icd_idx[16]; /* DWORD 2 */
+ u8 exp_data_sn[32]; /* DWORD 3 */
+ u8 iscsi_bhs_addr_hi[32]; /* DWORD 4 */
+ u8 iscsi_bhs_addr_lo[32]; /* DWORD 5 */
+ u8 cq_id[16]; /* DWORD 6 */
+ u8 rsvd1[16]; /* DWORD 6 */
+ u8 cmdsn_itt[32]; /* DWORD 7 */
+ u8 sge0_addr_hi[32]; /* DWORD 8 */
+ u8 sge0_addr_lo[32]; /* DWORD 9 */
+ u8 sge0_offset[24]; /* DWORD 10 */
+ u8 rsvd2[7]; /* DWORD 10 */
+ u8 sge0_last; /* DWORD 10 */
+ u8 sge0_len[17]; /* DWORD 11 */
+ u8 rsvd3[7]; /* DWORD 11 */
+ u8 diff_enbl; /* DWORD 11 */
+ u8 u_run; /* DWORD 11 */
+ u8 o_run; /* DWORD 11 */
+ u8 invalid; /* DWORD 11 */
+ u8 dsp; /* DWORD 11 */
+ u8 dmsg; /* DWORD 11 */
+ u8 rsvd4; /* DWORD 11 */
+ u8 lt; /* DWORD 11 */
+ u8 sge1_addr_hi[32]; /* DWORD 12 */
+ u8 sge1_addr_lo[32]; /* DWORD 13 */
+ u8 sge1_r2t_offset[24]; /* DWORD 14 */
+ u8 rsvd5[7]; /* DWORD 14 */
+ u8 sge1_last; /* DWORD 14 */
+ u8 sge1_len[17]; /* DWORD 15 */
+ u8 rsvd6[15]; /* DWORD 15 */
+} __packed;
+
+
struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid);
void
free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle);
void beiscsi_process_all_cqs(struct work_struct *work);
+static inline bool beiscsi_error(struct beiscsi_hba *phba)
+{
+ return phba->ue_detected || phba->fw_timeout;
+}
+
struct pdu_nop_out {
u32 dw[12];
};
@@ -728,6 +814,7 @@ struct iscsi_target_context_update_wrb {
* Pseudo amap definition in which each bit of the actual structure is defined
* as a byte: used to calculate offset/shift/mask of each field
*/
+#define BE_TGT_CTX_UPDT_CMD 0x07
struct amap_iscsi_target_context_update_wrb {
u8 lun[14]; /* DWORD 0 */
u8 lt; /* DWORD 0 */
@@ -773,6 +860,47 @@ struct amap_iscsi_target_context_update_wrb {
} __packed;
+#define BEISCSI_MAX_RECV_DATASEG_LEN (64 * 1024)
+#define BEISCSI_MAX_CXNS 1
+struct amap_iscsi_target_context_update_wrb_v2 {
+ u8 max_burst_length[24]; /* DWORD 0 */
+ u8 rsvd0[3]; /* DWORD 0 */
+ u8 type[5]; /* DWORD 0 */
+ u8 ptr2nextwrb[8]; /* DWORD 1 */
+ u8 wrb_idx[8]; /* DWORD 1 */
+ u8 rsvd1[16]; /* DWORD 1 */
+ u8 max_send_data_segment_length[24]; /* DWORD 2 */
+ u8 rsvd2[8]; /* DWORD 2 */
+ u8 first_burst_length[24]; /* DWORD 3 */
+ u8 rsvd3[8]; /* DOWRD 3 */
+ u8 max_r2t[16]; /* DWORD 4 */
+ u8 rsvd4[10]; /* DWORD 4 */
+ u8 hde; /* DWORD 4 */
+ u8 dde; /* DWORD 4 */
+ u8 erl[2]; /* DWORD 4 */
+ u8 imd; /* DWORD 4 */
+ u8 ir2t; /* DWORD 4 */
+ u8 stat_sn[32]; /* DWORD 5 */
+ u8 rsvd5[32]; /* DWORD 6 */
+ u8 rsvd6[32]; /* DWORD 7 */
+ u8 max_recv_dataseg_len[24]; /* DWORD 8 */
+ u8 rsvd7[8]; /* DWORD 8 */
+ u8 rsvd8[32]; /* DWORD 9 */
+ u8 rsvd9[32]; /* DWORD 10 */
+ u8 max_cxns[16]; /* DWORD 11 */
+ u8 rsvd10[11]; /* DWORD 11*/
+ u8 invld; /* DWORD 11 */
+ u8 rsvd11;/* DWORD 11*/
+ u8 dmsg; /* DWORD 11 */
+ u8 data_seq_inorder; /* DWORD 11 */
+ u8 pdu_seq_inorder; /* DWORD 11 */
+ u8 rsvd12[32]; /*DWORD 12 */
+ u8 rsvd13[32]; /* DWORD 13 */
+ u8 rsvd14[32]; /* DWORD 14 */
+ u8 rsvd15[32]; /* DWORD 15 */
+} __packed;
+
+
struct be_ring {
u32 pages; /* queue size in pages */
u32 id; /* queue id assigned by beklib */
@@ -837,7 +965,7 @@ struct hwi_context_memory {
u16 max_eqd; /* in usecs */
u16 cur_eqd; /* in usecs */
struct be_eq_obj be_eq[MAX_CPUS];
- struct be_queue_info be_cq[MAX_CPUS];
+ struct be_queue_info be_cq[MAX_CPUS - 1];
struct be_queue_info be_def_hdrq;
struct be_queue_info be_def_dataq;
diff --git a/drivers/scsi/be2iscsi/be_mgmt.c b/drivers/scsi/be2iscsi/be_mgmt.c
index aab5dd359e2..a6c2fe4b4d6 100644
--- a/drivers/scsi/be2iscsi/be_mgmt.c
+++ b/drivers/scsi/be2iscsi/be_mgmt.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2011 Emulex
+ * Copyright (C) 2005 - 2012 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -22,6 +22,138 @@
#include <scsi/scsi_bsg_iscsi.h>
#include "be_mgmt.h"
#include "be_iscsi.h"
+#include "be_main.h"
+
+/* UE Status Low CSR */
+static const char * const desc_ue_status_low[] = {
+ "CEV",
+ "CTX",
+ "DBUF",
+ "ERX",
+ "Host",
+ "MPU",
+ "NDMA",
+ "PTC ",
+ "RDMA ",
+ "RXF ",
+ "RXIPS ",
+ "RXULP0 ",
+ "RXULP1 ",
+ "RXULP2 ",
+ "TIM ",
+ "TPOST ",
+ "TPRE ",
+ "TXIPS ",
+ "TXULP0 ",
+ "TXULP1 ",
+ "UC ",
+ "WDMA ",
+ "TXULP2 ",
+ "HOST1 ",
+ "P0_OB_LINK ",
+ "P1_OB_LINK ",
+ "HOST_GPIO ",
+ "MBOX ",
+ "AXGMAC0",
+ "AXGMAC1",
+ "JTAG",
+ "MPU_INTPEND"
+};
+
+/* UE Status High CSR */
+static const char * const desc_ue_status_hi[] = {
+ "LPCMEMHOST",
+ "MGMT_MAC",
+ "PCS0ONLINE",
+ "MPU_IRAM",
+ "PCS1ONLINE",
+ "PCTL0",
+ "PCTL1",
+ "PMEM",
+ "RR",
+ "TXPB",
+ "RXPP",
+ "XAUI",
+ "TXP",
+ "ARM",
+ "IPC",
+ "HOST2",
+ "HOST3",
+ "HOST4",
+ "HOST5",
+ "HOST6",
+ "HOST7",
+ "HOST8",
+ "HOST9",
+ "NETC",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown",
+ "Unknown"
+};
+
+/*
+ * beiscsi_ue_detec()- Detect Unrecoverable Error on adapter
+ * @phba: Driver priv structure
+ *
+ * Read registers linked to UE and check for the UE status
+ **/
+void beiscsi_ue_detect(struct beiscsi_hba *phba)
+{
+ uint32_t ue_hi = 0, ue_lo = 0;
+ uint32_t ue_mask_hi = 0, ue_mask_lo = 0;
+ uint8_t i = 0;
+
+ if (phba->ue_detected)
+ return;
+
+ pci_read_config_dword(phba->pcidev,
+ PCICFG_UE_STATUS_LOW, &ue_lo);
+ pci_read_config_dword(phba->pcidev,
+ PCICFG_UE_STATUS_MASK_LOW,
+ &ue_mask_lo);
+ pci_read_config_dword(phba->pcidev,
+ PCICFG_UE_STATUS_HIGH,
+ &ue_hi);
+ pci_read_config_dword(phba->pcidev,
+ PCICFG_UE_STATUS_MASK_HI,
+ &ue_mask_hi);
+
+ ue_lo = (ue_lo & ~ue_mask_lo);
+ ue_hi = (ue_hi & ~ue_mask_hi);
+
+
+ if (ue_lo || ue_hi) {
+ phba->ue_detected = true;
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
+ "BG_%d : Error detected on the adapter\n");
+ }
+
+ if (ue_lo) {
+ for (i = 0; ue_lo; ue_lo >>= 1, i++) {
+ if (ue_lo & 1)
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_CONFIG,
+ "BG_%d : UE_LOW %s bit set\n",
+ desc_ue_status_low[i]);
+ }
+ }
+
+ if (ue_hi) {
+ for (i = 0; ue_hi; ue_hi >>= 1, i++) {
+ if (ue_hi & 1)
+ beiscsi_log(phba, KERN_ERR,
+ BEISCSI_LOG_CONFIG,
+ "BG_%d : UE_HIGH %s bit set\n",
+ desc_ue_status_hi[i]);
+ }
+ }
+}
/**
* mgmt_reopen_session()- Reopen a session based on reopen_type
@@ -575,13 +707,20 @@ unsigned int mgmt_get_all_if_id(struct beiscsi_hba *phba)
return status;
}
+/*
+ * mgmt_exec_nonemb_cmd()- Execute Non Embedded MBX Cmd
+ * @phba: Driver priv structure
+ * @nonemb_cmd: Address of the MBX command issued
+ * @resp_buf: Buffer to copy the MBX cmd response
+ * @resp_buf_len: respone lenght to be copied
+ *
+ **/
static int mgmt_exec_nonemb_cmd(struct beiscsi_hba *phba,
struct be_dma_mem *nonemb_cmd, void *resp_buf,
int resp_buf_len)
{
struct be_ctrl_info *ctrl = &phba->ctrl;
struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
- unsigned short status, extd_status;
struct be_sge *sge;
unsigned int tag;
int rc = 0;
@@ -599,31 +738,25 @@ static int mgmt_exec_nonemb_cmd(struct beiscsi_hba *phba,
be_wrb_hdr_prepare(wrb, nonemb_cmd->size, false, 1);
sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma));
- sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF);
+ sge->pa_lo = cpu_to_le32(lower_32_bits(nonemb_cmd->dma));
sge->len = cpu_to_le32(nonemb_cmd->size);
be_mcc_notify(phba);
spin_unlock(&ctrl->mbox_lock);
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
-
- extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
- status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
- if (status || extd_status) {
+ rc = beiscsi_mccq_compl(phba, tag, NULL, nonemb_cmd->va);
+ if (rc) {
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
- "BG_%d : mgmt_exec_nonemb_cmd Failed status = %d"
- "extd_status = %d\n", status, extd_status);
+ "BG_%d : mgmt_exec_nonemb_cmd Failed status\n");
+
rc = -EIO;
- goto free_tag;
+ goto free_cmd;
}
if (resp_buf)
memcpy(resp_buf, nonemb_cmd->va, resp_buf_len);
-free_tag:
- free_mcc_tag(&phba->ctrl, tag);
free_cmd:
pci_free_consistent(ctrl->pdev, nonemb_cmd->size,
nonemb_cmd->va, nonemb_cmd->dma);
@@ -1009,10 +1142,9 @@ int be_mgmt_get_boot_shandle(struct beiscsi_hba *phba,
{
struct be_cmd_get_boot_target_resp *boot_resp;
struct be_mcc_wrb *wrb;
- unsigned int tag, wrb_num;
+ unsigned int tag;
uint8_t boot_retry = 3;
- unsigned short status, extd_status;
- struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
+ int rc;
do {
/* Get the Boot Target Session Handle and Count*/
@@ -1022,24 +1154,16 @@ int be_mgmt_get_boot_shandle(struct beiscsi_hba *phba,
BEISCSI_LOG_CONFIG | BEISCSI_LOG_INIT,
"BG_%d : Getting Boot Target Info Failed\n");
return -EAGAIN;
- } else
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
-
- wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
- extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
- status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
- if (status || extd_status) {
+ }
+
+ rc = beiscsi_mccq_compl(phba, tag, &wrb, NULL);
+ if (rc) {
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
- "BG_%d : mgmt_get_boot_target Failed"
- " status = %d extd_status = %d\n",
- status, extd_status);
- free_mcc_tag(&phba->ctrl, tag);
+ "BG_%d : MBX CMD get_boot_target Failed\n");
return -EBUSY;
}
- wrb = queue_get_wrb(mccq, wrb_num);
- free_mcc_tag(&phba->ctrl, tag);
+
boot_resp = embedded_payload(wrb);
/* Check if the there are any Boot targets configured */
@@ -1064,24 +1188,15 @@ int be_mgmt_get_boot_shandle(struct beiscsi_hba *phba,
BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
"BG_%d : mgmt_reopen_session Failed\n");
return -EAGAIN;
- } else
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
-
- wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
- extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
- status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
- if (status || extd_status) {
+ }
+
+ rc = beiscsi_mccq_compl(phba, tag, NULL, NULL);
+ if (rc) {
beiscsi_log(phba, KERN_ERR,
BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
- "BG_%d : mgmt_reopen_session Failed"
- " status = %d extd_status = %d\n",
- status, extd_status);
- free_mcc_tag(&phba->ctrl, tag);
- return -EBUSY;
+ "BG_%d : mgmt_reopen_session Failed");
+ return rc;
}
- free_mcc_tag(&phba->ctrl, tag);
-
} while (--boot_retry);
/* Couldn't log into the boot target */
@@ -1106,8 +1221,9 @@ int be_mgmt_get_boot_shandle(struct beiscsi_hba *phba,
int mgmt_set_vlan(struct beiscsi_hba *phba,
uint16_t vlan_tag)
{
- unsigned int tag, wrb_num;
- unsigned short status, extd_status;
+ int rc;
+ unsigned int tag;
+ struct be_mcc_wrb *wrb = NULL;
tag = be_cmd_set_vlan(phba, vlan_tag);
if (!tag) {
@@ -1115,24 +1231,208 @@ int mgmt_set_vlan(struct beiscsi_hba *phba,
(BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX),
"BG_%d : VLAN Setting Failed\n");
return -EBUSY;
- } else
- wait_event_interruptible(phba->ctrl.mcc_wait[tag],
- phba->ctrl.mcc_numtag[tag]);
-
- wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
- extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
- status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
+ }
- if (status || extd_status) {
+ rc = beiscsi_mccq_compl(phba, tag, &wrb, NULL);
+ if (rc) {
beiscsi_log(phba, KERN_ERR,
(BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX),
- "BS_%d : status : %d extd_status : %d\n",
- status, extd_status);
+ "BS_%d : VLAN MBX Cmd Failed\n");
+ return rc;
+ }
+ return rc;
+}
- free_mcc_tag(&phba->ctrl, tag);
- return -EAGAIN;
+/**
+ * beiscsi_drvr_ver_disp()- Display the driver Name and Version
+ * @dev: ptr to device not used.
+ * @attr: device attribute, not used.
+ * @buf: contains formatted text driver name and version
+ *
+ * return
+ * size of the formatted string
+ **/
+ssize_t
+beiscsi_drvr_ver_disp(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, BE_NAME "\n");
+}
+
+/**
+ * beiscsi_adap_family_disp()- Display adapter family.
+ * @dev: ptr to device to get priv structure
+ * @attr: device attribute, not used.
+ * @buf: contains formatted text driver name and version
+ *
+ * return
+ * size of the formatted string
+ **/
+ssize_t
+beiscsi_adap_family_disp(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ uint16_t dev_id = 0;
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct beiscsi_hba *phba = iscsi_host_priv(shost);
+
+ dev_id = phba->pcidev->device;
+ switch (dev_id) {
+ case BE_DEVICE_ID1:
+ case OC_DEVICE_ID1:
+ case OC_DEVICE_ID2:
+ return snprintf(buf, PAGE_SIZE, "BE2 Adapter Family\n");
+ break;
+ case BE_DEVICE_ID2:
+ case OC_DEVICE_ID3:
+ return snprintf(buf, PAGE_SIZE, "BE3-R Adapter Family\n");
+ break;
+ case OC_SKH_ID1:
+ return snprintf(buf, PAGE_SIZE, "Skyhawk-R Adapter Family\n");
+ break;
+ default:
+ return snprintf(buf, PAGE_SIZE,
+ "Unkown Adapter Family: 0x%x\n", dev_id);
+ break;
}
+}
- free_mcc_tag(&phba->ctrl, tag);
- return 0;
+
+void beiscsi_offload_cxn_v0(struct beiscsi_offload_params *params,
+ struct wrb_handle *pwrb_handle,
+ struct be_mem_descriptor *mem_descr)
+{
+ struct iscsi_wrb *pwrb = pwrb_handle->pwrb;
+
+ memset(pwrb, 0, sizeof(*pwrb));
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
+ max_send_data_segment_length, pwrb,
+ params->dw[offsetof(struct amap_beiscsi_offload_params,
+ max_send_data_segment_length) / 32]);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, type, pwrb,
+ BE_TGT_CTX_UPDT_CMD);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
+ first_burst_length,
+ pwrb,
+ params->dw[offsetof(struct amap_beiscsi_offload_params,
+ first_burst_length) / 32]);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, erl, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ erl) / 32] & OFFLD_PARAMS_ERL));
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, dde, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ dde) / 32] & OFFLD_PARAMS_DDE) >> 2);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, hde, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ hde) / 32] & OFFLD_PARAMS_HDE) >> 3);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, ir2t, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ ir2t) / 32] & OFFLD_PARAMS_IR2T) >> 4);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, imd, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ imd) / 32] & OFFLD_PARAMS_IMD) >> 5);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, stat_sn,
+ pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ exp_statsn) / 32] + 1));
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, wrb_idx,
+ pwrb, pwrb_handle->wrb_index);
+
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
+ max_burst_length, pwrb, params->dw[offsetof
+ (struct amap_beiscsi_offload_params,
+ max_burst_length) / 32]);
+
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, ptr2nextwrb,
+ pwrb, pwrb_handle->nxt_wrb_index);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
+ session_state, pwrb, 0);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, compltonack,
+ pwrb, 1);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, notpredblq,
+ pwrb, 0);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, mode, pwrb,
+ 0);
+
+ mem_descr += ISCSI_MEM_GLOBAL_HEADER;
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
+ pad_buffer_addr_hi, pwrb,
+ mem_descr->mem_array[0].bus_address.u.a32.address_hi);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
+ pad_buffer_addr_lo, pwrb,
+ mem_descr->mem_array[0].bus_address.u.a32.address_lo);
+}
+
+void beiscsi_offload_cxn_v2(struct beiscsi_offload_params *params,
+ struct wrb_handle *pwrb_handle)
+{
+ struct iscsi_wrb *pwrb = pwrb_handle->pwrb;
+
+ memset(pwrb, 0, sizeof(*pwrb));
+
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
+ max_burst_length, pwrb, params->dw[offsetof
+ (struct amap_beiscsi_offload_params,
+ max_burst_length) / 32]);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ max_burst_length, pwrb, params->dw[offsetof
+ (struct amap_beiscsi_offload_params,
+ max_burst_length) / 32]);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ type, pwrb,
+ BE_TGT_CTX_UPDT_CMD);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ ptr2nextwrb,
+ pwrb, pwrb_handle->nxt_wrb_index);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, wrb_idx,
+ pwrb, pwrb_handle->wrb_index);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ max_send_data_segment_length, pwrb,
+ params->dw[offsetof(struct amap_beiscsi_offload_params,
+ max_send_data_segment_length) / 32]);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ first_burst_length, pwrb,
+ params->dw[offsetof(struct amap_beiscsi_offload_params,
+ first_burst_length) / 32]);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ max_recv_dataseg_len, pwrb, BEISCSI_MAX_RECV_DATASEG_LEN);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ max_cxns, pwrb, BEISCSI_MAX_CXNS);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, erl, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ erl) / 32] & OFFLD_PARAMS_ERL));
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, dde, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ dde) / 32] & OFFLD_PARAMS_DDE) >> 2);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, hde, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ hde) / 32] & OFFLD_PARAMS_HDE) >> 3);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ ir2t, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ ir2t) / 32] & OFFLD_PARAMS_IR2T) >> 4);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, imd, pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ imd) / 32] & OFFLD_PARAMS_IMD) >> 5);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ data_seq_inorder,
+ pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ data_seq_inorder) / 32] &
+ OFFLD_PARAMS_DATA_SEQ_INORDER) >> 6);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2,
+ pdu_seq_inorder,
+ pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ pdu_seq_inorder) / 32] &
+ OFFLD_PARAMS_PDU_SEQ_INORDER) >> 7);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, max_r2t,
+ pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ max_r2t) / 32] &
+ OFFLD_PARAMS_MAX_R2T) >> 8);
+ AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, stat_sn,
+ pwrb,
+ (params->dw[offsetof(struct amap_beiscsi_offload_params,
+ exp_statsn) / 32] + 1));
}
diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h
index c50cef6fec0..2e4968add79 100644
--- a/drivers/scsi/be2iscsi/be_mgmt.h
+++ b/drivers/scsi/be2iscsi/be_mgmt.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2005 - 2011 Emulex
+ * Copyright (C) 2005 - 2012 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -30,6 +30,12 @@
#define IP_V6_LEN 16
#define IP_V4_LEN 4
+/* UE Status and Mask register */
+#define PCICFG_UE_STATUS_LOW 0xA0
+#define PCICFG_UE_STATUS_HIGH 0xA4
+#define PCICFG_UE_STATUS_MASK_LOW 0xA8
+#define PCICFG_UE_STATUS_MASK_HI 0xAC
+
/**
* Pseudo amap definition in which each bit of the actual structure is defined
* as a byte: used to calculate offset/shift/mask of each field
@@ -301,4 +307,19 @@ int be_mgmt_get_boot_shandle(struct beiscsi_hba *phba,
unsigned int mgmt_get_all_if_id(struct beiscsi_hba *phba);
int mgmt_set_vlan(struct beiscsi_hba *phba, uint16_t vlan_tag);
+
+ssize_t beiscsi_drvr_ver_disp(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+ssize_t beiscsi_adap_family_disp(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+void beiscsi_offload_cxn_v0(struct beiscsi_offload_params *params,
+ struct wrb_handle *pwrb_handle,
+ struct be_mem_descriptor *mem_descr);
+
+void beiscsi_offload_cxn_v2(struct beiscsi_offload_params *params,
+ struct wrb_handle *pwrb_handle);
+void beiscsi_ue_detect(struct beiscsi_hba *phba);
+
#endif
diff --git a/drivers/scsi/bnx2fc/bnx2fc_hwi.c b/drivers/scsi/bnx2fc/bnx2fc_hwi.c
index 6d6eee42ac7..ef60afa94d0 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_hwi.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_hwi.c
@@ -296,7 +296,7 @@ int bnx2fc_send_session_ofld_req(struct fcoe_port *port,
ofld_req3.flags |= (interface->vlan_enabled <<
FCOE_KWQE_CONN_OFFLOAD3_B_VLAN_FLAG_SHIFT);
- /* C2_VALID and ACK flags are not set as they are not suppported */
+ /* C2_VALID and ACK flags are not set as they are not supported */
/* Initialize offload request 4 structure */
diff --git a/drivers/scsi/bnx2i/bnx2i.h b/drivers/scsi/bnx2i/bnx2i.h
index 3f9e7061258..b44d04e41b0 100644
--- a/drivers/scsi/bnx2i/bnx2i.h
+++ b/drivers/scsi/bnx2i/bnx2i.h
@@ -800,7 +800,7 @@ extern struct device_attribute *bnx2i_dev_attributes[];
/*
* Function Prototypes
*/
-extern void bnx2i_identify_device(struct bnx2i_hba *hba);
+extern void bnx2i_identify_device(struct bnx2i_hba *hba, struct cnic_dev *dev);
extern void bnx2i_ulp_init(struct cnic_dev *dev);
extern void bnx2i_ulp_exit(struct cnic_dev *dev);
diff --git a/drivers/scsi/bnx2i/bnx2i_init.c b/drivers/scsi/bnx2i/bnx2i_init.c
index b17637aab9a..ee009e4ad09 100644
--- a/drivers/scsi/bnx2i/bnx2i_init.c
+++ b/drivers/scsi/bnx2i/bnx2i_init.c
@@ -79,42 +79,33 @@ static struct notifier_block bnx2i_cpu_notifier = {
/**
* bnx2i_identify_device - identifies NetXtreme II device type
* @hba: Adapter structure pointer
+ * @cnic: Corresponding cnic device
*
* This function identifies the NX2 device type and sets appropriate
* queue mailbox register access method, 5709 requires driver to
* access MBOX regs using *bin* mode
*/
-void bnx2i_identify_device(struct bnx2i_hba *hba)
+void bnx2i_identify_device(struct bnx2i_hba *hba, struct cnic_dev *dev)
{
hba->cnic_dev_type = 0;
- if ((hba->pci_did == PCI_DEVICE_ID_NX2_5706) ||
- (hba->pci_did == PCI_DEVICE_ID_NX2_5706S))
- set_bit(BNX2I_NX2_DEV_5706, &hba->cnic_dev_type);
- else if ((hba->pci_did == PCI_DEVICE_ID_NX2_5708) ||
- (hba->pci_did == PCI_DEVICE_ID_NX2_5708S))
- set_bit(BNX2I_NX2_DEV_5708, &hba->cnic_dev_type);
- else if ((hba->pci_did == PCI_DEVICE_ID_NX2_5709) ||
- (hba->pci_did == PCI_DEVICE_ID_NX2_5709S)) {
- set_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type);
- hba->mail_queue_access = BNX2I_MQ_BIN_MODE;
- } else if (hba->pci_did == PCI_DEVICE_ID_NX2_57710 ||
- hba->pci_did == PCI_DEVICE_ID_NX2_57711 ||
- hba->pci_did == PCI_DEVICE_ID_NX2_57711E ||
- hba->pci_did == PCI_DEVICE_ID_NX2_57712 ||
- hba->pci_did == PCI_DEVICE_ID_NX2_57712E ||
- hba->pci_did == PCI_DEVICE_ID_NX2_57800 ||
- hba->pci_did == PCI_DEVICE_ID_NX2_57800_MF ||
- hba->pci_did == PCI_DEVICE_ID_NX2_57800_VF ||
- hba->pci_did == PCI_DEVICE_ID_NX2_57810 ||
- hba->pci_did == PCI_DEVICE_ID_NX2_57810_MF ||
- hba->pci_did == PCI_DEVICE_ID_NX2_57810_VF ||
- hba->pci_did == PCI_DEVICE_ID_NX2_57840 ||
- hba->pci_did == PCI_DEVICE_ID_NX2_57840_MF ||
- hba->pci_did == PCI_DEVICE_ID_NX2_57840_VF)
+ if (test_bit(CNIC_F_BNX2_CLASS, &dev->flags)) {
+ if (hba->pci_did == PCI_DEVICE_ID_NX2_5706 ||
+ hba->pci_did == PCI_DEVICE_ID_NX2_5706S) {
+ set_bit(BNX2I_NX2_DEV_5706, &hba->cnic_dev_type);
+ } else if (hba->pci_did == PCI_DEVICE_ID_NX2_5708 ||
+ hba->pci_did == PCI_DEVICE_ID_NX2_5708S) {
+ set_bit(BNX2I_NX2_DEV_5708, &hba->cnic_dev_type);
+ } else if (hba->pci_did == PCI_DEVICE_ID_NX2_5709 ||
+ hba->pci_did == PCI_DEVICE_ID_NX2_5709S) {
+ set_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type);
+ hba->mail_queue_access = BNX2I_MQ_BIN_MODE;
+ }
+ } else if (test_bit(CNIC_F_BNX2X_CLASS, &dev->flags)) {
set_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type);
- else
+ } else {
printk(KERN_ALERT "bnx2i: unknown device, 0x%x\n",
hba->pci_did);
+ }
}
diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c
index 3b34c13e2f0..0056e47bd56 100644
--- a/drivers/scsi/bnx2i/bnx2i_iscsi.c
+++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c
@@ -808,7 +808,7 @@ struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic)
hba->pci_func = PCI_FUNC(hba->pcidev->devfn);
hba->pci_devno = PCI_SLOT(hba->pcidev->devfn);
- bnx2i_identify_device(hba);
+ bnx2i_identify_device(hba, cnic);
bnx2i_setup_host_queue_size(hba, shost);
hba->reg_base = pci_resource_start(hba->pcidev, 0);
diff --git a/drivers/scsi/csiostor/Kconfig b/drivers/scsi/csiostor/Kconfig
new file mode 100644
index 00000000000..4d03b032aa1
--- /dev/null
+++ b/drivers/scsi/csiostor/Kconfig
@@ -0,0 +1,19 @@
+config SCSI_CHELSIO_FCOE
+ tristate "Chelsio Communications FCoE support"
+ depends on PCI && SCSI
+ select SCSI_FC_ATTRS
+ select FW_LOADER
+ help
+ This driver supports FCoE Offload functionality over
+ Chelsio T4-based 10Gb Converged Network Adapters.
+
+ For general information about Chelsio and our products, visit
+ our website at <http://www.chelsio.com>.
+
+ For customer support, please visit our customer support page at
+ <http://www.chelsio.com/support.html>.
+
+ Please send feedback to <linux-bugs@chelsio.com>.
+
+ To compile this driver as a module choose M here; the module
+ will be called csiostor.
diff --git a/drivers/scsi/csiostor/Makefile b/drivers/scsi/csiostor/Makefile
new file mode 100644
index 00000000000..b581966c88f
--- /dev/null
+++ b/drivers/scsi/csiostor/Makefile
@@ -0,0 +1,11 @@
+#
+## Chelsio FCoE driver
+#
+##
+
+ccflags-y += -I$(srctree)/drivers/net/ethernet/chelsio/cxgb4
+
+obj-$(CONFIG_SCSI_CHELSIO_FCOE) += csiostor.o
+
+csiostor-objs := csio_attr.o csio_init.o csio_lnode.o csio_scsi.o \
+ csio_hw.o csio_isr.o csio_mb.o csio_rnode.o csio_wr.o
diff --git a/drivers/scsi/csiostor/csio_attr.c b/drivers/scsi/csiostor/csio_attr.c
new file mode 100644
index 00000000000..065a87ace62
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_attr.c
@@ -0,0 +1,796 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/mm.h>
+#include <linux/jiffies.h>
+#include <scsi/fc/fc_fs.h>
+
+#include "csio_init.h"
+
+static void
+csio_vport_set_state(struct csio_lnode *ln);
+
+/*
+ * csio_reg_rnode - Register a remote port with FC transport.
+ * @rn: Rnode representing remote port.
+ *
+ * Call fc_remote_port_add() to register this remote port with FC transport.
+ * If remote port is Initiator OR Target OR both, change the role appropriately.
+ *
+ */
+void
+csio_reg_rnode(struct csio_rnode *rn)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+ struct Scsi_Host *shost = csio_ln_to_shost(ln);
+ struct fc_rport_identifiers ids;
+ struct fc_rport *rport;
+ struct csio_service_parms *sp;
+
+ ids.node_name = wwn_to_u64(csio_rn_wwnn(rn));
+ ids.port_name = wwn_to_u64(csio_rn_wwpn(rn));
+ ids.port_id = rn->nport_id;
+ ids.roles = FC_RPORT_ROLE_UNKNOWN;
+
+ if (rn->role & CSIO_RNFR_INITIATOR || rn->role & CSIO_RNFR_TARGET) {
+ rport = rn->rport;
+ CSIO_ASSERT(rport != NULL);
+ goto update_role;
+ }
+
+ rn->rport = fc_remote_port_add(shost, 0, &ids);
+ if (!rn->rport) {
+ csio_ln_err(ln, "Failed to register rport = 0x%x.\n",
+ rn->nport_id);
+ return;
+ }
+
+ ln->num_reg_rnodes++;
+ rport = rn->rport;
+ spin_lock_irq(shost->host_lock);
+ *((struct csio_rnode **)rport->dd_data) = rn;
+ spin_unlock_irq(shost->host_lock);
+
+ sp = &rn->rn_sparm;
+ rport->maxframe_size = ntohs(sp->csp.sp_bb_data);
+ if (ntohs(sp->clsp[2].cp_class) & FC_CPC_VALID)
+ rport->supported_classes = FC_COS_CLASS3;
+ else
+ rport->supported_classes = FC_COS_UNSPECIFIED;
+update_role:
+ if (rn->role & CSIO_RNFR_INITIATOR)
+ ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR;
+ if (rn->role & CSIO_RNFR_TARGET)
+ ids.roles |= FC_RPORT_ROLE_FCP_TARGET;
+
+ if (ids.roles != FC_RPORT_ROLE_UNKNOWN)
+ fc_remote_port_rolechg(rport, ids.roles);
+
+ rn->scsi_id = rport->scsi_target_id;
+
+ csio_ln_dbg(ln, "Remote port x%x role 0x%x registered\n",
+ rn->nport_id, ids.roles);
+}
+
+/*
+ * csio_unreg_rnode - Unregister a remote port with FC transport.
+ * @rn: Rnode representing remote port.
+ *
+ * Call fc_remote_port_delete() to unregister this remote port with FC
+ * transport.
+ *
+ */
+void
+csio_unreg_rnode(struct csio_rnode *rn)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+ struct fc_rport *rport = rn->rport;
+
+ rn->role &= ~(CSIO_RNFR_INITIATOR | CSIO_RNFR_TARGET);
+ fc_remote_port_delete(rport);
+ ln->num_reg_rnodes--;
+
+ csio_ln_dbg(ln, "Remote port x%x un-registered\n", rn->nport_id);
+}
+
+/*
+ * csio_lnode_async_event - Async events from local port.
+ * @ln: lnode representing local port.
+ *
+ * Async events from local node that FC transport/SCSI ML
+ * should be made aware of (Eg: RSCN).
+ */
+void
+csio_lnode_async_event(struct csio_lnode *ln, enum csio_ln_fc_evt fc_evt)
+{
+ switch (fc_evt) {
+ case CSIO_LN_FC_RSCN:
+ /* Get payload of rscn from ln */
+ /* For each RSCN entry */
+ /*
+ * fc_host_post_event(shost,
+ * fc_get_event_number(),
+ * FCH_EVT_RSCN,
+ * rscn_entry);
+ */
+ break;
+ case CSIO_LN_FC_LINKUP:
+ /* send fc_host_post_event */
+ /* set vport state */
+ if (csio_is_npiv_ln(ln))
+ csio_vport_set_state(ln);
+
+ break;
+ case CSIO_LN_FC_LINKDOWN:
+ /* send fc_host_post_event */
+ /* set vport state */
+ if (csio_is_npiv_ln(ln))
+ csio_vport_set_state(ln);
+
+ break;
+ case CSIO_LN_FC_ATTRIB_UPDATE:
+ csio_fchost_attr_init(ln);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * csio_fchost_attr_init - Initialize FC transport attributes
+ * @ln: Lnode.
+ *
+ */
+void
+csio_fchost_attr_init(struct csio_lnode *ln)
+{
+ struct Scsi_Host *shost = csio_ln_to_shost(ln);
+
+ fc_host_node_name(shost) = wwn_to_u64(csio_ln_wwnn(ln));
+ fc_host_port_name(shost) = wwn_to_u64(csio_ln_wwpn(ln));
+
+ fc_host_supported_classes(shost) = FC_COS_CLASS3;
+ fc_host_max_npiv_vports(shost) =
+ (csio_lnode_to_hw(ln))->fres_info.max_vnps;
+ fc_host_supported_speeds(shost) = FC_PORTSPEED_10GBIT |
+ FC_PORTSPEED_1GBIT;
+
+ fc_host_maxframe_size(shost) = ntohs(ln->ln_sparm.csp.sp_bb_data);
+ memset(fc_host_supported_fc4s(shost), 0,
+ sizeof(fc_host_supported_fc4s(shost)));
+ fc_host_supported_fc4s(shost)[7] = 1;
+
+ memset(fc_host_active_fc4s(shost), 0,
+ sizeof(fc_host_active_fc4s(shost)));
+ fc_host_active_fc4s(shost)[7] = 1;
+}
+
+/*
+ * csio_get_host_port_id - sysfs entries for nport_id is
+ * populated/cached from this function
+ */
+static void
+csio_get_host_port_id(struct Scsi_Host *shost)
+{
+ struct csio_lnode *ln = shost_priv(shost);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ spin_lock_irq(&hw->lock);
+ fc_host_port_id(shost) = ln->nport_id;
+ spin_unlock_irq(&hw->lock);
+}
+
+/*
+ * csio_get_port_type - Return FC local port type.
+ * @shost: scsi host.
+ *
+ */
+static void
+csio_get_host_port_type(struct Scsi_Host *shost)
+{
+ struct csio_lnode *ln = shost_priv(shost);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ spin_lock_irq(&hw->lock);
+ if (csio_is_npiv_ln(ln))
+ fc_host_port_type(shost) = FC_PORTTYPE_NPIV;
+ else
+ fc_host_port_type(shost) = FC_PORTTYPE_NPORT;
+ spin_unlock_irq(&hw->lock);
+}
+
+/*
+ * csio_get_port_state - Return FC local port state.
+ * @shost: scsi host.
+ *
+ */
+static void
+csio_get_host_port_state(struct Scsi_Host *shost)
+{
+ struct csio_lnode *ln = shost_priv(shost);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ char state[16];
+
+ spin_lock_irq(&hw->lock);
+
+ csio_lnode_state_to_str(ln, state);
+ if (!strcmp(state, "READY"))
+ fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+ else if (!strcmp(state, "OFFLINE"))
+ fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN;
+ else
+ fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
+
+ spin_unlock_irq(&hw->lock);
+}
+
+/*
+ * csio_get_host_speed - Return link speed to FC transport.
+ * @shost: scsi host.
+ *
+ */
+static void
+csio_get_host_speed(struct Scsi_Host *shost)
+{
+ struct csio_lnode *ln = shost_priv(shost);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ spin_lock_irq(&hw->lock);
+ switch (hw->pport[ln->portid].link_speed) {
+ case FW_PORT_CAP_SPEED_1G:
+ fc_host_speed(shost) = FC_PORTSPEED_1GBIT;
+ break;
+ case FW_PORT_CAP_SPEED_10G:
+ fc_host_speed(shost) = FC_PORTSPEED_10GBIT;
+ break;
+ default:
+ fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
+ break;
+ }
+ spin_unlock_irq(&hw->lock);
+}
+
+/*
+ * csio_get_host_fabric_name - Return fabric name
+ * @shost: scsi host.
+ *
+ */
+static void
+csio_get_host_fabric_name(struct Scsi_Host *shost)
+{
+ struct csio_lnode *ln = shost_priv(shost);
+ struct csio_rnode *rn = NULL;
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ spin_lock_irq(&hw->lock);
+ rn = csio_rnode_lookup_portid(ln, FC_FID_FLOGI);
+ if (rn)
+ fc_host_fabric_name(shost) = wwn_to_u64(csio_rn_wwnn(rn));
+ else
+ fc_host_fabric_name(shost) = 0;
+ spin_unlock_irq(&hw->lock);
+}
+
+/*
+ * csio_get_host_speed - Return FC transport statistics.
+ * @ln: Lnode.
+ *
+ */
+static struct fc_host_statistics *
+csio_get_stats(struct Scsi_Host *shost)
+{
+ struct csio_lnode *ln = shost_priv(shost);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ struct fc_host_statistics *fhs = &ln->fch_stats;
+ struct fw_fcoe_port_stats fcoe_port_stats;
+ uint64_t seconds;
+
+ memset(&fcoe_port_stats, 0, sizeof(struct fw_fcoe_port_stats));
+ csio_get_phy_port_stats(hw, ln->portid, &fcoe_port_stats);
+
+ fhs->tx_frames += (be64_to_cpu(fcoe_port_stats.tx_bcast_frames) +
+ be64_to_cpu(fcoe_port_stats.tx_mcast_frames) +
+ be64_to_cpu(fcoe_port_stats.tx_ucast_frames) +
+ be64_to_cpu(fcoe_port_stats.tx_offload_frames));
+ fhs->tx_words += (be64_to_cpu(fcoe_port_stats.tx_bcast_bytes) +
+ be64_to_cpu(fcoe_port_stats.tx_mcast_bytes) +
+ be64_to_cpu(fcoe_port_stats.tx_ucast_bytes) +
+ be64_to_cpu(fcoe_port_stats.tx_offload_bytes)) /
+ CSIO_WORD_TO_BYTE;
+ fhs->rx_frames += (be64_to_cpu(fcoe_port_stats.rx_bcast_frames) +
+ be64_to_cpu(fcoe_port_stats.rx_mcast_frames) +
+ be64_to_cpu(fcoe_port_stats.rx_ucast_frames));
+ fhs->rx_words += (be64_to_cpu(fcoe_port_stats.rx_bcast_bytes) +
+ be64_to_cpu(fcoe_port_stats.rx_mcast_bytes) +
+ be64_to_cpu(fcoe_port_stats.rx_ucast_bytes)) /
+ CSIO_WORD_TO_BYTE;
+ fhs->error_frames += be64_to_cpu(fcoe_port_stats.rx_err_frames);
+ fhs->fcp_input_requests += ln->stats.n_input_requests;
+ fhs->fcp_output_requests += ln->stats.n_output_requests;
+ fhs->fcp_control_requests += ln->stats.n_control_requests;
+ fhs->fcp_input_megabytes += ln->stats.n_input_bytes >> 20;
+ fhs->fcp_output_megabytes += ln->stats.n_output_bytes >> 20;
+ fhs->link_failure_count = ln->stats.n_link_down;
+ /* Reset stats for the device */
+ seconds = jiffies_to_msecs(jiffies) - hw->stats.n_reset_start;
+ do_div(seconds, 1000);
+ fhs->seconds_since_last_reset = seconds;
+
+ return fhs;
+}
+
+/*
+ * csio_set_rport_loss_tmo - Set the rport dev loss timeout
+ * @rport: fc rport.
+ * @timeout: new value for dev loss tmo.
+ *
+ * If timeout is non zero set the dev_loss_tmo to timeout, else set
+ * dev_loss_tmo to one.
+ */
+static void
+csio_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout)
+{
+ if (timeout)
+ rport->dev_loss_tmo = timeout;
+ else
+ rport->dev_loss_tmo = 1;
+}
+
+static void
+csio_vport_set_state(struct csio_lnode *ln)
+{
+ struct fc_vport *fc_vport = ln->fc_vport;
+ struct csio_lnode *pln = ln->pln;
+ char state[16];
+
+ /* Set fc vport state based on phyiscal lnode */
+ csio_lnode_state_to_str(pln, state);
+ if (strcmp(state, "READY")) {
+ fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN);
+ return;
+ }
+
+ if (!(pln->flags & CSIO_LNF_NPIVSUPP)) {
+ fc_vport_set_state(fc_vport, FC_VPORT_NO_FABRIC_SUPP);
+ return;
+ }
+
+ /* Set fc vport state based on virtual lnode */
+ csio_lnode_state_to_str(ln, state);
+ if (strcmp(state, "READY")) {
+ fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN);
+ return;
+ }
+ fc_vport_set_state(fc_vport, FC_VPORT_ACTIVE);
+}
+
+static int
+csio_fcoe_alloc_vnp(struct csio_hw *hw, struct csio_lnode *ln)
+{
+ struct csio_lnode *pln;
+ struct csio_mb *mbp;
+ struct fw_fcoe_vnp_cmd *rsp;
+ int ret = 0;
+ int retry = 0;
+
+ /* Issue VNP cmd to alloc vport */
+ /* Allocate Mbox request */
+ spin_lock_irq(&hw->lock);
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ pln = ln->pln;
+ ln->fcf_flowid = pln->fcf_flowid;
+ ln->portid = pln->portid;
+
+ csio_fcoe_vnp_alloc_init_mb(ln, mbp, CSIO_MB_DEFAULT_TMO,
+ pln->fcf_flowid, pln->vnp_flowid, 0,
+ csio_ln_wwnn(ln), csio_ln_wwpn(ln), NULL);
+
+ for (retry = 0; retry < 3; retry++) {
+ /* FW is expected to complete vnp cmd in immediate mode
+ * without much delay.
+ * Otherwise, there will be increase in IO latency since HW
+ * lock is held till completion of vnp mbox cmd.
+ */
+ ret = csio_mb_issue(hw, mbp);
+ if (ret != -EBUSY)
+ break;
+
+ /* Retry if mbox returns busy */
+ spin_unlock_irq(&hw->lock);
+ msleep(2000);
+ spin_lock_irq(&hw->lock);
+ }
+
+ if (ret) {
+ csio_ln_err(ln, "Failed to issue mbox FCoE VNP command\n");
+ goto out_free;
+ }
+
+ /* Process Mbox response of VNP command */
+ rsp = (struct fw_fcoe_vnp_cmd *)(mbp->mb);
+ if (FW_CMD_RETVAL_GET(ntohl(rsp->alloc_to_len16)) != FW_SUCCESS) {
+ csio_ln_err(ln, "FCOE VNP ALLOC cmd returned 0x%x!\n",
+ FW_CMD_RETVAL_GET(ntohl(rsp->alloc_to_len16)));
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ ln->vnp_flowid = FW_FCOE_VNP_CMD_VNPI_GET(
+ ntohl(rsp->gen_wwn_to_vnpi));
+ memcpy(csio_ln_wwnn(ln), rsp->vnport_wwnn, 8);
+ memcpy(csio_ln_wwpn(ln), rsp->vnport_wwpn, 8);
+
+ csio_ln_dbg(ln, "FCOE VNPI: 0x%x\n", ln->vnp_flowid);
+ csio_ln_dbg(ln, "\tWWNN: %x%x%x%x%x%x%x%x\n",
+ ln->ln_sparm.wwnn[0], ln->ln_sparm.wwnn[1],
+ ln->ln_sparm.wwnn[2], ln->ln_sparm.wwnn[3],
+ ln->ln_sparm.wwnn[4], ln->ln_sparm.wwnn[5],
+ ln->ln_sparm.wwnn[6], ln->ln_sparm.wwnn[7]);
+ csio_ln_dbg(ln, "\tWWPN: %x%x%x%x%x%x%x%x\n",
+ ln->ln_sparm.wwpn[0], ln->ln_sparm.wwpn[1],
+ ln->ln_sparm.wwpn[2], ln->ln_sparm.wwpn[3],
+ ln->ln_sparm.wwpn[4], ln->ln_sparm.wwpn[5],
+ ln->ln_sparm.wwpn[6], ln->ln_sparm.wwpn[7]);
+
+out_free:
+ mempool_free(mbp, hw->mb_mempool);
+out:
+ spin_unlock_irq(&hw->lock);
+ return ret;
+}
+
+static int
+csio_fcoe_free_vnp(struct csio_hw *hw, struct csio_lnode *ln)
+{
+ struct csio_lnode *pln;
+ struct csio_mb *mbp;
+ struct fw_fcoe_vnp_cmd *rsp;
+ int ret = 0;
+ int retry = 0;
+
+ /* Issue VNP cmd to free vport */
+ /* Allocate Mbox request */
+
+ spin_lock_irq(&hw->lock);
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ pln = ln->pln;
+
+ csio_fcoe_vnp_free_init_mb(ln, mbp, CSIO_MB_DEFAULT_TMO,
+ ln->fcf_flowid, ln->vnp_flowid,
+ NULL);
+
+ for (retry = 0; retry < 3; retry++) {
+ ret = csio_mb_issue(hw, mbp);
+ if (ret != -EBUSY)
+ break;
+
+ /* Retry if mbox returns busy */
+ spin_unlock_irq(&hw->lock);
+ msleep(2000);
+ spin_lock_irq(&hw->lock);
+ }
+
+ if (ret) {
+ csio_ln_err(ln, "Failed to issue mbox FCoE VNP command\n");
+ goto out_free;
+ }
+
+ /* Process Mbox response of VNP command */
+ rsp = (struct fw_fcoe_vnp_cmd *)(mbp->mb);
+ if (FW_CMD_RETVAL_GET(ntohl(rsp->alloc_to_len16)) != FW_SUCCESS) {
+ csio_ln_err(ln, "FCOE VNP FREE cmd returned 0x%x!\n",
+ FW_CMD_RETVAL_GET(ntohl(rsp->alloc_to_len16)));
+ ret = -EINVAL;
+ }
+
+out_free:
+ mempool_free(mbp, hw->mb_mempool);
+out:
+ spin_unlock_irq(&hw->lock);
+ return ret;
+}
+
+static int
+csio_vport_create(struct fc_vport *fc_vport, bool disable)
+{
+ struct Scsi_Host *shost = fc_vport->shost;
+ struct csio_lnode *pln = shost_priv(shost);
+ struct csio_lnode *ln = NULL;
+ struct csio_hw *hw = csio_lnode_to_hw(pln);
+ uint8_t wwn[8];
+ int ret = -1;
+
+ ln = csio_shost_init(hw, &fc_vport->dev, false, pln);
+ if (!ln)
+ goto error;
+
+ if (fc_vport->node_name != 0) {
+ u64_to_wwn(fc_vport->node_name, wwn);
+
+ if (!CSIO_VALID_WWN(wwn)) {
+ csio_ln_err(ln,
+ "vport create failed. Invalid wwnn\n");
+ goto error;
+ }
+ memcpy(csio_ln_wwnn(ln), wwn, 8);
+ }
+
+ if (fc_vport->port_name != 0) {
+ u64_to_wwn(fc_vport->port_name, wwn);
+
+ if (!CSIO_VALID_WWN(wwn)) {
+ csio_ln_err(ln,
+ "vport create failed. Invalid wwpn\n");
+ goto error;
+ }
+
+ if (csio_lnode_lookup_by_wwpn(hw, wwn)) {
+ csio_ln_err(ln,
+ "vport create failed. wwpn already exists\n");
+ goto error;
+ }
+ memcpy(csio_ln_wwpn(ln), wwn, 8);
+ }
+
+ fc_vport_set_state(fc_vport, FC_VPORT_INITIALIZING);
+
+ if (csio_fcoe_alloc_vnp(hw, ln))
+ goto error;
+
+ *(struct csio_lnode **)fc_vport->dd_data = ln;
+ ln->fc_vport = fc_vport;
+ if (!fc_vport->node_name)
+ fc_vport->node_name = wwn_to_u64(csio_ln_wwnn(ln));
+ if (!fc_vport->port_name)
+ fc_vport->port_name = wwn_to_u64(csio_ln_wwpn(ln));
+ csio_fchost_attr_init(ln);
+ return 0;
+error:
+ if (ln)
+ csio_shost_exit(ln);
+
+ return ret;
+}
+
+static int
+csio_vport_delete(struct fc_vport *fc_vport)
+{
+ struct csio_lnode *ln = *(struct csio_lnode **)fc_vport->dd_data;
+ struct Scsi_Host *shost = csio_ln_to_shost(ln);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ int rmv;
+
+ spin_lock_irq(&hw->lock);
+ rmv = csio_is_hw_removing(hw);
+ spin_unlock_irq(&hw->lock);
+
+ if (rmv) {
+ csio_shost_exit(ln);
+ return 0;
+ }
+
+ /* Quiesce ios and send remove event to lnode */
+ scsi_block_requests(shost);
+ spin_lock_irq(&hw->lock);
+ csio_scsim_cleanup_io_lnode(csio_hw_to_scsim(hw), ln);
+ csio_lnode_close(ln);
+ spin_unlock_irq(&hw->lock);
+ scsi_unblock_requests(shost);
+
+ /* Free vnp */
+ if (fc_vport->vport_state != FC_VPORT_DISABLED)
+ csio_fcoe_free_vnp(hw, ln);
+
+ csio_shost_exit(ln);
+ return 0;
+}
+
+static int
+csio_vport_disable(struct fc_vport *fc_vport, bool disable)
+{
+ struct csio_lnode *ln = *(struct csio_lnode **)fc_vport->dd_data;
+ struct Scsi_Host *shost = csio_ln_to_shost(ln);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ /* disable vport */
+ if (disable) {
+ /* Quiesce ios and send stop event to lnode */
+ scsi_block_requests(shost);
+ spin_lock_irq(&hw->lock);
+ csio_scsim_cleanup_io_lnode(csio_hw_to_scsim(hw), ln);
+ csio_lnode_stop(ln);
+ spin_unlock_irq(&hw->lock);
+ scsi_unblock_requests(shost);
+
+ /* Free vnp */
+ csio_fcoe_free_vnp(hw, ln);
+ fc_vport_set_state(fc_vport, FC_VPORT_DISABLED);
+ csio_ln_err(ln, "vport disabled\n");
+ return 0;
+ } else {
+ /* enable vport */
+ fc_vport_set_state(fc_vport, FC_VPORT_INITIALIZING);
+ if (csio_fcoe_alloc_vnp(hw, ln)) {
+ csio_ln_err(ln, "vport enabled failed.\n");
+ return -1;
+ }
+ csio_ln_err(ln, "vport enabled\n");
+ return 0;
+ }
+}
+
+static void
+csio_dev_loss_tmo_callbk(struct fc_rport *rport)
+{
+ struct csio_rnode *rn;
+ struct csio_hw *hw;
+ struct csio_lnode *ln;
+
+ rn = *((struct csio_rnode **)rport->dd_data);
+ ln = csio_rnode_to_lnode(rn);
+ hw = csio_lnode_to_hw(ln);
+
+ spin_lock_irq(&hw->lock);
+
+ /* return if driver is being removed or same rnode comes back online */
+ if (csio_is_hw_removing(hw) || csio_is_rnode_ready(rn))
+ goto out;
+
+ csio_ln_dbg(ln, "devloss timeout on rnode:%p portid:x%x flowid:x%x\n",
+ rn, rn->nport_id, csio_rn_flowid(rn));
+
+ CSIO_INC_STATS(ln, n_dev_loss_tmo);
+
+ /*
+ * enqueue devloss event to event worker thread to serialize all
+ * rnode events.
+ */
+ if (csio_enqueue_evt(hw, CSIO_EVT_DEV_LOSS, &rn, sizeof(rn))) {
+ CSIO_INC_STATS(hw, n_evt_drop);
+ goto out;
+ }
+
+ if (!(hw->flags & CSIO_HWF_FWEVT_PENDING)) {
+ hw->flags |= CSIO_HWF_FWEVT_PENDING;
+ spin_unlock_irq(&hw->lock);
+ schedule_work(&hw->evtq_work);
+ return;
+ }
+
+out:
+ spin_unlock_irq(&hw->lock);
+}
+
+/* FC transport functions template - Physical port */
+struct fc_function_template csio_fc_transport_funcs = {
+ .show_host_node_name = 1,
+ .show_host_port_name = 1,
+ .show_host_supported_classes = 1,
+ .show_host_supported_fc4s = 1,
+ .show_host_maxframe_size = 1,
+
+ .get_host_port_id = csio_get_host_port_id,
+ .show_host_port_id = 1,
+
+ .get_host_port_type = csio_get_host_port_type,
+ .show_host_port_type = 1,
+
+ .get_host_port_state = csio_get_host_port_state,
+ .show_host_port_state = 1,
+
+ .show_host_active_fc4s = 1,
+ .get_host_speed = csio_get_host_speed,
+ .show_host_speed = 1,
+ .get_host_fabric_name = csio_get_host_fabric_name,
+ .show_host_fabric_name = 1,
+
+ .get_fc_host_stats = csio_get_stats,
+
+ .dd_fcrport_size = sizeof(struct csio_rnode *),
+ .show_rport_maxframe_size = 1,
+ .show_rport_supported_classes = 1,
+
+ .set_rport_dev_loss_tmo = csio_set_rport_loss_tmo,
+ .show_rport_dev_loss_tmo = 1,
+
+ .show_starget_port_id = 1,
+ .show_starget_node_name = 1,
+ .show_starget_port_name = 1,
+
+ .dev_loss_tmo_callbk = csio_dev_loss_tmo_callbk,
+ .dd_fcvport_size = sizeof(struct csio_lnode *),
+
+ .vport_create = csio_vport_create,
+ .vport_disable = csio_vport_disable,
+ .vport_delete = csio_vport_delete,
+};
+
+/* FC transport functions template - Virtual port */
+struct fc_function_template csio_fc_transport_vport_funcs = {
+ .show_host_node_name = 1,
+ .show_host_port_name = 1,
+ .show_host_supported_classes = 1,
+ .show_host_supported_fc4s = 1,
+ .show_host_maxframe_size = 1,
+
+ .get_host_port_id = csio_get_host_port_id,
+ .show_host_port_id = 1,
+
+ .get_host_port_type = csio_get_host_port_type,
+ .show_host_port_type = 1,
+
+ .get_host_port_state = csio_get_host_port_state,
+ .show_host_port_state = 1,
+ .show_host_active_fc4s = 1,
+
+ .get_host_speed = csio_get_host_speed,
+ .show_host_speed = 1,
+
+ .get_host_fabric_name = csio_get_host_fabric_name,
+ .show_host_fabric_name = 1,
+
+ .get_fc_host_stats = csio_get_stats,
+
+ .dd_fcrport_size = sizeof(struct csio_rnode *),
+ .show_rport_maxframe_size = 1,
+ .show_rport_supported_classes = 1,
+
+ .set_rport_dev_loss_tmo = csio_set_rport_loss_tmo,
+ .show_rport_dev_loss_tmo = 1,
+
+ .show_starget_port_id = 1,
+ .show_starget_node_name = 1,
+ .show_starget_port_name = 1,
+
+ .dev_loss_tmo_callbk = csio_dev_loss_tmo_callbk,
+
+};
diff --git a/drivers/scsi/csiostor/csio_defs.h b/drivers/scsi/csiostor/csio_defs.h
new file mode 100644
index 00000000000..c38017b4af9
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_defs.h
@@ -0,0 +1,121 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CSIO_DEFS_H__
+#define __CSIO_DEFS_H__
+
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/bug.h>
+#include <linux/pci.h>
+#include <linux/jiffies.h>
+
+#define CSIO_INVALID_IDX 0xFFFFFFFF
+#define CSIO_INC_STATS(elem, val) ((elem)->stats.val++)
+#define CSIO_DEC_STATS(elem, val) ((elem)->stats.val--)
+#define CSIO_VALID_WWN(__n) ((*__n >> 4) == 0x5 ? true : false)
+#define CSIO_DID_MASK 0xFFFFFF
+#define CSIO_WORD_TO_BYTE 4
+
+#ifndef readq
+static inline u64 readq(void __iomem *addr)
+{
+ return readl(addr) + ((u64)readl(addr + 4) << 32);
+}
+
+static inline void writeq(u64 val, void __iomem *addr)
+{
+ writel(val, addr);
+ writel(val >> 32, addr + 4);
+}
+#endif
+
+static inline int
+csio_list_deleted(struct list_head *list)
+{
+ return ((list->next == list) && (list->prev == list));
+}
+
+#define csio_list_next(elem) (((struct list_head *)(elem))->next)
+#define csio_list_prev(elem) (((struct list_head *)(elem))->prev)
+
+/* State machine */
+typedef void (*csio_sm_state_t)(void *, uint32_t);
+
+struct csio_sm {
+ struct list_head sm_list;
+ csio_sm_state_t sm_state;
+};
+
+static inline void
+csio_set_state(void *smp, void *state)
+{
+ ((struct csio_sm *)smp)->sm_state = (csio_sm_state_t)state;
+}
+
+static inline void
+csio_init_state(struct csio_sm *smp, void *state)
+{
+ csio_set_state(smp, state);
+}
+
+static inline void
+csio_post_event(void *smp, uint32_t evt)
+{
+ ((struct csio_sm *)smp)->sm_state(smp, evt);
+}
+
+static inline csio_sm_state_t
+csio_get_state(void *smp)
+{
+ return ((struct csio_sm *)smp)->sm_state;
+}
+
+static inline bool
+csio_match_state(void *smp, void *state)
+{
+ return (csio_get_state(smp) == (csio_sm_state_t)state);
+}
+
+#define CSIO_ASSERT(cond) BUG_ON(!(cond))
+
+#ifdef __CSIO_DEBUG__
+#define CSIO_DB_ASSERT(__c) CSIO_ASSERT((__c))
+#else
+#define CSIO_DB_ASSERT(__c)
+#endif
+
+#endif /* ifndef __CSIO_DEFS_H__ */
diff --git a/drivers/scsi/csiostor/csio_hw.c b/drivers/scsi/csiostor/csio_hw.c
new file mode 100644
index 00000000000..8ecdb94a59f
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_hw.c
@@ -0,0 +1,4395 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/pci.h>
+#include <linux/pci_regs.h>
+#include <linux/firmware.h>
+#include <linux/stddef.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/compiler.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+
+#include "csio_hw.h"
+#include "csio_lnode.h"
+#include "csio_rnode.h"
+
+int csio_force_master;
+int csio_dbg_level = 0xFEFF;
+unsigned int csio_port_mask = 0xf;
+
+/* Default FW event queue entries. */
+static uint32_t csio_evtq_sz = CSIO_EVTQ_SIZE;
+
+/* Default MSI param level */
+int csio_msi = 2;
+
+/* FCoE function instances */
+static int dev_num;
+
+/* FCoE Adapter types & its description */
+static const struct csio_adap_desc csio_fcoe_adapters[] = {
+ {"T440-Dbg 10G", "Chelsio T440-Dbg 10G [FCoE]"},
+ {"T420-CR 10G", "Chelsio T420-CR 10G [FCoE]"},
+ {"T422-CR 10G/1G", "Chelsio T422-CR 10G/1G [FCoE]"},
+ {"T440-CR 10G", "Chelsio T440-CR 10G [FCoE]"},
+ {"T420-BCH 10G", "Chelsio T420-BCH 10G [FCoE]"},
+ {"T440-BCH 10G", "Chelsio T440-BCH 10G [FCoE]"},
+ {"T440-CH 10G", "Chelsio T440-CH 10G [FCoE]"},
+ {"T420-SO 10G", "Chelsio T420-SO 10G [FCoE]"},
+ {"T420-CX4 10G", "Chelsio T420-CX4 10G [FCoE]"},
+ {"T420-BT 10G", "Chelsio T420-BT 10G [FCoE]"},
+ {"T404-BT 1G", "Chelsio T404-BT 1G [FCoE]"},
+ {"B420-SR 10G", "Chelsio B420-SR 10G [FCoE]"},
+ {"B404-BT 1G", "Chelsio B404-BT 1G [FCoE]"},
+ {"T480-CR 10G", "Chelsio T480-CR 10G [FCoE]"},
+ {"T440-LP-CR 10G", "Chelsio T440-LP-CR 10G [FCoE]"},
+ {"T4 FPGA", "Chelsio T4 FPGA [FCoE]"}
+};
+
+static void csio_mgmtm_cleanup(struct csio_mgmtm *);
+static void csio_hw_mbm_cleanup(struct csio_hw *);
+
+/* State machine forward declarations */
+static void csio_hws_uninit(struct csio_hw *, enum csio_hw_ev);
+static void csio_hws_configuring(struct csio_hw *, enum csio_hw_ev);
+static void csio_hws_initializing(struct csio_hw *, enum csio_hw_ev);
+static void csio_hws_ready(struct csio_hw *, enum csio_hw_ev);
+static void csio_hws_quiescing(struct csio_hw *, enum csio_hw_ev);
+static void csio_hws_quiesced(struct csio_hw *, enum csio_hw_ev);
+static void csio_hws_resetting(struct csio_hw *, enum csio_hw_ev);
+static void csio_hws_removing(struct csio_hw *, enum csio_hw_ev);
+static void csio_hws_pcierr(struct csio_hw *, enum csio_hw_ev);
+
+static void csio_hw_initialize(struct csio_hw *hw);
+static void csio_evtq_stop(struct csio_hw *hw);
+static void csio_evtq_start(struct csio_hw *hw);
+
+int csio_is_hw_ready(struct csio_hw *hw)
+{
+ return csio_match_state(hw, csio_hws_ready);
+}
+
+int csio_is_hw_removing(struct csio_hw *hw)
+{
+ return csio_match_state(hw, csio_hws_removing);
+}
+
+
+/*
+ * csio_hw_wait_op_done_val - wait until an operation is completed
+ * @hw: the HW module
+ * @reg: the register to check for completion
+ * @mask: a single-bit field within @reg that indicates completion
+ * @polarity: the value of the field when the operation is completed
+ * @attempts: number of check iterations
+ * @delay: delay in usecs between iterations
+ * @valp: where to store the value of the register at completion time
+ *
+ * Wait until an operation is completed by checking a bit in a register
+ * up to @attempts times. If @valp is not NULL the value of the register
+ * at the time it indicated completion is stored there. Returns 0 if the
+ * operation completes and -EAGAIN otherwise.
+ */
+static int
+csio_hw_wait_op_done_val(struct csio_hw *hw, int reg, uint32_t mask,
+ int polarity, int attempts, int delay, uint32_t *valp)
+{
+ uint32_t val;
+ while (1) {
+ val = csio_rd_reg32(hw, reg);
+
+ if (!!(val & mask) == polarity) {
+ if (valp)
+ *valp = val;
+ return 0;
+ }
+
+ if (--attempts == 0)
+ return -EAGAIN;
+ if (delay)
+ udelay(delay);
+ }
+}
+
+void
+csio_set_reg_field(struct csio_hw *hw, uint32_t reg, uint32_t mask,
+ uint32_t value)
+{
+ uint32_t val = csio_rd_reg32(hw, reg) & ~mask;
+
+ csio_wr_reg32(hw, val | value, reg);
+ /* Flush */
+ csio_rd_reg32(hw, reg);
+
+}
+
+/*
+ * csio_hw_mc_read - read from MC through backdoor accesses
+ * @hw: the hw module
+ * @addr: address of first byte requested
+ * @data: 64 bytes of data containing the requested address
+ * @ecc: where to store the corresponding 64-bit ECC word
+ *
+ * Read 64 bytes of data from MC starting at a 64-byte-aligned address
+ * that covers the requested address @addr. If @parity is not %NULL it
+ * is assigned the 64-bit ECC word for the read data.
+ */
+int
+csio_hw_mc_read(struct csio_hw *hw, uint32_t addr, __be32 *data,
+ uint64_t *ecc)
+{
+ int i;
+
+ if (csio_rd_reg32(hw, MC_BIST_CMD) & START_BIST)
+ return -EBUSY;
+ csio_wr_reg32(hw, addr & ~0x3fU, MC_BIST_CMD_ADDR);
+ csio_wr_reg32(hw, 64, MC_BIST_CMD_LEN);
+ csio_wr_reg32(hw, 0xc, MC_BIST_DATA_PATTERN);
+ csio_wr_reg32(hw, BIST_OPCODE(1) | START_BIST | BIST_CMD_GAP(1),
+ MC_BIST_CMD);
+ i = csio_hw_wait_op_done_val(hw, MC_BIST_CMD, START_BIST,
+ 0, 10, 1, NULL);
+ if (i)
+ return i;
+
+#define MC_DATA(i) MC_BIST_STATUS_REG(MC_BIST_STATUS_RDATA, i)
+
+ for (i = 15; i >= 0; i--)
+ *data++ = htonl(csio_rd_reg32(hw, MC_DATA(i)));
+ if (ecc)
+ *ecc = csio_rd_reg64(hw, MC_DATA(16));
+#undef MC_DATA
+ return 0;
+}
+
+/*
+ * csio_hw_edc_read - read from EDC through backdoor accesses
+ * @hw: the hw module
+ * @idx: which EDC to access
+ * @addr: address of first byte requested
+ * @data: 64 bytes of data containing the requested address
+ * @ecc: where to store the corresponding 64-bit ECC word
+ *
+ * Read 64 bytes of data from EDC starting at a 64-byte-aligned address
+ * that covers the requested address @addr. If @parity is not %NULL it
+ * is assigned the 64-bit ECC word for the read data.
+ */
+int
+csio_hw_edc_read(struct csio_hw *hw, int idx, uint32_t addr, __be32 *data,
+ uint64_t *ecc)
+{
+ int i;
+
+ idx *= EDC_STRIDE;
+ if (csio_rd_reg32(hw, EDC_BIST_CMD + idx) & START_BIST)
+ return -EBUSY;
+ csio_wr_reg32(hw, addr & ~0x3fU, EDC_BIST_CMD_ADDR + idx);
+ csio_wr_reg32(hw, 64, EDC_BIST_CMD_LEN + idx);
+ csio_wr_reg32(hw, 0xc, EDC_BIST_DATA_PATTERN + idx);
+ csio_wr_reg32(hw, BIST_OPCODE(1) | BIST_CMD_GAP(1) | START_BIST,
+ EDC_BIST_CMD + idx);
+ i = csio_hw_wait_op_done_val(hw, EDC_BIST_CMD + idx, START_BIST,
+ 0, 10, 1, NULL);
+ if (i)
+ return i;
+
+#define EDC_DATA(i) (EDC_BIST_STATUS_REG(EDC_BIST_STATUS_RDATA, i) + idx)
+
+ for (i = 15; i >= 0; i--)
+ *data++ = htonl(csio_rd_reg32(hw, EDC_DATA(i)));
+ if (ecc)
+ *ecc = csio_rd_reg64(hw, EDC_DATA(16));
+#undef EDC_DATA
+ return 0;
+}
+
+/*
+ * csio_mem_win_rw - read/write memory through PCIE memory window
+ * @hw: the adapter
+ * @addr: address of first byte requested
+ * @data: MEMWIN0_APERTURE bytes of data containing the requested address
+ * @dir: direction of transfer 1 => read, 0 => write
+ *
+ * Read/write MEMWIN0_APERTURE bytes of data from MC starting at a
+ * MEMWIN0_APERTURE-byte-aligned address that covers the requested
+ * address @addr.
+ */
+static int
+csio_mem_win_rw(struct csio_hw *hw, u32 addr, u32 *data, int dir)
+{
+ int i;
+
+ /*
+ * Setup offset into PCIE memory window. Address must be a
+ * MEMWIN0_APERTURE-byte-aligned address. (Read back MA register to
+ * ensure that changes propagate before we attempt to use the new
+ * values.)
+ */
+ csio_wr_reg32(hw, addr & ~(MEMWIN0_APERTURE - 1),
+ PCIE_MEM_ACCESS_OFFSET);
+ csio_rd_reg32(hw, PCIE_MEM_ACCESS_OFFSET);
+
+ /* Collecting data 4 bytes at a time upto MEMWIN0_APERTURE */
+ for (i = 0; i < MEMWIN0_APERTURE; i = i + sizeof(__be32)) {
+ if (dir)
+ *data++ = csio_rd_reg32(hw, (MEMWIN0_BASE + i));
+ else
+ csio_wr_reg32(hw, *data++, (MEMWIN0_BASE + i));
+ }
+
+ return 0;
+}
+
+/*
+ * csio_memory_rw - read/write EDC 0, EDC 1 or MC via PCIE memory window
+ * @hw: the csio_hw
+ * @mtype: memory type: MEM_EDC0, MEM_EDC1 or MEM_MC
+ * @addr: address within indicated memory type
+ * @len: amount of memory to transfer
+ * @buf: host memory buffer
+ * @dir: direction of transfer 1 => read, 0 => write
+ *
+ * Reads/writes an [almost] arbitrary memory region in the firmware: the
+ * firmware memory address, length and host buffer must be aligned on
+ * 32-bit boudaries. The memory is transferred as a raw byte sequence
+ * from/to the firmware's memory. If this memory contains data
+ * structures which contain multi-byte integers, it's the callers
+ * responsibility to perform appropriate byte order conversions.
+ */
+static int
+csio_memory_rw(struct csio_hw *hw, int mtype, u32 addr, u32 len,
+ uint32_t *buf, int dir)
+{
+ uint32_t pos, start, end, offset, memoffset;
+ int ret;
+ uint32_t *data;
+
+ /*
+ * Argument sanity checks ...
+ */
+ if ((addr & 0x3) || (len & 0x3))
+ return -EINVAL;
+
+ data = kzalloc(MEMWIN0_APERTURE, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* Offset into the region of memory which is being accessed
+ * MEM_EDC0 = 0
+ * MEM_EDC1 = 1
+ * MEM_MC = 2
+ */
+ memoffset = (mtype * (5 * 1024 * 1024));
+
+ /* Determine the PCIE_MEM_ACCESS_OFFSET */
+ addr = addr + memoffset;
+
+ /*
+ * The underlaying EDC/MC read routines read MEMWIN0_APERTURE bytes
+ * at a time so we need to round down the start and round up the end.
+ * We'll start copying out of the first line at (addr - start) a word
+ * at a time.
+ */
+ start = addr & ~(MEMWIN0_APERTURE-1);
+ end = (addr + len + MEMWIN0_APERTURE-1) & ~(MEMWIN0_APERTURE-1);
+ offset = (addr - start)/sizeof(__be32);
+
+ for (pos = start; pos < end; pos += MEMWIN0_APERTURE, offset = 0) {
+ /*
+ * If we're writing, copy the data from the caller's memory
+ * buffer
+ */
+ if (!dir) {
+ /*
+ * If we're doing a partial write, then we need to do
+ * a read-modify-write ...
+ */
+ if (offset || len < MEMWIN0_APERTURE) {
+ ret = csio_mem_win_rw(hw, pos, data, 1);
+ if (ret) {
+ kfree(data);
+ return ret;
+ }
+ }
+ while (offset < (MEMWIN0_APERTURE/sizeof(__be32)) &&
+ len > 0) {
+ data[offset++] = *buf++;
+ len -= sizeof(__be32);
+ }
+ }
+
+ /*
+ * Transfer a block of memory and bail if there's an error.
+ */
+ ret = csio_mem_win_rw(hw, pos, data, dir);
+ if (ret) {
+ kfree(data);
+ return ret;
+ }
+
+ /*
+ * If we're reading, copy the data into the caller's memory
+ * buffer.
+ */
+ if (dir)
+ while (offset < (MEMWIN0_APERTURE/sizeof(__be32)) &&
+ len > 0) {
+ *buf++ = data[offset++];
+ len -= sizeof(__be32);
+ }
+ }
+
+ kfree(data);
+
+ return 0;
+}
+
+static int
+csio_memory_write(struct csio_hw *hw, int mtype, u32 addr, u32 len, u32 *buf)
+{
+ return csio_memory_rw(hw, mtype, addr, len, buf, 0);
+}
+
+/*
+ * EEPROM reads take a few tens of us while writes can take a bit over 5 ms.
+ */
+#define EEPROM_MAX_RD_POLL 40
+#define EEPROM_MAX_WR_POLL 6
+#define EEPROM_STAT_ADDR 0x7bfc
+#define VPD_BASE 0x400
+#define VPD_BASE_OLD 0
+#define VPD_LEN 512
+#define VPD_INFO_FLD_HDR_SIZE 3
+
+/*
+ * csio_hw_seeprom_read - read a serial EEPROM location
+ * @hw: hw to read
+ * @addr: EEPROM virtual address
+ * @data: where to store the read data
+ *
+ * Read a 32-bit word from a location in serial EEPROM using the card's PCI
+ * VPD capability. Note that this function must be called with a virtual
+ * address.
+ */
+static int
+csio_hw_seeprom_read(struct csio_hw *hw, uint32_t addr, uint32_t *data)
+{
+ uint16_t val = 0;
+ int attempts = EEPROM_MAX_RD_POLL;
+ uint32_t base = hw->params.pci.vpd_cap_addr;
+
+ if (addr >= EEPROMVSIZE || (addr & 3))
+ return -EINVAL;
+
+ pci_write_config_word(hw->pdev, base + PCI_VPD_ADDR, (uint16_t)addr);
+
+ do {
+ udelay(10);
+ pci_read_config_word(hw->pdev, base + PCI_VPD_ADDR, &val);
+ } while (!(val & PCI_VPD_ADDR_F) && --attempts);
+
+ if (!(val & PCI_VPD_ADDR_F)) {
+ csio_err(hw, "reading EEPROM address 0x%x failed\n", addr);
+ return -EINVAL;
+ }
+
+ pci_read_config_dword(hw->pdev, base + PCI_VPD_DATA, data);
+ *data = le32_to_cpu(*data);
+
+ return 0;
+}
+
+/*
+ * Partial EEPROM Vital Product Data structure. Includes only the ID and
+ * VPD-R sections.
+ */
+struct t4_vpd_hdr {
+ u8 id_tag;
+ u8 id_len[2];
+ u8 id_data[ID_LEN];
+ u8 vpdr_tag;
+ u8 vpdr_len[2];
+};
+
+/*
+ * csio_hw_get_vpd_keyword_val - Locates an information field keyword in
+ * the VPD
+ * @v: Pointer to buffered vpd data structure
+ * @kw: The keyword to search for
+ *
+ * Returns the value of the information field keyword or
+ * -EINVAL otherwise.
+ */
+static int
+csio_hw_get_vpd_keyword_val(const struct t4_vpd_hdr *v, const char *kw)
+{
+ int32_t i;
+ int32_t offset , len;
+ const uint8_t *buf = &v->id_tag;
+ const uint8_t *vpdr_len = &v->vpdr_tag;
+ offset = sizeof(struct t4_vpd_hdr);
+ len = (uint16_t)vpdr_len[1] + ((uint16_t)vpdr_len[2] << 8);
+
+ if (len + sizeof(struct t4_vpd_hdr) > VPD_LEN)
+ return -EINVAL;
+
+ for (i = offset; (i + VPD_INFO_FLD_HDR_SIZE) <= (offset + len);) {
+ if (memcmp(buf + i , kw, 2) == 0) {
+ i += VPD_INFO_FLD_HDR_SIZE;
+ return i;
+ }
+
+ i += VPD_INFO_FLD_HDR_SIZE + buf[i+2];
+ }
+
+ return -EINVAL;
+}
+
+static int
+csio_pci_capability(struct pci_dev *pdev, int cap, int *pos)
+{
+ *pos = pci_find_capability(pdev, cap);
+ if (*pos)
+ return 0;
+
+ return -1;
+}
+
+/*
+ * csio_hw_get_vpd_params - read VPD parameters from VPD EEPROM
+ * @hw: HW module
+ * @p: where to store the parameters
+ *
+ * Reads card parameters stored in VPD EEPROM.
+ */
+static int
+csio_hw_get_vpd_params(struct csio_hw *hw, struct csio_vpd *p)
+{
+ int i, ret, ec, sn, addr;
+ uint8_t *vpd, csum;
+ const struct t4_vpd_hdr *v;
+ /* To get around compilation warning from strstrip */
+ char *s;
+
+ if (csio_is_valid_vpd(hw))
+ return 0;
+
+ ret = csio_pci_capability(hw->pdev, PCI_CAP_ID_VPD,
+ &hw->params.pci.vpd_cap_addr);
+ if (ret)
+ return -EINVAL;
+
+ vpd = kzalloc(VPD_LEN, GFP_ATOMIC);
+ if (vpd == NULL)
+ return -ENOMEM;
+
+ /*
+ * Card information normally starts at VPD_BASE but early cards had
+ * it at 0.
+ */
+ ret = csio_hw_seeprom_read(hw, VPD_BASE, (uint32_t *)(vpd));
+ addr = *vpd == 0x82 ? VPD_BASE : VPD_BASE_OLD;
+
+ for (i = 0; i < VPD_LEN; i += 4) {
+ ret = csio_hw_seeprom_read(hw, addr + i, (uint32_t *)(vpd + i));
+ if (ret) {
+ kfree(vpd);
+ return ret;
+ }
+ }
+
+ /* Reset the VPD flag! */
+ hw->flags &= (~CSIO_HWF_VPD_VALID);
+
+ v = (const struct t4_vpd_hdr *)vpd;
+
+#define FIND_VPD_KW(var, name) do { \
+ var = csio_hw_get_vpd_keyword_val(v, name); \
+ if (var < 0) { \
+ csio_err(hw, "missing VPD keyword " name "\n"); \
+ kfree(vpd); \
+ return -EINVAL; \
+ } \
+} while (0)
+
+ FIND_VPD_KW(i, "RV");
+ for (csum = 0; i >= 0; i--)
+ csum += vpd[i];
+
+ if (csum) {
+ csio_err(hw, "corrupted VPD EEPROM, actual csum %u\n", csum);
+ kfree(vpd);
+ return -EINVAL;
+ }
+ FIND_VPD_KW(ec, "EC");
+ FIND_VPD_KW(sn, "SN");
+#undef FIND_VPD_KW
+
+ memcpy(p->id, v->id_data, ID_LEN);
+ s = strstrip(p->id);
+ memcpy(p->ec, vpd + ec, EC_LEN);
+ s = strstrip(p->ec);
+ i = vpd[sn - VPD_INFO_FLD_HDR_SIZE + 2];
+ memcpy(p->sn, vpd + sn, min(i, SERNUM_LEN));
+ s = strstrip(p->sn);
+
+ csio_valid_vpd_copied(hw);
+
+ kfree(vpd);
+ return 0;
+}
+
+/*
+ * csio_hw_sf1_read - read data from the serial flash
+ * @hw: the HW module
+ * @byte_cnt: number of bytes to read
+ * @cont: whether another operation will be chained
+ * @lock: whether to lock SF for PL access only
+ * @valp: where to store the read data
+ *
+ * Reads up to 4 bytes of data from the serial flash. The location of
+ * the read needs to be specified prior to calling this by issuing the
+ * appropriate commands to the serial flash.
+ */
+static int
+csio_hw_sf1_read(struct csio_hw *hw, uint32_t byte_cnt, int32_t cont,
+ int32_t lock, uint32_t *valp)
+{
+ int ret;
+
+ if (!byte_cnt || byte_cnt > 4)
+ return -EINVAL;
+ if (csio_rd_reg32(hw, SF_OP) & SF_BUSY)
+ return -EBUSY;
+
+ cont = cont ? SF_CONT : 0;
+ lock = lock ? SF_LOCK : 0;
+
+ csio_wr_reg32(hw, lock | cont | BYTECNT(byte_cnt - 1), SF_OP);
+ ret = csio_hw_wait_op_done_val(hw, SF_OP, SF_BUSY, 0, SF_ATTEMPTS,
+ 10, NULL);
+ if (!ret)
+ *valp = csio_rd_reg32(hw, SF_DATA);
+ return ret;
+}
+
+/*
+ * csio_hw_sf1_write - write data to the serial flash
+ * @hw: the HW module
+ * @byte_cnt: number of bytes to write
+ * @cont: whether another operation will be chained
+ * @lock: whether to lock SF for PL access only
+ * @val: value to write
+ *
+ * Writes up to 4 bytes of data to the serial flash. The location of
+ * the write needs to be specified prior to calling this by issuing the
+ * appropriate commands to the serial flash.
+ */
+static int
+csio_hw_sf1_write(struct csio_hw *hw, uint32_t byte_cnt, uint32_t cont,
+ int32_t lock, uint32_t val)
+{
+ if (!byte_cnt || byte_cnt > 4)
+ return -EINVAL;
+ if (csio_rd_reg32(hw, SF_OP) & SF_BUSY)
+ return -EBUSY;
+
+ cont = cont ? SF_CONT : 0;
+ lock = lock ? SF_LOCK : 0;
+
+ csio_wr_reg32(hw, val, SF_DATA);
+ csio_wr_reg32(hw, cont | BYTECNT(byte_cnt - 1) | OP_WR | lock, SF_OP);
+
+ return csio_hw_wait_op_done_val(hw, SF_OP, SF_BUSY, 0, SF_ATTEMPTS,
+ 10, NULL);
+}
+
+/*
+ * csio_hw_flash_wait_op - wait for a flash operation to complete
+ * @hw: the HW module
+ * @attempts: max number of polls of the status register
+ * @delay: delay between polls in ms
+ *
+ * Wait for a flash operation to complete by polling the status register.
+ */
+static int
+csio_hw_flash_wait_op(struct csio_hw *hw, int32_t attempts, int32_t delay)
+{
+ int ret;
+ uint32_t status;
+
+ while (1) {
+ ret = csio_hw_sf1_write(hw, 1, 1, 1, SF_RD_STATUS);
+ if (ret != 0)
+ return ret;
+
+ ret = csio_hw_sf1_read(hw, 1, 0, 1, &status);
+ if (ret != 0)
+ return ret;
+
+ if (!(status & 1))
+ return 0;
+ if (--attempts == 0)
+ return -EAGAIN;
+ if (delay)
+ msleep(delay);
+ }
+}
+
+/*
+ * csio_hw_read_flash - read words from serial flash
+ * @hw: the HW module
+ * @addr: the start address for the read
+ * @nwords: how many 32-bit words to read
+ * @data: where to store the read data
+ * @byte_oriented: whether to store data as bytes or as words
+ *
+ * Read the specified number of 32-bit words from the serial flash.
+ * If @byte_oriented is set the read data is stored as a byte array
+ * (i.e., big-endian), otherwise as 32-bit words in the platform's
+ * natural endianess.
+ */
+static int
+csio_hw_read_flash(struct csio_hw *hw, uint32_t addr, uint32_t nwords,
+ uint32_t *data, int32_t byte_oriented)
+{
+ int ret;
+
+ if (addr + nwords * sizeof(uint32_t) > hw->params.sf_size || (addr & 3))
+ return -EINVAL;
+
+ addr = swab32(addr) | SF_RD_DATA_FAST;
+
+ ret = csio_hw_sf1_write(hw, 4, 1, 0, addr);
+ if (ret != 0)
+ return ret;
+
+ ret = csio_hw_sf1_read(hw, 1, 1, 0, data);
+ if (ret != 0)
+ return ret;
+
+ for ( ; nwords; nwords--, data++) {
+ ret = csio_hw_sf1_read(hw, 4, nwords > 1, nwords == 1, data);
+ if (nwords == 1)
+ csio_wr_reg32(hw, 0, SF_OP); /* unlock SF */
+ if (ret)
+ return ret;
+ if (byte_oriented)
+ *data = htonl(*data);
+ }
+ return 0;
+}
+
+/*
+ * csio_hw_write_flash - write up to a page of data to the serial flash
+ * @hw: the hw
+ * @addr: the start address to write
+ * @n: length of data to write in bytes
+ * @data: the data to write
+ *
+ * Writes up to a page of data (256 bytes) to the serial flash starting
+ * at the given address. All the data must be written to the same page.
+ */
+static int
+csio_hw_write_flash(struct csio_hw *hw, uint32_t addr,
+ uint32_t n, const uint8_t *data)
+{
+ int ret = -EINVAL;
+ uint32_t buf[64];
+ uint32_t i, c, left, val, offset = addr & 0xff;
+
+ if (addr >= hw->params.sf_size || offset + n > SF_PAGE_SIZE)
+ return -EINVAL;
+
+ val = swab32(addr) | SF_PROG_PAGE;
+
+ ret = csio_hw_sf1_write(hw, 1, 0, 1, SF_WR_ENABLE);
+ if (ret != 0)
+ goto unlock;
+
+ ret = csio_hw_sf1_write(hw, 4, 1, 1, val);
+ if (ret != 0)
+ goto unlock;
+
+ for (left = n; left; left -= c) {
+ c = min(left, 4U);
+ for (val = 0, i = 0; i < c; ++i)
+ val = (val << 8) + *data++;
+
+ ret = csio_hw_sf1_write(hw, c, c != left, 1, val);
+ if (ret)
+ goto unlock;
+ }
+ ret = csio_hw_flash_wait_op(hw, 8, 1);
+ if (ret)
+ goto unlock;
+
+ csio_wr_reg32(hw, 0, SF_OP); /* unlock SF */
+
+ /* Read the page to verify the write succeeded */
+ ret = csio_hw_read_flash(hw, addr & ~0xff, ARRAY_SIZE(buf), buf, 1);
+ if (ret)
+ return ret;
+
+ if (memcmp(data - n, (uint8_t *)buf + offset, n)) {
+ csio_err(hw,
+ "failed to correctly write the flash page at %#x\n",
+ addr);
+ return -EINVAL;
+ }
+
+ return 0;
+
+unlock:
+ csio_wr_reg32(hw, 0, SF_OP); /* unlock SF */
+ return ret;
+}
+
+/*
+ * csio_hw_flash_erase_sectors - erase a range of flash sectors
+ * @hw: the HW module
+ * @start: the first sector to erase
+ * @end: the last sector to erase
+ *
+ * Erases the sectors in the given inclusive range.
+ */
+static int
+csio_hw_flash_erase_sectors(struct csio_hw *hw, int32_t start, int32_t end)
+{
+ int ret = 0;
+
+ while (start <= end) {
+
+ ret = csio_hw_sf1_write(hw, 1, 0, 1, SF_WR_ENABLE);
+ if (ret != 0)
+ goto out;
+
+ ret = csio_hw_sf1_write(hw, 4, 0, 1,
+ SF_ERASE_SECTOR | (start << 8));
+ if (ret != 0)
+ goto out;
+
+ ret = csio_hw_flash_wait_op(hw, 14, 500);
+ if (ret != 0)
+ goto out;
+
+ start++;
+ }
+out:
+ if (ret)
+ csio_err(hw, "erase of flash sector %d failed, error %d\n",
+ start, ret);
+ csio_wr_reg32(hw, 0, SF_OP); /* unlock SF */
+ return 0;
+}
+
+/*
+ * csio_hw_flash_cfg_addr - return the address of the flash
+ * configuration file
+ * @hw: the HW module
+ *
+ * Return the address within the flash where the Firmware Configuration
+ * File is stored.
+ */
+static unsigned int
+csio_hw_flash_cfg_addr(struct csio_hw *hw)
+{
+ if (hw->params.sf_size == 0x100000)
+ return FPGA_FLASH_CFG_OFFSET;
+ else
+ return FLASH_CFG_OFFSET;
+}
+
+static void
+csio_hw_print_fw_version(struct csio_hw *hw, char *str)
+{
+ csio_info(hw, "%s: %u.%u.%u.%u\n", str,
+ FW_HDR_FW_VER_MAJOR_GET(hw->fwrev),
+ FW_HDR_FW_VER_MINOR_GET(hw->fwrev),
+ FW_HDR_FW_VER_MICRO_GET(hw->fwrev),
+ FW_HDR_FW_VER_BUILD_GET(hw->fwrev));
+}
+
+/*
+ * csio_hw_get_fw_version - read the firmware version
+ * @hw: HW module
+ * @vers: where to place the version
+ *
+ * Reads the FW version from flash.
+ */
+static int
+csio_hw_get_fw_version(struct csio_hw *hw, uint32_t *vers)
+{
+ return csio_hw_read_flash(hw, FW_IMG_START +
+ offsetof(struct fw_hdr, fw_ver), 1,
+ vers, 0);
+}
+
+/*
+ * csio_hw_get_tp_version - read the TP microcode version
+ * @hw: HW module
+ * @vers: where to place the version
+ *
+ * Reads the TP microcode version from flash.
+ */
+static int
+csio_hw_get_tp_version(struct csio_hw *hw, u32 *vers)
+{
+ return csio_hw_read_flash(hw, FLASH_FW_START +
+ offsetof(struct fw_hdr, tp_microcode_ver), 1,
+ vers, 0);
+}
+
+/*
+ * csio_hw_check_fw_version - check if the FW is compatible with
+ * this driver
+ * @hw: HW module
+ *
+ * Checks if an adapter's FW is compatible with the driver. Returns 0
+ * if there's exact match, a negative error if the version could not be
+ * read or there's a major/minor version mismatch/minor.
+ */
+static int
+csio_hw_check_fw_version(struct csio_hw *hw)
+{
+ int ret, major, minor, micro;
+
+ ret = csio_hw_get_fw_version(hw, &hw->fwrev);
+ if (!ret)
+ ret = csio_hw_get_tp_version(hw, &hw->tp_vers);
+ if (ret)
+ return ret;
+
+ major = FW_HDR_FW_VER_MAJOR_GET(hw->fwrev);
+ minor = FW_HDR_FW_VER_MINOR_GET(hw->fwrev);
+ micro = FW_HDR_FW_VER_MICRO_GET(hw->fwrev);
+
+ if (major != FW_VERSION_MAJOR) { /* major mismatch - fail */
+ csio_err(hw, "card FW has major version %u, driver wants %u\n",
+ major, FW_VERSION_MAJOR);
+ return -EINVAL;
+ }
+
+ if (minor == FW_VERSION_MINOR && micro == FW_VERSION_MICRO)
+ return 0; /* perfect match */
+
+ /* Minor/micro version mismatch */
+ return -EINVAL;
+}
+
+/*
+ * csio_hw_fw_dload - download firmware.
+ * @hw: HW module
+ * @fw_data: firmware image to write.
+ * @size: image size
+ *
+ * Write the supplied firmware image to the card's serial flash.
+ */
+static int
+csio_hw_fw_dload(struct csio_hw *hw, uint8_t *fw_data, uint32_t size)
+{
+ uint32_t csum;
+ int32_t addr;
+ int ret;
+ uint32_t i;
+ uint8_t first_page[SF_PAGE_SIZE];
+ const __be32 *p = (const __be32 *)fw_data;
+ struct fw_hdr *hdr = (struct fw_hdr *)fw_data;
+ uint32_t sf_sec_size;
+
+ if ((!hw->params.sf_size) || (!hw->params.sf_nsec)) {
+ csio_err(hw, "Serial Flash data invalid\n");
+ return -EINVAL;
+ }
+
+ if (!size) {
+ csio_err(hw, "FW image has no data\n");
+ return -EINVAL;
+ }
+
+ if (size & 511) {
+ csio_err(hw, "FW image size not multiple of 512 bytes\n");
+ return -EINVAL;
+ }
+
+ if (ntohs(hdr->len512) * 512 != size) {
+ csio_err(hw, "FW image size differs from size in FW header\n");
+ return -EINVAL;
+ }
+
+ if (size > FW_MAX_SIZE) {
+ csio_err(hw, "FW image too large, max is %u bytes\n",
+ FW_MAX_SIZE);
+ return -EINVAL;
+ }
+
+ for (csum = 0, i = 0; i < size / sizeof(csum); i++)
+ csum += ntohl(p[i]);
+
+ if (csum != 0xffffffff) {
+ csio_err(hw, "corrupted firmware image, checksum %#x\n", csum);
+ return -EINVAL;
+ }
+
+ sf_sec_size = hw->params.sf_size / hw->params.sf_nsec;
+ i = DIV_ROUND_UP(size, sf_sec_size); /* # of sectors spanned */
+
+ csio_dbg(hw, "Erasing sectors... start:%d end:%d\n",
+ FW_START_SEC, FW_START_SEC + i - 1);
+
+ ret = csio_hw_flash_erase_sectors(hw, FW_START_SEC,
+ FW_START_SEC + i - 1);
+ if (ret) {
+ csio_err(hw, "Flash Erase failed\n");
+ goto out;
+ }
+
+ /*
+ * We write the correct version at the end so the driver can see a bad
+ * version if the FW write fails. Start by writing a copy of the
+ * first page with a bad version.
+ */
+ memcpy(first_page, fw_data, SF_PAGE_SIZE);
+ ((struct fw_hdr *)first_page)->fw_ver = htonl(0xffffffff);
+ ret = csio_hw_write_flash(hw, FW_IMG_START, SF_PAGE_SIZE, first_page);
+ if (ret)
+ goto out;
+
+ csio_dbg(hw, "Writing Flash .. start:%d end:%d\n",
+ FW_IMG_START, FW_IMG_START + size);
+
+ addr = FW_IMG_START;
+ for (size -= SF_PAGE_SIZE; size; size -= SF_PAGE_SIZE) {
+ addr += SF_PAGE_SIZE;
+ fw_data += SF_PAGE_SIZE;
+ ret = csio_hw_write_flash(hw, addr, SF_PAGE_SIZE, fw_data);
+ if (ret)
+ goto out;
+ }
+
+ ret = csio_hw_write_flash(hw,
+ FW_IMG_START +
+ offsetof(struct fw_hdr, fw_ver),
+ sizeof(hdr->fw_ver),
+ (const uint8_t *)&hdr->fw_ver);
+
+out:
+ if (ret)
+ csio_err(hw, "firmware download failed, error %d\n", ret);
+ return ret;
+}
+
+static int
+csio_hw_get_flash_params(struct csio_hw *hw)
+{
+ int ret;
+ uint32_t info = 0;
+
+ ret = csio_hw_sf1_write(hw, 1, 1, 0, SF_RD_ID);
+ if (!ret)
+ ret = csio_hw_sf1_read(hw, 3, 0, 1, &info);
+ csio_wr_reg32(hw, 0, SF_OP); /* unlock SF */
+ if (ret != 0)
+ return ret;
+
+ if ((info & 0xff) != 0x20) /* not a Numonix flash */
+ return -EINVAL;
+ info >>= 16; /* log2 of size */
+ if (info >= 0x14 && info < 0x18)
+ hw->params.sf_nsec = 1 << (info - 16);
+ else if (info == 0x18)
+ hw->params.sf_nsec = 64;
+ else
+ return -EINVAL;
+ hw->params.sf_size = 1 << info;
+
+ return 0;
+}
+
+static void
+csio_set_pcie_completion_timeout(struct csio_hw *hw, u8 range)
+{
+ uint16_t val;
+ uint32_t pcie_cap;
+
+ if (!csio_pci_capability(hw->pdev, PCI_CAP_ID_EXP, &pcie_cap)) {
+ pci_read_config_word(hw->pdev,
+ pcie_cap + PCI_EXP_DEVCTL2, &val);
+ val &= 0xfff0;
+ val |= range ;
+ pci_write_config_word(hw->pdev,
+ pcie_cap + PCI_EXP_DEVCTL2, val);
+ }
+}
+
+
+/*
+ * Return the specified PCI-E Configuration Space register from our Physical
+ * Function. We try first via a Firmware LDST Command since we prefer to let
+ * the firmware own all of these registers, but if that fails we go for it
+ * directly ourselves.
+ */
+static uint32_t
+csio_read_pcie_cfg4(struct csio_hw *hw, int reg)
+{
+ u32 val = 0;
+ struct csio_mb *mbp;
+ int rv;
+ struct fw_ldst_cmd *ldst_cmd;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ pci_read_config_dword(hw->pdev, reg, &val);
+ return val;
+ }
+
+ csio_mb_ldst(hw, mbp, CSIO_MB_DEFAULT_TMO, reg);
+
+ rv = csio_mb_issue(hw, mbp);
+
+ /*
+ * If the LDST Command suucceeded, exctract the returned register
+ * value. Otherwise read it directly ourself.
+ */
+ if (rv == 0) {
+ ldst_cmd = (struct fw_ldst_cmd *)(mbp->mb);
+ val = ntohl(ldst_cmd->u.pcie.data[0]);
+ } else
+ pci_read_config_dword(hw->pdev, reg, &val);
+
+ mempool_free(mbp, hw->mb_mempool);
+
+ return val;
+} /* csio_read_pcie_cfg4 */
+
+static int
+csio_hw_set_mem_win(struct csio_hw *hw)
+{
+ u32 bar0;
+
+ /*
+ * Truncation intentional: we only read the bottom 32-bits of the
+ * 64-bit BAR0/BAR1 ... We use the hardware backdoor mechanism to
+ * read BAR0 instead of using pci_resource_start() because we could be
+ * operating from within a Virtual Machine which is trapping our
+ * accesses to our Configuration Space and we need to set up the PCI-E
+ * Memory Window decoders with the actual addresses which will be
+ * coming across the PCI-E link.
+ */
+ bar0 = csio_read_pcie_cfg4(hw, PCI_BASE_ADDRESS_0);
+ bar0 &= PCI_BASE_ADDRESS_MEM_MASK;
+
+ /*
+ * Set up memory window for accessing adapter memory ranges. (Read
+ * back MA register to ensure that changes propagate before we attempt
+ * to use the new values.)
+ */
+ csio_wr_reg32(hw, (bar0 + MEMWIN0_BASE) | BIR(0) |
+ WINDOW(ilog2(MEMWIN0_APERTURE) - 10),
+ PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 0));
+ csio_wr_reg32(hw, (bar0 + MEMWIN1_BASE) | BIR(0) |
+ WINDOW(ilog2(MEMWIN1_APERTURE) - 10),
+ PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 1));
+ csio_wr_reg32(hw, (bar0 + MEMWIN2_BASE) | BIR(0) |
+ WINDOW(ilog2(MEMWIN2_APERTURE) - 10),
+ PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 2));
+ csio_rd_reg32(hw, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 2));
+ return 0;
+} /* csio_hw_set_mem_win */
+
+
+
+/*****************************************************************************/
+/* HW State machine assists */
+/*****************************************************************************/
+
+static int
+csio_hw_dev_ready(struct csio_hw *hw)
+{
+ uint32_t reg;
+ int cnt = 6;
+
+ while (((reg = csio_rd_reg32(hw, PL_WHOAMI)) == 0xFFFFFFFF) &&
+ (--cnt != 0))
+ mdelay(100);
+
+ if ((cnt == 0) && (((int32_t)(SOURCEPF_GET(reg)) < 0) ||
+ (SOURCEPF_GET(reg) >= CSIO_MAX_PFN))) {
+ csio_err(hw, "PL_WHOAMI returned 0x%x, cnt:%d\n", reg, cnt);
+ return -EIO;
+ }
+
+ hw->pfn = SOURCEPF_GET(reg);
+
+ return 0;
+}
+
+/*
+ * csio_do_hello - Perform the HELLO FW Mailbox command and process response.
+ * @hw: HW module
+ * @state: Device state
+ *
+ * FW_HELLO_CMD has to be polled for completion.
+ */
+static int
+csio_do_hello(struct csio_hw *hw, enum csio_dev_state *state)
+{
+ struct csio_mb *mbp;
+ int rv = 0;
+ enum csio_dev_master master;
+ enum fw_retval retval;
+ uint8_t mpfn;
+ char state_str[16];
+ int retries = FW_CMD_HELLO_RETRIES;
+
+ memset(state_str, 0, sizeof(state_str));
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ rv = -ENOMEM;
+ CSIO_INC_STATS(hw, n_err_nomem);
+ goto out;
+ }
+
+ master = csio_force_master ? CSIO_MASTER_MUST : CSIO_MASTER_MAY;
+
+retry:
+ csio_mb_hello(hw, mbp, CSIO_MB_DEFAULT_TMO, hw->pfn,
+ hw->pfn, master, NULL);
+
+ rv = csio_mb_issue(hw, mbp);
+ if (rv) {
+ csio_err(hw, "failed to issue HELLO cmd. ret:%d.\n", rv);
+ goto out_free_mb;
+ }
+
+ csio_mb_process_hello_rsp(hw, mbp, &retval, state, &mpfn);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "HELLO cmd failed with ret: %d\n", retval);
+ rv = -EINVAL;
+ goto out_free_mb;
+ }
+
+ /* Firmware has designated us to be master */
+ if (hw->pfn == mpfn) {
+ hw->flags |= CSIO_HWF_MASTER;
+ } else if (*state == CSIO_DEV_STATE_UNINIT) {
+ /*
+ * If we're not the Master PF then we need to wait around for
+ * the Master PF Driver to finish setting up the adapter.
+ *
+ * Note that we also do this wait if we're a non-Master-capable
+ * PF and there is no current Master PF; a Master PF may show up
+ * momentarily and we wouldn't want to fail pointlessly. (This
+ * can happen when an OS loads lots of different drivers rapidly
+ * at the same time). In this case, the Master PF returned by
+ * the firmware will be PCIE_FW_MASTER_MASK so the test below
+ * will work ...
+ */
+
+ int waiting = FW_CMD_HELLO_TIMEOUT;
+
+ /*
+ * Wait for the firmware to either indicate an error or
+ * initialized state. If we see either of these we bail out
+ * and report the issue to the caller. If we exhaust the
+ * "hello timeout" and we haven't exhausted our retries, try
+ * again. Otherwise bail with a timeout error.
+ */
+ for (;;) {
+ uint32_t pcie_fw;
+
+ msleep(50);
+ waiting -= 50;
+
+ /*
+ * If neither Error nor Initialialized are indicated
+ * by the firmware keep waiting till we exaust our
+ * timeout ... and then retry if we haven't exhausted
+ * our retries ...
+ */
+ pcie_fw = csio_rd_reg32(hw, PCIE_FW);
+ if (!(pcie_fw & (PCIE_FW_ERR|PCIE_FW_INIT))) {
+ if (waiting <= 0) {
+ if (retries-- > 0)
+ goto retry;
+
+ rv = -ETIMEDOUT;
+ break;
+ }
+ continue;
+ }
+
+ /*
+ * We either have an Error or Initialized condition
+ * report errors preferentially.
+ */
+ if (state) {
+ if (pcie_fw & PCIE_FW_ERR) {
+ *state = CSIO_DEV_STATE_ERR;
+ rv = -ETIMEDOUT;
+ } else if (pcie_fw & PCIE_FW_INIT)
+ *state = CSIO_DEV_STATE_INIT;
+ }
+
+ /*
+ * If we arrived before a Master PF was selected and
+ * there's not a valid Master PF, grab its identity
+ * for our caller.
+ */
+ if (mpfn == PCIE_FW_MASTER_MASK &&
+ (pcie_fw & PCIE_FW_MASTER_VLD))
+ mpfn = PCIE_FW_MASTER_GET(pcie_fw);
+ break;
+ }
+ hw->flags &= ~CSIO_HWF_MASTER;
+ }
+
+ switch (*state) {
+ case CSIO_DEV_STATE_UNINIT:
+ strcpy(state_str, "Initializing");
+ break;
+ case CSIO_DEV_STATE_INIT:
+ strcpy(state_str, "Initialized");
+ break;
+ case CSIO_DEV_STATE_ERR:
+ strcpy(state_str, "Error");
+ break;
+ default:
+ strcpy(state_str, "Unknown");
+ break;
+ }
+
+ if (hw->pfn == mpfn)
+ csio_info(hw, "PF: %d, Coming up as MASTER, HW state: %s\n",
+ hw->pfn, state_str);
+ else
+ csio_info(hw,
+ "PF: %d, Coming up as SLAVE, Master PF: %d, HW state: %s\n",
+ hw->pfn, mpfn, state_str);
+
+out_free_mb:
+ mempool_free(mbp, hw->mb_mempool);
+out:
+ return rv;
+}
+
+/*
+ * csio_do_bye - Perform the BYE FW Mailbox command and process response.
+ * @hw: HW module
+ *
+ */
+static int
+csio_do_bye(struct csio_hw *hw)
+{
+ struct csio_mb *mbp;
+ enum fw_retval retval;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ csio_mb_bye(hw, mbp, CSIO_MB_DEFAULT_TMO, NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of BYE command failed\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ retval = csio_mb_fw_retval(mbp);
+ if (retval != FW_SUCCESS) {
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ mempool_free(mbp, hw->mb_mempool);
+
+ return 0;
+}
+
+/*
+ * csio_do_reset- Perform the device reset.
+ * @hw: HW module
+ * @fw_rst: FW reset
+ *
+ * If fw_rst is set, issues FW reset mbox cmd otherwise
+ * does PIO reset.
+ * Performs reset of the function.
+ */
+static int
+csio_do_reset(struct csio_hw *hw, bool fw_rst)
+{
+ struct csio_mb *mbp;
+ enum fw_retval retval;
+
+ if (!fw_rst) {
+ /* PIO reset */
+ csio_wr_reg32(hw, PIORSTMODE | PIORST, PL_RST);
+ mdelay(2000);
+ return 0;
+ }
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ csio_mb_reset(hw, mbp, CSIO_MB_DEFAULT_TMO,
+ PIORSTMODE | PIORST, 0, NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of RESET command failed.n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ retval = csio_mb_fw_retval(mbp);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "RESET cmd failed with ret:0x%x.\n", retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ mempool_free(mbp, hw->mb_mempool);
+
+ return 0;
+}
+
+static int
+csio_hw_validate_caps(struct csio_hw *hw, struct csio_mb *mbp)
+{
+ struct fw_caps_config_cmd *rsp = (struct fw_caps_config_cmd *)mbp->mb;
+ uint16_t caps;
+
+ caps = ntohs(rsp->fcoecaps);
+
+ if (!(caps & FW_CAPS_CONFIG_FCOE_INITIATOR)) {
+ csio_err(hw, "No FCoE Initiator capability in the firmware.\n");
+ return -EINVAL;
+ }
+
+ if (!(caps & FW_CAPS_CONFIG_FCOE_CTRL_OFLD)) {
+ csio_err(hw, "No FCoE Control Offload capability\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * csio_hw_fw_halt - issue a reset/halt to FW and put uP into RESET
+ * @hw: the HW module
+ * @mbox: mailbox to use for the FW RESET command (if desired)
+ * @force: force uP into RESET even if FW RESET command fails
+ *
+ * Issues a RESET command to firmware (if desired) with a HALT indication
+ * and then puts the microprocessor into RESET state. The RESET command
+ * will only be issued if a legitimate mailbox is provided (mbox <=
+ * PCIE_FW_MASTER_MASK).
+ *
+ * This is generally used in order for the host to safely manipulate the
+ * adapter without fear of conflicting with whatever the firmware might
+ * be doing. The only way out of this state is to RESTART the firmware
+ * ...
+ */
+static int
+csio_hw_fw_halt(struct csio_hw *hw, uint32_t mbox, int32_t force)
+{
+ enum fw_retval retval = 0;
+
+ /*
+ * If a legitimate mailbox is provided, issue a RESET command
+ * with a HALT indication.
+ */
+ if (mbox <= PCIE_FW_MASTER_MASK) {
+ struct csio_mb *mbp;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ csio_mb_reset(hw, mbp, CSIO_MB_DEFAULT_TMO,
+ PIORSTMODE | PIORST, FW_RESET_CMD_HALT(1),
+ NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of RESET command failed!\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ retval = csio_mb_fw_retval(mbp);
+ mempool_free(mbp, hw->mb_mempool);
+ }
+
+ /*
+ * Normally we won't complete the operation if the firmware RESET
+ * command fails but if our caller insists we'll go ahead and put the
+ * uP into RESET. This can be useful if the firmware is hung or even
+ * missing ... We'll have to take the risk of putting the uP into
+ * RESET without the cooperation of firmware in that case.
+ *
+ * We also force the firmware's HALT flag to be on in case we bypassed
+ * the firmware RESET command above or we're dealing with old firmware
+ * which doesn't have the HALT capability. This will serve as a flag
+ * for the incoming firmware to know that it's coming out of a HALT
+ * rather than a RESET ... if it's new enough to understand that ...
+ */
+ if (retval == 0 || force) {
+ csio_set_reg_field(hw, CIM_BOOT_CFG, UPCRST, UPCRST);
+ csio_set_reg_field(hw, PCIE_FW, PCIE_FW_HALT, PCIE_FW_HALT);
+ }
+
+ /*
+ * And we always return the result of the firmware RESET command
+ * even when we force the uP into RESET ...
+ */
+ return retval ? -EINVAL : 0;
+}
+
+/*
+ * csio_hw_fw_restart - restart the firmware by taking the uP out of RESET
+ * @hw: the HW module
+ * @reset: if we want to do a RESET to restart things
+ *
+ * Restart firmware previously halted by csio_hw_fw_halt(). On successful
+ * return the previous PF Master remains as the new PF Master and there
+ * is no need to issue a new HELLO command, etc.
+ *
+ * We do this in two ways:
+ *
+ * 1. If we're dealing with newer firmware we'll simply want to take
+ * the chip's microprocessor out of RESET. This will cause the
+ * firmware to start up from its start vector. And then we'll loop
+ * until the firmware indicates it's started again (PCIE_FW.HALT
+ * reset to 0) or we timeout.
+ *
+ * 2. If we're dealing with older firmware then we'll need to RESET
+ * the chip since older firmware won't recognize the PCIE_FW.HALT
+ * flag and automatically RESET itself on startup.
+ */
+static int
+csio_hw_fw_restart(struct csio_hw *hw, uint32_t mbox, int32_t reset)
+{
+ if (reset) {
+ /*
+ * Since we're directing the RESET instead of the firmware
+ * doing it automatically, we need to clear the PCIE_FW.HALT
+ * bit.
+ */
+ csio_set_reg_field(hw, PCIE_FW, PCIE_FW_HALT, 0);
+
+ /*
+ * If we've been given a valid mailbox, first try to get the
+ * firmware to do the RESET. If that works, great and we can
+ * return success. Otherwise, if we haven't been given a
+ * valid mailbox or the RESET command failed, fall back to
+ * hitting the chip with a hammer.
+ */
+ if (mbox <= PCIE_FW_MASTER_MASK) {
+ csio_set_reg_field(hw, CIM_BOOT_CFG, UPCRST, 0);
+ msleep(100);
+ if (csio_do_reset(hw, true) == 0)
+ return 0;
+ }
+
+ csio_wr_reg32(hw, PIORSTMODE | PIORST, PL_RST);
+ msleep(2000);
+ } else {
+ int ms;
+
+ csio_set_reg_field(hw, CIM_BOOT_CFG, UPCRST, 0);
+ for (ms = 0; ms < FW_CMD_MAX_TIMEOUT; ) {
+ if (!(csio_rd_reg32(hw, PCIE_FW) & PCIE_FW_HALT))
+ return 0;
+ msleep(100);
+ ms += 100;
+ }
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+/*
+ * csio_hw_fw_upgrade - perform all of the steps necessary to upgrade FW
+ * @hw: the HW module
+ * @mbox: mailbox to use for the FW RESET command (if desired)
+ * @fw_data: the firmware image to write
+ * @size: image size
+ * @force: force upgrade even if firmware doesn't cooperate
+ *
+ * Perform all of the steps necessary for upgrading an adapter's
+ * firmware image. Normally this requires the cooperation of the
+ * existing firmware in order to halt all existing activities
+ * but if an invalid mailbox token is passed in we skip that step
+ * (though we'll still put the adapter microprocessor into RESET in
+ * that case).
+ *
+ * On successful return the new firmware will have been loaded and
+ * the adapter will have been fully RESET losing all previous setup
+ * state. On unsuccessful return the adapter may be completely hosed ...
+ * positive errno indicates that the adapter is ~probably~ intact, a
+ * negative errno indicates that things are looking bad ...
+ */
+static int
+csio_hw_fw_upgrade(struct csio_hw *hw, uint32_t mbox,
+ const u8 *fw_data, uint32_t size, int32_t force)
+{
+ const struct fw_hdr *fw_hdr = (const struct fw_hdr *)fw_data;
+ int reset, ret;
+
+ ret = csio_hw_fw_halt(hw, mbox, force);
+ if (ret != 0 && !force)
+ return ret;
+
+ ret = csio_hw_fw_dload(hw, (uint8_t *) fw_data, size);
+ if (ret != 0)
+ return ret;
+
+ /*
+ * Older versions of the firmware don't understand the new
+ * PCIE_FW.HALT flag and so won't know to perform a RESET when they
+ * restart. So for newly loaded older firmware we'll have to do the
+ * RESET for it so it starts up on a clean slate. We can tell if
+ * the newly loaded firmware will handle this right by checking
+ * its header flags to see if it advertises the capability.
+ */
+ reset = ((ntohl(fw_hdr->flags) & FW_HDR_FLAGS_RESET_HALT) == 0);
+ return csio_hw_fw_restart(hw, mbox, reset);
+}
+
+
+/*
+ * csio_hw_fw_config_file - setup an adapter via a Configuration File
+ * @hw: the HW module
+ * @mbox: mailbox to use for the FW command
+ * @mtype: the memory type where the Configuration File is located
+ * @maddr: the memory address where the Configuration File is located
+ * @finiver: return value for CF [fini] version
+ * @finicsum: return value for CF [fini] checksum
+ * @cfcsum: return value for CF computed checksum
+ *
+ * Issue a command to get the firmware to process the Configuration
+ * File located at the specified mtype/maddress. If the Configuration
+ * File is processed successfully and return value pointers are
+ * provided, the Configuration File "[fini] section version and
+ * checksum values will be returned along with the computed checksum.
+ * It's up to the caller to decide how it wants to respond to the
+ * checksums not matching but it recommended that a prominant warning
+ * be emitted in order to help people rapidly identify changed or
+ * corrupted Configuration Files.
+ *
+ * Also note that it's possible to modify things like "niccaps",
+ * "toecaps",etc. between processing the Configuration File and telling
+ * the firmware to use the new configuration. Callers which want to
+ * do this will need to "hand-roll" their own CAPS_CONFIGS commands for
+ * Configuration Files if they want to do this.
+ */
+static int
+csio_hw_fw_config_file(struct csio_hw *hw,
+ unsigned int mtype, unsigned int maddr,
+ uint32_t *finiver, uint32_t *finicsum, uint32_t *cfcsum)
+{
+ struct csio_mb *mbp;
+ struct fw_caps_config_cmd *caps_cmd;
+ int rv = -EINVAL;
+ enum fw_retval ret;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+ /*
+ * Tell the firmware to process the indicated Configuration File.
+ * If there are no errors and the caller has provided return value
+ * pointers for the [fini] section version, checksum and computed
+ * checksum, pass those back to the caller.
+ */
+ caps_cmd = (struct fw_caps_config_cmd *)(mbp->mb);
+ CSIO_INIT_MBP(mbp, caps_cmd, CSIO_MB_DEFAULT_TMO, hw, NULL, 1);
+ caps_cmd->op_to_write =
+ htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) |
+ FW_CMD_REQUEST |
+ FW_CMD_READ);
+ caps_cmd->cfvalid_to_len16 =
+ htonl(FW_CAPS_CONFIG_CMD_CFVALID |
+ FW_CAPS_CONFIG_CMD_MEMTYPE_CF(mtype) |
+ FW_CAPS_CONFIG_CMD_MEMADDR64K_CF(maddr >> 16) |
+ FW_LEN16(*caps_cmd));
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of FW_CAPS_CONFIG_CMD failed!\n");
+ goto out;
+ }
+
+ ret = csio_mb_fw_retval(mbp);
+ if (ret != FW_SUCCESS) {
+ csio_dbg(hw, "FW_CAPS_CONFIG_CMD returned %d!\n", rv);
+ goto out;
+ }
+
+ if (finiver)
+ *finiver = ntohl(caps_cmd->finiver);
+ if (finicsum)
+ *finicsum = ntohl(caps_cmd->finicsum);
+ if (cfcsum)
+ *cfcsum = ntohl(caps_cmd->cfcsum);
+
+ /* Validate device capabilities */
+ if (csio_hw_validate_caps(hw, mbp)) {
+ rv = -ENOENT;
+ goto out;
+ }
+
+ /*
+ * And now tell the firmware to use the configuration we just loaded.
+ */
+ caps_cmd->op_to_write =
+ htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) |
+ FW_CMD_REQUEST |
+ FW_CMD_WRITE);
+ caps_cmd->cfvalid_to_len16 = htonl(FW_LEN16(*caps_cmd));
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of FW_CAPS_CONFIG_CMD failed!\n");
+ goto out;
+ }
+
+ ret = csio_mb_fw_retval(mbp);
+ if (ret != FW_SUCCESS) {
+ csio_dbg(hw, "FW_CAPS_CONFIG_CMD returned %d!\n", rv);
+ goto out;
+ }
+
+ rv = 0;
+out:
+ mempool_free(mbp, hw->mb_mempool);
+ return rv;
+}
+
+/*
+ * csio_get_device_params - Get device parameters.
+ * @hw: HW module
+ *
+ */
+static int
+csio_get_device_params(struct csio_hw *hw)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_mb *mbp;
+ enum fw_retval retval;
+ u32 param[6];
+ int i, j = 0;
+
+ /* Initialize portids to -1 */
+ for (i = 0; i < CSIO_MAX_PPORTS; i++)
+ hw->pport[i].portid = -1;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ /* Get port vec information. */
+ param[0] = FW_PARAM_DEV(PORTVEC);
+
+ /* Get Core clock. */
+ param[1] = FW_PARAM_DEV(CCLK);
+
+ /* Get EQ id start and end. */
+ param[2] = FW_PARAM_PFVF(EQ_START);
+ param[3] = FW_PARAM_PFVF(EQ_END);
+
+ /* Get IQ id start and end. */
+ param[4] = FW_PARAM_PFVF(IQFLINT_START);
+ param[5] = FW_PARAM_PFVF(IQFLINT_END);
+
+ csio_mb_params(hw, mbp, CSIO_MB_DEFAULT_TMO, hw->pfn, 0,
+ ARRAY_SIZE(param), param, NULL, false, NULL);
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of FW_PARAMS_CMD(read) failed!\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ csio_mb_process_read_params_rsp(hw, mbp, &retval,
+ ARRAY_SIZE(param), param);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FW_PARAMS_CMD(read) failed with ret:0x%x!\n",
+ retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ /* cache the information. */
+ hw->port_vec = param[0];
+ hw->vpd.cclk = param[1];
+ wrm->fw_eq_start = param[2];
+ wrm->fw_iq_start = param[4];
+
+ /* Using FW configured max iqs & eqs */
+ if ((hw->flags & CSIO_HWF_USING_SOFT_PARAMS) ||
+ !csio_is_hw_master(hw)) {
+ hw->cfg_niq = param[5] - param[4] + 1;
+ hw->cfg_neq = param[3] - param[2] + 1;
+ csio_dbg(hw, "Using fwconfig max niqs %d neqs %d\n",
+ hw->cfg_niq, hw->cfg_neq);
+ }
+
+ hw->port_vec &= csio_port_mask;
+
+ hw->num_pports = hweight32(hw->port_vec);
+
+ csio_dbg(hw, "Port vector: 0x%x, #ports: %d\n",
+ hw->port_vec, hw->num_pports);
+
+ for (i = 0; i < hw->num_pports; i++) {
+ while ((hw->port_vec & (1 << j)) == 0)
+ j++;
+ hw->pport[i].portid = j++;
+ csio_dbg(hw, "Found Port:%d\n", hw->pport[i].portid);
+ }
+ mempool_free(mbp, hw->mb_mempool);
+
+ return 0;
+}
+
+
+/*
+ * csio_config_device_caps - Get and set device capabilities.
+ * @hw: HW module
+ *
+ */
+static int
+csio_config_device_caps(struct csio_hw *hw)
+{
+ struct csio_mb *mbp;
+ enum fw_retval retval;
+ int rv = -EINVAL;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ /* Get device capabilities */
+ csio_mb_caps_config(hw, mbp, CSIO_MB_DEFAULT_TMO, 0, 0, 0, 0, NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of FW_CAPS_CONFIG_CMD(r) failed!\n");
+ goto out;
+ }
+
+ retval = csio_mb_fw_retval(mbp);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FW_CAPS_CONFIG_CMD(r) returned %d!\n", retval);
+ goto out;
+ }
+
+ /* Validate device capabilities */
+ if (csio_hw_validate_caps(hw, mbp))
+ goto out;
+
+ /* Don't config device capabilities if already configured */
+ if (hw->fw_state == CSIO_DEV_STATE_INIT) {
+ rv = 0;
+ goto out;
+ }
+
+ /* Write back desired device capabilities */
+ csio_mb_caps_config(hw, mbp, CSIO_MB_DEFAULT_TMO, true, true,
+ false, true, NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of FW_CAPS_CONFIG_CMD(w) failed!\n");
+ goto out;
+ }
+
+ retval = csio_mb_fw_retval(mbp);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FW_CAPS_CONFIG_CMD(w) returned %d!\n", retval);
+ goto out;
+ }
+
+ rv = 0;
+out:
+ mempool_free(mbp, hw->mb_mempool);
+ return rv;
+}
+
+static int
+csio_config_global_rss(struct csio_hw *hw)
+{
+ struct csio_mb *mbp;
+ enum fw_retval retval;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ csio_rss_glb_config(hw, mbp, CSIO_MB_DEFAULT_TMO,
+ FW_RSS_GLB_CONFIG_CMD_MODE_BASICVIRTUAL,
+ FW_RSS_GLB_CONFIG_CMD_TNLMAPEN |
+ FW_RSS_GLB_CONFIG_CMD_HASHTOEPLITZ |
+ FW_RSS_GLB_CONFIG_CMD_TNLALLLKP,
+ NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of FW_RSS_GLB_CONFIG_CMD failed!\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ retval = csio_mb_fw_retval(mbp);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FW_RSS_GLB_CONFIG_CMD returned 0x%x!\n", retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ mempool_free(mbp, hw->mb_mempool);
+
+ return 0;
+}
+
+/*
+ * csio_config_pfvf - Configure Physical/Virtual functions settings.
+ * @hw: HW module
+ *
+ */
+static int
+csio_config_pfvf(struct csio_hw *hw)
+{
+ struct csio_mb *mbp;
+ enum fw_retval retval;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ /*
+ * For now, allow all PFs to access to all ports using a pmask
+ * value of 0xF (M_FW_PFVF_CMD_PMASK). Once we have VFs, we will
+ * need to provide access based on some rule.
+ */
+ csio_mb_pfvf(hw, mbp, CSIO_MB_DEFAULT_TMO, hw->pfn, 0, CSIO_NEQ,
+ CSIO_NETH_CTRL, CSIO_NIQ_FLINT, 0, 0, CSIO_NVI, CSIO_CMASK,
+ CSIO_PMASK, CSIO_NEXACTF, CSIO_R_CAPS, CSIO_WX_CAPS, NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of FW_PFVF_CMD failed!\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ retval = csio_mb_fw_retval(mbp);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FW_PFVF_CMD returned 0x%x!\n", retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ mempool_free(mbp, hw->mb_mempool);
+
+ return 0;
+}
+
+/*
+ * csio_enable_ports - Bring up all available ports.
+ * @hw: HW module.
+ *
+ */
+static int
+csio_enable_ports(struct csio_hw *hw)
+{
+ struct csio_mb *mbp;
+ enum fw_retval retval;
+ uint8_t portid;
+ int i;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < hw->num_pports; i++) {
+ portid = hw->pport[i].portid;
+
+ /* Read PORT information */
+ csio_mb_port(hw, mbp, CSIO_MB_DEFAULT_TMO, portid,
+ false, 0, 0, NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "failed to issue FW_PORT_CMD(r) port:%d\n",
+ portid);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ csio_mb_process_read_port_rsp(hw, mbp, &retval,
+ &hw->pport[i].pcap);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FW_PORT_CMD(r) port:%d failed: 0x%x\n",
+ portid, retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ /* Write back PORT information */
+ csio_mb_port(hw, mbp, CSIO_MB_DEFAULT_TMO, portid, true,
+ (PAUSE_RX | PAUSE_TX), hw->pport[i].pcap, NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "failed to issue FW_PORT_CMD(w) port:%d\n",
+ portid);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ retval = csio_mb_fw_retval(mbp);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FW_PORT_CMD(w) port:%d failed :0x%x\n",
+ portid, retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ } /* For all ports */
+
+ mempool_free(mbp, hw->mb_mempool);
+
+ return 0;
+}
+
+/*
+ * csio_get_fcoe_resinfo - Read fcoe fw resource info.
+ * @hw: HW module
+ * Issued with lock held.
+ */
+static int
+csio_get_fcoe_resinfo(struct csio_hw *hw)
+{
+ struct csio_fcoe_res_info *res_info = &hw->fres_info;
+ struct fw_fcoe_res_info_cmd *rsp;
+ struct csio_mb *mbp;
+ enum fw_retval retval;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ /* Get FCoE FW resource information */
+ csio_fcoe_read_res_info_init_mb(hw, mbp, CSIO_MB_DEFAULT_TMO, NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "failed to issue FW_FCOE_RES_INFO_CMD\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ rsp = (struct fw_fcoe_res_info_cmd *)(mbp->mb);
+ retval = FW_CMD_RETVAL_GET(ntohl(rsp->retval_len16));
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FW_FCOE_RES_INFO_CMD failed with ret x%x\n",
+ retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ res_info->e_d_tov = ntohs(rsp->e_d_tov);
+ res_info->r_a_tov_seq = ntohs(rsp->r_a_tov_seq);
+ res_info->r_a_tov_els = ntohs(rsp->r_a_tov_els);
+ res_info->r_r_tov = ntohs(rsp->r_r_tov);
+ res_info->max_xchgs = ntohl(rsp->max_xchgs);
+ res_info->max_ssns = ntohl(rsp->max_ssns);
+ res_info->used_xchgs = ntohl(rsp->used_xchgs);
+ res_info->used_ssns = ntohl(rsp->used_ssns);
+ res_info->max_fcfs = ntohl(rsp->max_fcfs);
+ res_info->max_vnps = ntohl(rsp->max_vnps);
+ res_info->used_fcfs = ntohl(rsp->used_fcfs);
+ res_info->used_vnps = ntohl(rsp->used_vnps);
+
+ csio_dbg(hw, "max ssns:%d max xchgs:%d\n", res_info->max_ssns,
+ res_info->max_xchgs);
+ mempool_free(mbp, hw->mb_mempool);
+
+ return 0;
+}
+
+static int
+csio_hw_check_fwconfig(struct csio_hw *hw, u32 *param)
+{
+ struct csio_mb *mbp;
+ enum fw_retval retval;
+ u32 _param[1];
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ /*
+ * Find out whether we're dealing with a version of
+ * the firmware which has configuration file support.
+ */
+ _param[0] = (FW_PARAMS_MNEM(FW_PARAMS_MNEM_DEV) |
+ FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_DEV_CF));
+
+ csio_mb_params(hw, mbp, CSIO_MB_DEFAULT_TMO, hw->pfn, 0,
+ ARRAY_SIZE(_param), _param, NULL, false, NULL);
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of FW_PARAMS_CMD(read) failed!\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ csio_mb_process_read_params_rsp(hw, mbp, &retval,
+ ARRAY_SIZE(_param), _param);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FW_PARAMS_CMD(read) failed with ret:0x%x!\n",
+ retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ mempool_free(mbp, hw->mb_mempool);
+ *param = _param[0];
+
+ return 0;
+}
+
+static int
+csio_hw_flash_config(struct csio_hw *hw, u32 *fw_cfg_param, char *path)
+{
+ int ret = 0;
+ const struct firmware *cf;
+ struct pci_dev *pci_dev = hw->pdev;
+ struct device *dev = &pci_dev->dev;
+ unsigned int mtype = 0, maddr = 0;
+ uint32_t *cfg_data;
+ int value_to_add = 0;
+
+ if (request_firmware(&cf, CSIO_CF_FNAME, dev) < 0) {
+ csio_err(hw, "could not find config file " CSIO_CF_FNAME
+ ",err: %d\n", ret);
+ return -ENOENT;
+ }
+
+ if (cf->size%4 != 0)
+ value_to_add = 4 - (cf->size % 4);
+
+ cfg_data = kzalloc(cf->size+value_to_add, GFP_KERNEL);
+ if (cfg_data == NULL)
+ return -ENOMEM;
+
+ memcpy((void *)cfg_data, (const void *)cf->data, cf->size);
+
+ if (csio_hw_check_fwconfig(hw, fw_cfg_param) != 0)
+ return -EINVAL;
+
+ mtype = FW_PARAMS_PARAM_Y_GET(*fw_cfg_param);
+ maddr = FW_PARAMS_PARAM_Z_GET(*fw_cfg_param) << 16;
+
+ ret = csio_memory_write(hw, mtype, maddr,
+ cf->size + value_to_add, cfg_data);
+ if (ret == 0) {
+ csio_info(hw, "config file upgraded to " CSIO_CF_FNAME "\n");
+ strncpy(path, "/lib/firmware/" CSIO_CF_FNAME, 64);
+ }
+
+ kfree(cfg_data);
+ release_firmware(cf);
+
+ return ret;
+}
+
+/*
+ * HW initialization: contact FW, obtain config, perform basic init.
+ *
+ * If the firmware we're dealing with has Configuration File support, then
+ * we use that to perform all configuration -- either using the configuration
+ * file stored in flash on the adapter or using a filesystem-local file
+ * if available.
+ *
+ * If we don't have configuration file support in the firmware, then we'll
+ * have to set things up the old fashioned way with hard-coded register
+ * writes and firmware commands ...
+ */
+
+/*
+ * Attempt to initialize the HW via a Firmware Configuration File.
+ */
+static int
+csio_hw_use_fwconfig(struct csio_hw *hw, int reset, u32 *fw_cfg_param)
+{
+ unsigned int mtype, maddr;
+ int rv;
+ uint32_t finiver, finicsum, cfcsum;
+ int using_flash;
+ char path[64];
+
+ /*
+ * Reset device if necessary
+ */
+ if (reset) {
+ rv = csio_do_reset(hw, true);
+ if (rv != 0)
+ goto bye;
+ }
+
+ /*
+ * If we have a configuration file in host ,
+ * then use that. Otherwise, use the configuration file stored
+ * in the HW flash ...
+ */
+ spin_unlock_irq(&hw->lock);
+ rv = csio_hw_flash_config(hw, fw_cfg_param, path);
+ spin_lock_irq(&hw->lock);
+ if (rv != 0) {
+ if (rv == -ENOENT) {
+ /*
+ * config file was not found. Use default
+ * config file from flash.
+ */
+ mtype = FW_MEMTYPE_CF_FLASH;
+ maddr = csio_hw_flash_cfg_addr(hw);
+ using_flash = 1;
+ } else {
+ /*
+ * we revert back to the hardwired config if
+ * flashing failed.
+ */
+ goto bye;
+ }
+ } else {
+ mtype = FW_PARAMS_PARAM_Y_GET(*fw_cfg_param);
+ maddr = FW_PARAMS_PARAM_Z_GET(*fw_cfg_param) << 16;
+ using_flash = 0;
+ }
+
+ hw->cfg_store = (uint8_t)mtype;
+
+ /*
+ * Issue a Capability Configuration command to the firmware to get it
+ * to parse the Configuration File.
+ */
+ rv = csio_hw_fw_config_file(hw, mtype, maddr, &finiver,
+ &finicsum, &cfcsum);
+ if (rv != 0)
+ goto bye;
+
+ hw->cfg_finiver = finiver;
+ hw->cfg_finicsum = finicsum;
+ hw->cfg_cfcsum = cfcsum;
+ hw->cfg_csum_status = true;
+
+ if (finicsum != cfcsum) {
+ csio_warn(hw,
+ "Config File checksum mismatch: csum=%#x, computed=%#x\n",
+ finicsum, cfcsum);
+
+ hw->cfg_csum_status = false;
+ }
+
+ /*
+ * Note that we're operating with parameters
+ * not supplied by the driver, rather than from hard-wired
+ * initialization constants buried in the driver.
+ */
+ hw->flags |= CSIO_HWF_USING_SOFT_PARAMS;
+
+ /* device parameters */
+ rv = csio_get_device_params(hw);
+ if (rv != 0)
+ goto bye;
+
+ /* Configure SGE */
+ csio_wr_sge_init(hw);
+
+ /*
+ * And finally tell the firmware to initialize itself using the
+ * parameters from the Configuration File.
+ */
+ /* Post event to notify completion of configuration */
+ csio_post_event(&hw->sm, CSIO_HWE_INIT);
+
+ csio_info(hw,
+ "Firmware Configuration File %s, version %#x, computed checksum %#x\n",
+ (using_flash ? "in device FLASH" : path), finiver, cfcsum);
+
+ return 0;
+
+ /*
+ * Something bad happened. Return the error ...
+ */
+bye:
+ hw->flags &= ~CSIO_HWF_USING_SOFT_PARAMS;
+ csio_dbg(hw, "Configuration file error %d\n", rv);
+ return rv;
+}
+
+/*
+ * Attempt to initialize the adapter via hard-coded, driver supplied
+ * parameters ...
+ */
+static int
+csio_hw_no_fwconfig(struct csio_hw *hw, int reset)
+{
+ int rv;
+ /*
+ * Reset device if necessary
+ */
+ if (reset) {
+ rv = csio_do_reset(hw, true);
+ if (rv != 0)
+ goto out;
+ }
+
+ /* Get and set device capabilities */
+ rv = csio_config_device_caps(hw);
+ if (rv != 0)
+ goto out;
+
+ /* Config Global RSS command */
+ rv = csio_config_global_rss(hw);
+ if (rv != 0)
+ goto out;
+
+ /* Configure PF/VF capabilities of device */
+ rv = csio_config_pfvf(hw);
+ if (rv != 0)
+ goto out;
+
+ /* device parameters */
+ rv = csio_get_device_params(hw);
+ if (rv != 0)
+ goto out;
+
+ /* Configure SGE */
+ csio_wr_sge_init(hw);
+
+ /* Post event to notify completion of configuration */
+ csio_post_event(&hw->sm, CSIO_HWE_INIT);
+
+out:
+ return rv;
+}
+
+/*
+ * Returns -EINVAL if attempts to flash the firmware failed
+ * else returns 0,
+ * if flashing was not attempted because the card had the
+ * latest firmware ECANCELED is returned
+ */
+static int
+csio_hw_flash_fw(struct csio_hw *hw)
+{
+ int ret = -ECANCELED;
+ const struct firmware *fw;
+ const struct fw_hdr *hdr;
+ u32 fw_ver;
+ struct pci_dev *pci_dev = hw->pdev;
+ struct device *dev = &pci_dev->dev ;
+
+ if (request_firmware(&fw, CSIO_FW_FNAME, dev) < 0) {
+ csio_err(hw, "could not find firmware image " CSIO_FW_FNAME
+ ",err: %d\n", ret);
+ return -EINVAL;
+ }
+
+ hdr = (const struct fw_hdr *)fw->data;
+ fw_ver = ntohl(hdr->fw_ver);
+ if (FW_HDR_FW_VER_MAJOR_GET(fw_ver) != FW_VERSION_MAJOR)
+ return -EINVAL; /* wrong major version, won't do */
+
+ /*
+ * If the flash FW is unusable or we found something newer, load it.
+ */
+ if (FW_HDR_FW_VER_MAJOR_GET(hw->fwrev) != FW_VERSION_MAJOR ||
+ fw_ver > hw->fwrev) {
+ ret = csio_hw_fw_upgrade(hw, hw->pfn, fw->data, fw->size,
+ /*force=*/false);
+ if (!ret)
+ csio_info(hw, "firmware upgraded to version %pI4 from "
+ CSIO_FW_FNAME "\n", &hdr->fw_ver);
+ else
+ csio_err(hw, "firmware upgrade failed! err=%d\n", ret);
+ }
+
+ release_firmware(fw);
+
+ return ret;
+}
+
+
+/*
+ * csio_hw_configure - Configure HW
+ * @hw - HW module
+ *
+ */
+static void
+csio_hw_configure(struct csio_hw *hw)
+{
+ int reset = 1;
+ int rv;
+ u32 param[1];
+
+ rv = csio_hw_dev_ready(hw);
+ if (rv != 0) {
+ CSIO_INC_STATS(hw, n_err_fatal);
+ csio_post_event(&hw->sm, CSIO_HWE_FATAL);
+ goto out;
+ }
+
+ /* HW version */
+ hw->chip_ver = (char)csio_rd_reg32(hw, PL_REV);
+
+ /* Needed for FW download */
+ rv = csio_hw_get_flash_params(hw);
+ if (rv != 0) {
+ csio_err(hw, "Failed to get serial flash params rv:%d\n", rv);
+ csio_post_event(&hw->sm, CSIO_HWE_FATAL);
+ goto out;
+ }
+
+ /* Set pci completion timeout value to 4 seconds. */
+ csio_set_pcie_completion_timeout(hw, 0xd);
+
+ csio_hw_set_mem_win(hw);
+
+ rv = csio_hw_get_fw_version(hw, &hw->fwrev);
+ if (rv != 0)
+ goto out;
+
+ csio_hw_print_fw_version(hw, "Firmware revision");
+
+ rv = csio_do_hello(hw, &hw->fw_state);
+ if (rv != 0) {
+ CSIO_INC_STATS(hw, n_err_fatal);
+ csio_post_event(&hw->sm, CSIO_HWE_FATAL);
+ goto out;
+ }
+
+ /* Read vpd */
+ rv = csio_hw_get_vpd_params(hw, &hw->vpd);
+ if (rv != 0)
+ goto out;
+
+ if (csio_is_hw_master(hw) && hw->fw_state != CSIO_DEV_STATE_INIT) {
+ rv = csio_hw_check_fw_version(hw);
+ if (rv == -EINVAL) {
+
+ /* Do firmware update */
+ spin_unlock_irq(&hw->lock);
+ rv = csio_hw_flash_fw(hw);
+ spin_lock_irq(&hw->lock);
+
+ if (rv == 0) {
+ reset = 0;
+ /*
+ * Note that the chip was reset as part of the
+ * firmware upgrade so we don't reset it again
+ * below and grab the new firmware version.
+ */
+ rv = csio_hw_check_fw_version(hw);
+ }
+ }
+ /*
+ * If the firmware doesn't support Configuration
+ * Files, use the old Driver-based, hard-wired
+ * initialization. Otherwise, try using the
+ * Configuration File support and fall back to the
+ * Driver-based initialization if there's no
+ * Configuration File found.
+ */
+ if (csio_hw_check_fwconfig(hw, param) == 0) {
+ rv = csio_hw_use_fwconfig(hw, reset, param);
+ if (rv == -ENOENT)
+ goto out;
+ if (rv != 0) {
+ csio_info(hw,
+ "No Configuration File present "
+ "on adapter. Using hard-wired "
+ "configuration parameters.\n");
+ rv = csio_hw_no_fwconfig(hw, reset);
+ }
+ } else {
+ rv = csio_hw_no_fwconfig(hw, reset);
+ }
+
+ if (rv != 0)
+ goto out;
+
+ } else {
+ if (hw->fw_state == CSIO_DEV_STATE_INIT) {
+
+ /* device parameters */
+ rv = csio_get_device_params(hw);
+ if (rv != 0)
+ goto out;
+
+ /* Get device capabilities */
+ rv = csio_config_device_caps(hw);
+ if (rv != 0)
+ goto out;
+
+ /* Configure SGE */
+ csio_wr_sge_init(hw);
+
+ /* Post event to notify completion of configuration */
+ csio_post_event(&hw->sm, CSIO_HWE_INIT);
+ goto out;
+ }
+ } /* if not master */
+
+out:
+ return;
+}
+
+/*
+ * csio_hw_initialize - Initialize HW
+ * @hw - HW module
+ *
+ */
+static void
+csio_hw_initialize(struct csio_hw *hw)
+{
+ struct csio_mb *mbp;
+ enum fw_retval retval;
+ int rv;
+ int i;
+
+ if (csio_is_hw_master(hw) && hw->fw_state != CSIO_DEV_STATE_INIT) {
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp)
+ goto out;
+
+ csio_mb_initialize(hw, mbp, CSIO_MB_DEFAULT_TMO, NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of FW_INITIALIZE_CMD failed!\n");
+ goto free_and_out;
+ }
+
+ retval = csio_mb_fw_retval(mbp);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FW_INITIALIZE_CMD returned 0x%x!\n",
+ retval);
+ goto free_and_out;
+ }
+
+ mempool_free(mbp, hw->mb_mempool);
+ }
+
+ rv = csio_get_fcoe_resinfo(hw);
+ if (rv != 0) {
+ csio_err(hw, "Failed to read fcoe resource info: %d\n", rv);
+ goto out;
+ }
+
+ spin_unlock_irq(&hw->lock);
+ rv = csio_config_queues(hw);
+ spin_lock_irq(&hw->lock);
+
+ if (rv != 0) {
+ csio_err(hw, "Config of queues failed!: %d\n", rv);
+ goto out;
+ }
+
+ for (i = 0; i < hw->num_pports; i++)
+ hw->pport[i].mod_type = FW_PORT_MOD_TYPE_NA;
+
+ if (csio_is_hw_master(hw) && hw->fw_state != CSIO_DEV_STATE_INIT) {
+ rv = csio_enable_ports(hw);
+ if (rv != 0) {
+ csio_err(hw, "Failed to enable ports: %d\n", rv);
+ goto out;
+ }
+ }
+
+ csio_post_event(&hw->sm, CSIO_HWE_INIT_DONE);
+ return;
+
+free_and_out:
+ mempool_free(mbp, hw->mb_mempool);
+out:
+ return;
+}
+
+#define PF_INTR_MASK (PFSW | PFCIM)
+
+/*
+ * csio_hw_intr_enable - Enable HW interrupts
+ * @hw: Pointer to HW module.
+ *
+ * Enable interrupts in HW registers.
+ */
+static void
+csio_hw_intr_enable(struct csio_hw *hw)
+{
+ uint16_t vec = (uint16_t)csio_get_mb_intr_idx(csio_hw_to_mbm(hw));
+ uint32_t pf = SOURCEPF_GET(csio_rd_reg32(hw, PL_WHOAMI));
+ uint32_t pl = csio_rd_reg32(hw, PL_INT_ENABLE);
+
+ /*
+ * Set aivec for MSI/MSIX. PCIE_PF_CFG.INTXType is set up
+ * by FW, so do nothing for INTX.
+ */
+ if (hw->intr_mode == CSIO_IM_MSIX)
+ csio_set_reg_field(hw, MYPF_REG(PCIE_PF_CFG),
+ AIVEC(AIVEC_MASK), vec);
+ else if (hw->intr_mode == CSIO_IM_MSI)
+ csio_set_reg_field(hw, MYPF_REG(PCIE_PF_CFG),
+ AIVEC(AIVEC_MASK), 0);
+
+ csio_wr_reg32(hw, PF_INTR_MASK, MYPF_REG(PL_PF_INT_ENABLE));
+
+ /* Turn on MB interrupts - this will internally flush PIO as well */
+ csio_mb_intr_enable(hw);
+
+ /* These are common registers - only a master can modify them */
+ if (csio_is_hw_master(hw)) {
+ /*
+ * Disable the Serial FLASH interrupt, if enabled!
+ */
+ pl &= (~SF);
+ csio_wr_reg32(hw, pl, PL_INT_ENABLE);
+
+ csio_wr_reg32(hw, ERR_CPL_EXCEED_IQE_SIZE |
+ EGRESS_SIZE_ERR | ERR_INVALID_CIDX_INC |
+ ERR_CPL_OPCODE_0 | ERR_DROPPED_DB |
+ ERR_DATA_CPL_ON_HIGH_QID1 |
+ ERR_DATA_CPL_ON_HIGH_QID0 | ERR_BAD_DB_PIDX3 |
+ ERR_BAD_DB_PIDX2 | ERR_BAD_DB_PIDX1 |
+ ERR_BAD_DB_PIDX0 | ERR_ING_CTXT_PRIO |
+ ERR_EGR_CTXT_PRIO | INGRESS_SIZE_ERR,
+ SGE_INT_ENABLE3);
+ csio_set_reg_field(hw, PL_INT_MAP0, 0, 1 << pf);
+ }
+
+ hw->flags |= CSIO_HWF_HW_INTR_ENABLED;
+
+}
+
+/*
+ * csio_hw_intr_disable - Disable HW interrupts
+ * @hw: Pointer to HW module.
+ *
+ * Turn off Mailbox and PCI_PF_CFG interrupts.
+ */
+void
+csio_hw_intr_disable(struct csio_hw *hw)
+{
+ uint32_t pf = SOURCEPF_GET(csio_rd_reg32(hw, PL_WHOAMI));
+
+ if (!(hw->flags & CSIO_HWF_HW_INTR_ENABLED))
+ return;
+
+ hw->flags &= ~CSIO_HWF_HW_INTR_ENABLED;
+
+ csio_wr_reg32(hw, 0, MYPF_REG(PL_PF_INT_ENABLE));
+ if (csio_is_hw_master(hw))
+ csio_set_reg_field(hw, PL_INT_MAP0, 1 << pf, 0);
+
+ /* Turn off MB interrupts */
+ csio_mb_intr_disable(hw);
+
+}
+
+static void
+csio_hw_fatal_err(struct csio_hw *hw)
+{
+ csio_set_reg_field(hw, SGE_CONTROL, GLOBALENABLE, 0);
+ csio_hw_intr_disable(hw);
+
+ /* Do not reset HW, we may need FW state for debugging */
+ csio_fatal(hw, "HW Fatal error encountered!\n");
+}
+
+/*****************************************************************************/
+/* START: HW SM */
+/*****************************************************************************/
+/*
+ * csio_hws_uninit - Uninit state
+ * @hw - HW module
+ * @evt - Event
+ *
+ */
+static void
+csio_hws_uninit(struct csio_hw *hw, enum csio_hw_ev evt)
+{
+ hw->prev_evt = hw->cur_evt;
+ hw->cur_evt = evt;
+ CSIO_INC_STATS(hw, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_HWE_CFG:
+ csio_set_state(&hw->sm, csio_hws_configuring);
+ csio_hw_configure(hw);
+ break;
+
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_hws_configuring - Configuring state
+ * @hw - HW module
+ * @evt - Event
+ *
+ */
+static void
+csio_hws_configuring(struct csio_hw *hw, enum csio_hw_ev evt)
+{
+ hw->prev_evt = hw->cur_evt;
+ hw->cur_evt = evt;
+ CSIO_INC_STATS(hw, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_HWE_INIT:
+ csio_set_state(&hw->sm, csio_hws_initializing);
+ csio_hw_initialize(hw);
+ break;
+
+ case CSIO_HWE_INIT_DONE:
+ csio_set_state(&hw->sm, csio_hws_ready);
+ /* Fan out event to all lnode SMs */
+ csio_notify_lnodes(hw, CSIO_LN_NOTIFY_HWREADY);
+ break;
+
+ case CSIO_HWE_FATAL:
+ csio_set_state(&hw->sm, csio_hws_uninit);
+ break;
+
+ case CSIO_HWE_PCI_REMOVE:
+ csio_do_bye(hw);
+ break;
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_hws_initializing - Initialiazing state
+ * @hw - HW module
+ * @evt - Event
+ *
+ */
+static void
+csio_hws_initializing(struct csio_hw *hw, enum csio_hw_ev evt)
+{
+ hw->prev_evt = hw->cur_evt;
+ hw->cur_evt = evt;
+ CSIO_INC_STATS(hw, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_HWE_INIT_DONE:
+ csio_set_state(&hw->sm, csio_hws_ready);
+
+ /* Fan out event to all lnode SMs */
+ csio_notify_lnodes(hw, CSIO_LN_NOTIFY_HWREADY);
+
+ /* Enable interrupts */
+ csio_hw_intr_enable(hw);
+ break;
+
+ case CSIO_HWE_FATAL:
+ csio_set_state(&hw->sm, csio_hws_uninit);
+ break;
+
+ case CSIO_HWE_PCI_REMOVE:
+ csio_do_bye(hw);
+ break;
+
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_hws_ready - Ready state
+ * @hw - HW module
+ * @evt - Event
+ *
+ */
+static void
+csio_hws_ready(struct csio_hw *hw, enum csio_hw_ev evt)
+{
+ /* Remember the event */
+ hw->evtflag = evt;
+
+ hw->prev_evt = hw->cur_evt;
+ hw->cur_evt = evt;
+ CSIO_INC_STATS(hw, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_HWE_HBA_RESET:
+ case CSIO_HWE_FW_DLOAD:
+ case CSIO_HWE_SUSPEND:
+ case CSIO_HWE_PCI_REMOVE:
+ case CSIO_HWE_PCIERR_DETECTED:
+ csio_set_state(&hw->sm, csio_hws_quiescing);
+ /* cleanup all outstanding cmds */
+ if (evt == CSIO_HWE_HBA_RESET ||
+ evt == CSIO_HWE_PCIERR_DETECTED)
+ csio_scsim_cleanup_io(csio_hw_to_scsim(hw), false);
+ else
+ csio_scsim_cleanup_io(csio_hw_to_scsim(hw), true);
+
+ csio_hw_intr_disable(hw);
+ csio_hw_mbm_cleanup(hw);
+ csio_evtq_stop(hw);
+ csio_notify_lnodes(hw, CSIO_LN_NOTIFY_HWSTOP);
+ csio_evtq_flush(hw);
+ csio_mgmtm_cleanup(csio_hw_to_mgmtm(hw));
+ csio_post_event(&hw->sm, CSIO_HWE_QUIESCED);
+ break;
+
+ case CSIO_HWE_FATAL:
+ csio_set_state(&hw->sm, csio_hws_uninit);
+ break;
+
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_hws_quiescing - Quiescing state
+ * @hw - HW module
+ * @evt - Event
+ *
+ */
+static void
+csio_hws_quiescing(struct csio_hw *hw, enum csio_hw_ev evt)
+{
+ hw->prev_evt = hw->cur_evt;
+ hw->cur_evt = evt;
+ CSIO_INC_STATS(hw, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_HWE_QUIESCED:
+ switch (hw->evtflag) {
+ case CSIO_HWE_FW_DLOAD:
+ csio_set_state(&hw->sm, csio_hws_resetting);
+ /* Download firmware */
+ /* Fall through */
+
+ case CSIO_HWE_HBA_RESET:
+ csio_set_state(&hw->sm, csio_hws_resetting);
+ /* Start reset of the HBA */
+ csio_notify_lnodes(hw, CSIO_LN_NOTIFY_HWRESET);
+ csio_wr_destroy_queues(hw, false);
+ csio_do_reset(hw, false);
+ csio_post_event(&hw->sm, CSIO_HWE_HBA_RESET_DONE);
+ break;
+
+ case CSIO_HWE_PCI_REMOVE:
+ csio_set_state(&hw->sm, csio_hws_removing);
+ csio_notify_lnodes(hw, CSIO_LN_NOTIFY_HWREMOVE);
+ csio_wr_destroy_queues(hw, true);
+ /* Now send the bye command */
+ csio_do_bye(hw);
+ break;
+
+ case CSIO_HWE_SUSPEND:
+ csio_set_state(&hw->sm, csio_hws_quiesced);
+ break;
+
+ case CSIO_HWE_PCIERR_DETECTED:
+ csio_set_state(&hw->sm, csio_hws_pcierr);
+ csio_wr_destroy_queues(hw, false);
+ break;
+
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+
+ }
+ break;
+
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_hws_quiesced - Quiesced state
+ * @hw - HW module
+ * @evt - Event
+ *
+ */
+static void
+csio_hws_quiesced(struct csio_hw *hw, enum csio_hw_ev evt)
+{
+ hw->prev_evt = hw->cur_evt;
+ hw->cur_evt = evt;
+ CSIO_INC_STATS(hw, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_HWE_RESUME:
+ csio_set_state(&hw->sm, csio_hws_configuring);
+ csio_hw_configure(hw);
+ break;
+
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_hws_resetting - HW Resetting state
+ * @hw - HW module
+ * @evt - Event
+ *
+ */
+static void
+csio_hws_resetting(struct csio_hw *hw, enum csio_hw_ev evt)
+{
+ hw->prev_evt = hw->cur_evt;
+ hw->cur_evt = evt;
+ CSIO_INC_STATS(hw, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_HWE_HBA_RESET_DONE:
+ csio_evtq_start(hw);
+ csio_set_state(&hw->sm, csio_hws_configuring);
+ csio_hw_configure(hw);
+ break;
+
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_hws_removing - PCI Hotplug removing state
+ * @hw - HW module
+ * @evt - Event
+ *
+ */
+static void
+csio_hws_removing(struct csio_hw *hw, enum csio_hw_ev evt)
+{
+ hw->prev_evt = hw->cur_evt;
+ hw->cur_evt = evt;
+ CSIO_INC_STATS(hw, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_HWE_HBA_RESET:
+ if (!csio_is_hw_master(hw))
+ break;
+ /*
+ * The BYE should have alerady been issued, so we cant
+ * use the mailbox interface. Hence we use the PL_RST
+ * register directly.
+ */
+ csio_err(hw, "Resetting HW and waiting 2 seconds...\n");
+ csio_wr_reg32(hw, PIORSTMODE | PIORST, PL_RST);
+ mdelay(2000);
+ break;
+
+ /* Should never receive any new events */
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+
+ }
+}
+
+/*
+ * csio_hws_pcierr - PCI Error state
+ * @hw - HW module
+ * @evt - Event
+ *
+ */
+static void
+csio_hws_pcierr(struct csio_hw *hw, enum csio_hw_ev evt)
+{
+ hw->prev_evt = hw->cur_evt;
+ hw->cur_evt = evt;
+ CSIO_INC_STATS(hw, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_HWE_PCIERR_SLOT_RESET:
+ csio_evtq_start(hw);
+ csio_set_state(&hw->sm, csio_hws_configuring);
+ csio_hw_configure(hw);
+ break;
+
+ default:
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+ }
+}
+
+/*****************************************************************************/
+/* END: HW SM */
+/*****************************************************************************/
+
+/* Slow path handlers */
+struct intr_info {
+ unsigned int mask; /* bits to check in interrupt status */
+ const char *msg; /* message to print or NULL */
+ short stat_idx; /* stat counter to increment or -1 */
+ unsigned short fatal; /* whether the condition reported is fatal */
+};
+
+/*
+ * csio_handle_intr_status - table driven interrupt handler
+ * @hw: HW instance
+ * @reg: the interrupt status register to process
+ * @acts: table of interrupt actions
+ *
+ * A table driven interrupt handler that applies a set of masks to an
+ * interrupt status word and performs the corresponding actions if the
+ * interrupts described by the mask have occured. The actions include
+ * optionally emitting a warning or alert message. The table is terminated
+ * by an entry specifying mask 0. Returns the number of fatal interrupt
+ * conditions.
+ */
+static int
+csio_handle_intr_status(struct csio_hw *hw, unsigned int reg,
+ const struct intr_info *acts)
+{
+ int fatal = 0;
+ unsigned int mask = 0;
+ unsigned int status = csio_rd_reg32(hw, reg);
+
+ for ( ; acts->mask; ++acts) {
+ if (!(status & acts->mask))
+ continue;
+ if (acts->fatal) {
+ fatal++;
+ csio_fatal(hw, "Fatal %s (0x%x)\n",
+ acts->msg, status & acts->mask);
+ } else if (acts->msg)
+ csio_info(hw, "%s (0x%x)\n",
+ acts->msg, status & acts->mask);
+ mask |= acts->mask;
+ }
+ status &= mask;
+ if (status) /* clear processed interrupts */
+ csio_wr_reg32(hw, status, reg);
+ return fatal;
+}
+
+/*
+ * Interrupt handler for the PCIE module.
+ */
+static void
+csio_pcie_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info sysbus_intr_info[] = {
+ { RNPP, "RXNP array parity error", -1, 1 },
+ { RPCP, "RXPC array parity error", -1, 1 },
+ { RCIP, "RXCIF array parity error", -1, 1 },
+ { RCCP, "Rx completions control array parity error", -1, 1 },
+ { RFTP, "RXFT array parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+ static struct intr_info pcie_port_intr_info[] = {
+ { TPCP, "TXPC array parity error", -1, 1 },
+ { TNPP, "TXNP array parity error", -1, 1 },
+ { TFTP, "TXFT array parity error", -1, 1 },
+ { TCAP, "TXCA array parity error", -1, 1 },
+ { TCIP, "TXCIF array parity error", -1, 1 },
+ { RCAP, "RXCA array parity error", -1, 1 },
+ { OTDD, "outbound request TLP discarded", -1, 1 },
+ { RDPE, "Rx data parity error", -1, 1 },
+ { TDUE, "Tx uncorrectable data error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+ static struct intr_info pcie_intr_info[] = {
+ { MSIADDRLPERR, "MSI AddrL parity error", -1, 1 },
+ { MSIADDRHPERR, "MSI AddrH parity error", -1, 1 },
+ { MSIDATAPERR, "MSI data parity error", -1, 1 },
+ { MSIXADDRLPERR, "MSI-X AddrL parity error", -1, 1 },
+ { MSIXADDRHPERR, "MSI-X AddrH parity error", -1, 1 },
+ { MSIXDATAPERR, "MSI-X data parity error", -1, 1 },
+ { MSIXDIPERR, "MSI-X DI parity error", -1, 1 },
+ { PIOCPLPERR, "PCI PIO completion FIFO parity error", -1, 1 },
+ { PIOREQPERR, "PCI PIO request FIFO parity error", -1, 1 },
+ { TARTAGPERR, "PCI PCI target tag FIFO parity error", -1, 1 },
+ { CCNTPERR, "PCI CMD channel count parity error", -1, 1 },
+ { CREQPERR, "PCI CMD channel request parity error", -1, 1 },
+ { CRSPPERR, "PCI CMD channel response parity error", -1, 1 },
+ { DCNTPERR, "PCI DMA channel count parity error", -1, 1 },
+ { DREQPERR, "PCI DMA channel request parity error", -1, 1 },
+ { DRSPPERR, "PCI DMA channel response parity error", -1, 1 },
+ { HCNTPERR, "PCI HMA channel count parity error", -1, 1 },
+ { HREQPERR, "PCI HMA channel request parity error", -1, 1 },
+ { HRSPPERR, "PCI HMA channel response parity error", -1, 1 },
+ { CFGSNPPERR, "PCI config snoop FIFO parity error", -1, 1 },
+ { FIDPERR, "PCI FID parity error", -1, 1 },
+ { INTXCLRPERR, "PCI INTx clear parity error", -1, 1 },
+ { MATAGPERR, "PCI MA tag parity error", -1, 1 },
+ { PIOTAGPERR, "PCI PIO tag parity error", -1, 1 },
+ { RXCPLPERR, "PCI Rx completion parity error", -1, 1 },
+ { RXWRPERR, "PCI Rx write parity error", -1, 1 },
+ { RPLPERR, "PCI replay buffer parity error", -1, 1 },
+ { PCIESINT, "PCI core secondary fault", -1, 1 },
+ { PCIEPINT, "PCI core primary fault", -1, 1 },
+ { UNXSPLCPLERR, "PCI unexpected split completion error", -1,
+ 0 },
+ { 0, NULL, 0, 0 }
+ };
+
+ int fat;
+
+ fat = csio_handle_intr_status(hw,
+ PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS,
+ sysbus_intr_info) +
+ csio_handle_intr_status(hw,
+ PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS,
+ pcie_port_intr_info) +
+ csio_handle_intr_status(hw, PCIE_INT_CAUSE, pcie_intr_info);
+ if (fat)
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * TP interrupt handler.
+ */
+static void csio_tp_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info tp_intr_info[] = {
+ { 0x3fffffff, "TP parity error", -1, 1 },
+ { FLMTXFLSTEMPTY, "TP out of Tx pages", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, TP_INT_CAUSE, tp_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * SGE interrupt handler.
+ */
+static void csio_sge_intr_handler(struct csio_hw *hw)
+{
+ uint64_t v;
+
+ static struct intr_info sge_intr_info[] = {
+ { ERR_CPL_EXCEED_IQE_SIZE,
+ "SGE received CPL exceeding IQE size", -1, 1 },
+ { ERR_INVALID_CIDX_INC,
+ "SGE GTS CIDX increment too large", -1, 0 },
+ { ERR_CPL_OPCODE_0, "SGE received 0-length CPL", -1, 0 },
+ { ERR_DROPPED_DB, "SGE doorbell dropped", -1, 0 },
+ { ERR_DATA_CPL_ON_HIGH_QID1 | ERR_DATA_CPL_ON_HIGH_QID0,
+ "SGE IQID > 1023 received CPL for FL", -1, 0 },
+ { ERR_BAD_DB_PIDX3, "SGE DBP 3 pidx increment too large", -1,
+ 0 },
+ { ERR_BAD_DB_PIDX2, "SGE DBP 2 pidx increment too large", -1,
+ 0 },
+ { ERR_BAD_DB_PIDX1, "SGE DBP 1 pidx increment too large", -1,
+ 0 },
+ { ERR_BAD_DB_PIDX0, "SGE DBP 0 pidx increment too large", -1,
+ 0 },
+ { ERR_ING_CTXT_PRIO,
+ "SGE too many priority ingress contexts", -1, 0 },
+ { ERR_EGR_CTXT_PRIO,
+ "SGE too many priority egress contexts", -1, 0 },
+ { INGRESS_SIZE_ERR, "SGE illegal ingress QID", -1, 0 },
+ { EGRESS_SIZE_ERR, "SGE illegal egress QID", -1, 0 },
+ { 0, NULL, 0, 0 }
+ };
+
+ v = (uint64_t)csio_rd_reg32(hw, SGE_INT_CAUSE1) |
+ ((uint64_t)csio_rd_reg32(hw, SGE_INT_CAUSE2) << 32);
+ if (v) {
+ csio_fatal(hw, "SGE parity error (%#llx)\n",
+ (unsigned long long)v);
+ csio_wr_reg32(hw, (uint32_t)(v & 0xFFFFFFFF),
+ SGE_INT_CAUSE1);
+ csio_wr_reg32(hw, (uint32_t)(v >> 32), SGE_INT_CAUSE2);
+ }
+
+ v |= csio_handle_intr_status(hw, SGE_INT_CAUSE3, sge_intr_info);
+
+ if (csio_handle_intr_status(hw, SGE_INT_CAUSE3, sge_intr_info) ||
+ v != 0)
+ csio_hw_fatal_err(hw);
+}
+
+#define CIM_OBQ_INTR (OBQULP0PARERR | OBQULP1PARERR | OBQULP2PARERR |\
+ OBQULP3PARERR | OBQSGEPARERR | OBQNCSIPARERR)
+#define CIM_IBQ_INTR (IBQTP0PARERR | IBQTP1PARERR | IBQULPPARERR |\
+ IBQSGEHIPARERR | IBQSGELOPARERR | IBQNCSIPARERR)
+
+/*
+ * CIM interrupt handler.
+ */
+static void csio_cim_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info cim_intr_info[] = {
+ { PREFDROPINT, "CIM control register prefetch drop", -1, 1 },
+ { CIM_OBQ_INTR, "CIM OBQ parity error", -1, 1 },
+ { CIM_IBQ_INTR, "CIM IBQ parity error", -1, 1 },
+ { MBUPPARERR, "CIM mailbox uP parity error", -1, 1 },
+ { MBHOSTPARERR, "CIM mailbox host parity error", -1, 1 },
+ { TIEQINPARERRINT, "CIM TIEQ outgoing parity error", -1, 1 },
+ { TIEQOUTPARERRINT, "CIM TIEQ incoming parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+ static struct intr_info cim_upintr_info[] = {
+ { RSVDSPACEINT, "CIM reserved space access", -1, 1 },
+ { ILLTRANSINT, "CIM illegal transaction", -1, 1 },
+ { ILLWRINT, "CIM illegal write", -1, 1 },
+ { ILLRDINT, "CIM illegal read", -1, 1 },
+ { ILLRDBEINT, "CIM illegal read BE", -1, 1 },
+ { ILLWRBEINT, "CIM illegal write BE", -1, 1 },
+ { SGLRDBOOTINT, "CIM single read from boot space", -1, 1 },
+ { SGLWRBOOTINT, "CIM single write to boot space", -1, 1 },
+ { BLKWRBOOTINT, "CIM block write to boot space", -1, 1 },
+ { SGLRDFLASHINT, "CIM single read from flash space", -1, 1 },
+ { SGLWRFLASHINT, "CIM single write to flash space", -1, 1 },
+ { BLKWRFLASHINT, "CIM block write to flash space", -1, 1 },
+ { SGLRDEEPROMINT, "CIM single EEPROM read", -1, 1 },
+ { SGLWREEPROMINT, "CIM single EEPROM write", -1, 1 },
+ { BLKRDEEPROMINT, "CIM block EEPROM read", -1, 1 },
+ { BLKWREEPROMINT, "CIM block EEPROM write", -1, 1 },
+ { SGLRDCTLINT , "CIM single read from CTL space", -1, 1 },
+ { SGLWRCTLINT , "CIM single write to CTL space", -1, 1 },
+ { BLKRDCTLINT , "CIM block read from CTL space", -1, 1 },
+ { BLKWRCTLINT , "CIM block write to CTL space", -1, 1 },
+ { SGLRDPLINT , "CIM single read from PL space", -1, 1 },
+ { SGLWRPLINT , "CIM single write to PL space", -1, 1 },
+ { BLKRDPLINT , "CIM block read from PL space", -1, 1 },
+ { BLKWRPLINT , "CIM block write to PL space", -1, 1 },
+ { REQOVRLOOKUPINT , "CIM request FIFO overwrite", -1, 1 },
+ { RSPOVRLOOKUPINT , "CIM response FIFO overwrite", -1, 1 },
+ { TIMEOUTINT , "CIM PIF timeout", -1, 1 },
+ { TIMEOUTMAINT , "CIM PIF MA timeout", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ int fat;
+
+ fat = csio_handle_intr_status(hw, CIM_HOST_INT_CAUSE,
+ cim_intr_info) +
+ csio_handle_intr_status(hw, CIM_HOST_UPACC_INT_CAUSE,
+ cim_upintr_info);
+ if (fat)
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * ULP RX interrupt handler.
+ */
+static void csio_ulprx_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info ulprx_intr_info[] = {
+ { 0x1800000, "ULPRX context error", -1, 1 },
+ { 0x7fffff, "ULPRX parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, ULP_RX_INT_CAUSE, ulprx_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * ULP TX interrupt handler.
+ */
+static void csio_ulptx_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info ulptx_intr_info[] = {
+ { PBL_BOUND_ERR_CH3, "ULPTX channel 3 PBL out of bounds", -1,
+ 0 },
+ { PBL_BOUND_ERR_CH2, "ULPTX channel 2 PBL out of bounds", -1,
+ 0 },
+ { PBL_BOUND_ERR_CH1, "ULPTX channel 1 PBL out of bounds", -1,
+ 0 },
+ { PBL_BOUND_ERR_CH0, "ULPTX channel 0 PBL out of bounds", -1,
+ 0 },
+ { 0xfffffff, "ULPTX parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, ULP_TX_INT_CAUSE, ulptx_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * PM TX interrupt handler.
+ */
+static void csio_pmtx_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info pmtx_intr_info[] = {
+ { PCMD_LEN_OVFL0, "PMTX channel 0 pcmd too large", -1, 1 },
+ { PCMD_LEN_OVFL1, "PMTX channel 1 pcmd too large", -1, 1 },
+ { PCMD_LEN_OVFL2, "PMTX channel 2 pcmd too large", -1, 1 },
+ { ZERO_C_CMD_ERROR, "PMTX 0-length pcmd", -1, 1 },
+ { 0xffffff0, "PMTX framing error", -1, 1 },
+ { OESPI_PAR_ERROR, "PMTX oespi parity error", -1, 1 },
+ { DB_OPTIONS_PAR_ERROR, "PMTX db_options parity error", -1,
+ 1 },
+ { ICSPI_PAR_ERROR, "PMTX icspi parity error", -1, 1 },
+ { C_PCMD_PAR_ERROR, "PMTX c_pcmd parity error", -1, 1},
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, PM_TX_INT_CAUSE, pmtx_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * PM RX interrupt handler.
+ */
+static void csio_pmrx_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info pmrx_intr_info[] = {
+ { ZERO_E_CMD_ERROR, "PMRX 0-length pcmd", -1, 1 },
+ { 0x3ffff0, "PMRX framing error", -1, 1 },
+ { OCSPI_PAR_ERROR, "PMRX ocspi parity error", -1, 1 },
+ { DB_OPTIONS_PAR_ERROR, "PMRX db_options parity error", -1,
+ 1 },
+ { IESPI_PAR_ERROR, "PMRX iespi parity error", -1, 1 },
+ { E_PCMD_PAR_ERROR, "PMRX e_pcmd parity error", -1, 1},
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, PM_RX_INT_CAUSE, pmrx_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * CPL switch interrupt handler.
+ */
+static void csio_cplsw_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info cplsw_intr_info[] = {
+ { CIM_OP_MAP_PERR, "CPLSW CIM op_map parity error", -1, 1 },
+ { CIM_OVFL_ERROR, "CPLSW CIM overflow", -1, 1 },
+ { TP_FRAMING_ERROR, "CPLSW TP framing error", -1, 1 },
+ { SGE_FRAMING_ERROR, "CPLSW SGE framing error", -1, 1 },
+ { CIM_FRAMING_ERROR, "CPLSW CIM framing error", -1, 1 },
+ { ZERO_SWITCH_ERROR, "CPLSW no-switch error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, CPL_INTR_CAUSE, cplsw_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * LE interrupt handler.
+ */
+static void csio_le_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info le_intr_info[] = {
+ { LIPMISS, "LE LIP miss", -1, 0 },
+ { LIP0, "LE 0 LIP error", -1, 0 },
+ { PARITYERR, "LE parity error", -1, 1 },
+ { UNKNOWNCMD, "LE unknown command", -1, 1 },
+ { REQQPARERR, "LE request queue parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, LE_DB_INT_CAUSE, le_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * MPS interrupt handler.
+ */
+static void csio_mps_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info mps_rx_intr_info[] = {
+ { 0xffffff, "MPS Rx parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+ static struct intr_info mps_tx_intr_info[] = {
+ { TPFIFO, "MPS Tx TP FIFO parity error", -1, 1 },
+ { NCSIFIFO, "MPS Tx NC-SI FIFO parity error", -1, 1 },
+ { TXDATAFIFO, "MPS Tx data FIFO parity error", -1, 1 },
+ { TXDESCFIFO, "MPS Tx desc FIFO parity error", -1, 1 },
+ { BUBBLE, "MPS Tx underflow", -1, 1 },
+ { SECNTERR, "MPS Tx SOP/EOP error", -1, 1 },
+ { FRMERR, "MPS Tx framing error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+ static struct intr_info mps_trc_intr_info[] = {
+ { FILTMEM, "MPS TRC filter parity error", -1, 1 },
+ { PKTFIFO, "MPS TRC packet FIFO parity error", -1, 1 },
+ { MISCPERR, "MPS TRC misc parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+ static struct intr_info mps_stat_sram_intr_info[] = {
+ { 0x1fffff, "MPS statistics SRAM parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+ static struct intr_info mps_stat_tx_intr_info[] = {
+ { 0xfffff, "MPS statistics Tx FIFO parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+ static struct intr_info mps_stat_rx_intr_info[] = {
+ { 0xffffff, "MPS statistics Rx FIFO parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+ static struct intr_info mps_cls_intr_info[] = {
+ { MATCHSRAM, "MPS match SRAM parity error", -1, 1 },
+ { MATCHTCAM, "MPS match TCAM parity error", -1, 1 },
+ { HASHSRAM, "MPS hash SRAM parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ int fat;
+
+ fat = csio_handle_intr_status(hw, MPS_RX_PERR_INT_CAUSE,
+ mps_rx_intr_info) +
+ csio_handle_intr_status(hw, MPS_TX_INT_CAUSE,
+ mps_tx_intr_info) +
+ csio_handle_intr_status(hw, MPS_TRC_INT_CAUSE,
+ mps_trc_intr_info) +
+ csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_SRAM,
+ mps_stat_sram_intr_info) +
+ csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_TX_FIFO,
+ mps_stat_tx_intr_info) +
+ csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_RX_FIFO,
+ mps_stat_rx_intr_info) +
+ csio_handle_intr_status(hw, MPS_CLS_INT_CAUSE,
+ mps_cls_intr_info);
+
+ csio_wr_reg32(hw, 0, MPS_INT_CAUSE);
+ csio_rd_reg32(hw, MPS_INT_CAUSE); /* flush */
+ if (fat)
+ csio_hw_fatal_err(hw);
+}
+
+#define MEM_INT_MASK (PERR_INT_CAUSE | ECC_CE_INT_CAUSE | ECC_UE_INT_CAUSE)
+
+/*
+ * EDC/MC interrupt handler.
+ */
+static void csio_mem_intr_handler(struct csio_hw *hw, int idx)
+{
+ static const char name[3][5] = { "EDC0", "EDC1", "MC" };
+
+ unsigned int addr, cnt_addr, v;
+
+ if (idx <= MEM_EDC1) {
+ addr = EDC_REG(EDC_INT_CAUSE, idx);
+ cnt_addr = EDC_REG(EDC_ECC_STATUS, idx);
+ } else {
+ addr = MC_INT_CAUSE;
+ cnt_addr = MC_ECC_STATUS;
+ }
+
+ v = csio_rd_reg32(hw, addr) & MEM_INT_MASK;
+ if (v & PERR_INT_CAUSE)
+ csio_fatal(hw, "%s FIFO parity error\n", name[idx]);
+ if (v & ECC_CE_INT_CAUSE) {
+ uint32_t cnt = ECC_CECNT_GET(csio_rd_reg32(hw, cnt_addr));
+
+ csio_wr_reg32(hw, ECC_CECNT_MASK, cnt_addr);
+ csio_warn(hw, "%u %s correctable ECC data error%s\n",
+ cnt, name[idx], cnt > 1 ? "s" : "");
+ }
+ if (v & ECC_UE_INT_CAUSE)
+ csio_fatal(hw, "%s uncorrectable ECC data error\n", name[idx]);
+
+ csio_wr_reg32(hw, v, addr);
+ if (v & (PERR_INT_CAUSE | ECC_UE_INT_CAUSE))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * MA interrupt handler.
+ */
+static void csio_ma_intr_handler(struct csio_hw *hw)
+{
+ uint32_t v, status = csio_rd_reg32(hw, MA_INT_CAUSE);
+
+ if (status & MEM_PERR_INT_CAUSE)
+ csio_fatal(hw, "MA parity error, parity status %#x\n",
+ csio_rd_reg32(hw, MA_PARITY_ERROR_STATUS));
+ if (status & MEM_WRAP_INT_CAUSE) {
+ v = csio_rd_reg32(hw, MA_INT_WRAP_STATUS);
+ csio_fatal(hw,
+ "MA address wrap-around error by client %u to address %#x\n",
+ MEM_WRAP_CLIENT_NUM_GET(v), MEM_WRAP_ADDRESS_GET(v) << 4);
+ }
+ csio_wr_reg32(hw, status, MA_INT_CAUSE);
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * SMB interrupt handler.
+ */
+static void csio_smb_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info smb_intr_info[] = {
+ { MSTTXFIFOPARINT, "SMB master Tx FIFO parity error", -1, 1 },
+ { MSTRXFIFOPARINT, "SMB master Rx FIFO parity error", -1, 1 },
+ { SLVFIFOPARINT, "SMB slave FIFO parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, SMB_INT_CAUSE, smb_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * NC-SI interrupt handler.
+ */
+static void csio_ncsi_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info ncsi_intr_info[] = {
+ { CIM_DM_PRTY_ERR, "NC-SI CIM parity error", -1, 1 },
+ { MPS_DM_PRTY_ERR, "NC-SI MPS parity error", -1, 1 },
+ { TXFIFO_PRTY_ERR, "NC-SI Tx FIFO parity error", -1, 1 },
+ { RXFIFO_PRTY_ERR, "NC-SI Rx FIFO parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, NCSI_INT_CAUSE, ncsi_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * XGMAC interrupt handler.
+ */
+static void csio_xgmac_intr_handler(struct csio_hw *hw, int port)
+{
+ uint32_t v = csio_rd_reg32(hw, PORT_REG(port, XGMAC_PORT_INT_CAUSE));
+
+ v &= TXFIFO_PRTY_ERR | RXFIFO_PRTY_ERR;
+ if (!v)
+ return;
+
+ if (v & TXFIFO_PRTY_ERR)
+ csio_fatal(hw, "XGMAC %d Tx FIFO parity error\n", port);
+ if (v & RXFIFO_PRTY_ERR)
+ csio_fatal(hw, "XGMAC %d Rx FIFO parity error\n", port);
+ csio_wr_reg32(hw, v, PORT_REG(port, XGMAC_PORT_INT_CAUSE));
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * PL interrupt handler.
+ */
+static void csio_pl_intr_handler(struct csio_hw *hw)
+{
+ static struct intr_info pl_intr_info[] = {
+ { FATALPERR, "T4 fatal parity error", -1, 1 },
+ { PERRVFID, "PL VFID_MAP parity error", -1, 1 },
+ { 0, NULL, 0, 0 }
+ };
+
+ if (csio_handle_intr_status(hw, PL_PL_INT_CAUSE, pl_intr_info))
+ csio_hw_fatal_err(hw);
+}
+
+/*
+ * csio_hw_slow_intr_handler - control path interrupt handler
+ * @hw: HW module
+ *
+ * Interrupt handler for non-data global interrupt events, e.g., errors.
+ * The designation 'slow' is because it involves register reads, while
+ * data interrupts typically don't involve any MMIOs.
+ */
+int
+csio_hw_slow_intr_handler(struct csio_hw *hw)
+{
+ uint32_t cause = csio_rd_reg32(hw, PL_INT_CAUSE);
+
+ if (!(cause & CSIO_GLBL_INTR_MASK)) {
+ CSIO_INC_STATS(hw, n_plint_unexp);
+ return 0;
+ }
+
+ csio_dbg(hw, "Slow interrupt! cause: 0x%x\n", cause);
+
+ CSIO_INC_STATS(hw, n_plint_cnt);
+
+ if (cause & CIM)
+ csio_cim_intr_handler(hw);
+
+ if (cause & MPS)
+ csio_mps_intr_handler(hw);
+
+ if (cause & NCSI)
+ csio_ncsi_intr_handler(hw);
+
+ if (cause & PL)
+ csio_pl_intr_handler(hw);
+
+ if (cause & SMB)
+ csio_smb_intr_handler(hw);
+
+ if (cause & XGMAC0)
+ csio_xgmac_intr_handler(hw, 0);
+
+ if (cause & XGMAC1)
+ csio_xgmac_intr_handler(hw, 1);
+
+ if (cause & XGMAC_KR0)
+ csio_xgmac_intr_handler(hw, 2);
+
+ if (cause & XGMAC_KR1)
+ csio_xgmac_intr_handler(hw, 3);
+
+ if (cause & PCIE)
+ csio_pcie_intr_handler(hw);
+
+ if (cause & MC)
+ csio_mem_intr_handler(hw, MEM_MC);
+
+ if (cause & EDC0)
+ csio_mem_intr_handler(hw, MEM_EDC0);
+
+ if (cause & EDC1)
+ csio_mem_intr_handler(hw, MEM_EDC1);
+
+ if (cause & LE)
+ csio_le_intr_handler(hw);
+
+ if (cause & TP)
+ csio_tp_intr_handler(hw);
+
+ if (cause & MA)
+ csio_ma_intr_handler(hw);
+
+ if (cause & PM_TX)
+ csio_pmtx_intr_handler(hw);
+
+ if (cause & PM_RX)
+ csio_pmrx_intr_handler(hw);
+
+ if (cause & ULP_RX)
+ csio_ulprx_intr_handler(hw);
+
+ if (cause & CPL_SWITCH)
+ csio_cplsw_intr_handler(hw);
+
+ if (cause & SGE)
+ csio_sge_intr_handler(hw);
+
+ if (cause & ULP_TX)
+ csio_ulptx_intr_handler(hw);
+
+ /* Clear the interrupts just processed for which we are the master. */
+ csio_wr_reg32(hw, cause & CSIO_GLBL_INTR_MASK, PL_INT_CAUSE);
+ csio_rd_reg32(hw, PL_INT_CAUSE); /* flush */
+
+ return 1;
+}
+
+/*****************************************************************************
+ * HW <--> mailbox interfacing routines.
+ ****************************************************************************/
+/*
+ * csio_mberr_worker - Worker thread (dpc) for mailbox/error completions
+ *
+ * @data: Private data pointer.
+ *
+ * Called from worker thread context.
+ */
+static void
+csio_mberr_worker(void *data)
+{
+ struct csio_hw *hw = (struct csio_hw *)data;
+ struct csio_mbm *mbm = &hw->mbm;
+ LIST_HEAD(cbfn_q);
+ struct csio_mb *mbp_next;
+ int rv;
+
+ del_timer_sync(&mbm->timer);
+
+ spin_lock_irq(&hw->lock);
+ if (list_empty(&mbm->cbfn_q)) {
+ spin_unlock_irq(&hw->lock);
+ return;
+ }
+
+ list_splice_tail_init(&mbm->cbfn_q, &cbfn_q);
+ mbm->stats.n_cbfnq = 0;
+
+ /* Try to start waiting mailboxes */
+ if (!list_empty(&mbm->req_q)) {
+ mbp_next = list_first_entry(&mbm->req_q, struct csio_mb, list);
+ list_del_init(&mbp_next->list);
+
+ rv = csio_mb_issue(hw, mbp_next);
+ if (rv != 0)
+ list_add_tail(&mbp_next->list, &mbm->req_q);
+ else
+ CSIO_DEC_STATS(mbm, n_activeq);
+ }
+ spin_unlock_irq(&hw->lock);
+
+ /* Now callback completions */
+ csio_mb_completions(hw, &cbfn_q);
+}
+
+/*
+ * csio_hw_mb_timer - Top-level Mailbox timeout handler.
+ *
+ * @data: private data pointer
+ *
+ **/
+static void
+csio_hw_mb_timer(uintptr_t data)
+{
+ struct csio_hw *hw = (struct csio_hw *)data;
+ struct csio_mb *mbp = NULL;
+
+ spin_lock_irq(&hw->lock);
+ mbp = csio_mb_tmo_handler(hw);
+ spin_unlock_irq(&hw->lock);
+
+ /* Call back the function for the timed-out Mailbox */
+ if (mbp)
+ mbp->mb_cbfn(hw, mbp);
+
+}
+
+/*
+ * csio_hw_mbm_cleanup - Cleanup Mailbox module.
+ * @hw: HW module
+ *
+ * Called with lock held, should exit with lock held.
+ * Cancels outstanding mailboxes (waiting, in-flight) and gathers them
+ * into a local queue. Drops lock and calls the completions. Holds
+ * lock and returns.
+ */
+static void
+csio_hw_mbm_cleanup(struct csio_hw *hw)
+{
+ LIST_HEAD(cbfn_q);
+
+ csio_mb_cancel_all(hw, &cbfn_q);
+
+ spin_unlock_irq(&hw->lock);
+ csio_mb_completions(hw, &cbfn_q);
+ spin_lock_irq(&hw->lock);
+}
+
+/*****************************************************************************
+ * Event handling
+ ****************************************************************************/
+int
+csio_enqueue_evt(struct csio_hw *hw, enum csio_evt type, void *evt_msg,
+ uint16_t len)
+{
+ struct csio_evt_msg *evt_entry = NULL;
+
+ if (type >= CSIO_EVT_MAX)
+ return -EINVAL;
+
+ if (len > CSIO_EVT_MSG_SIZE)
+ return -EINVAL;
+
+ if (hw->flags & CSIO_HWF_FWEVT_STOP)
+ return -EINVAL;
+
+ if (list_empty(&hw->evt_free_q)) {
+ csio_err(hw, "Failed to alloc evt entry, msg type %d len %d\n",
+ type, len);
+ return -ENOMEM;
+ }
+
+ evt_entry = list_first_entry(&hw->evt_free_q,
+ struct csio_evt_msg, list);
+ list_del_init(&evt_entry->list);
+
+ /* copy event msg and queue the event */
+ evt_entry->type = type;
+ memcpy((void *)evt_entry->data, evt_msg, len);
+ list_add_tail(&evt_entry->list, &hw->evt_active_q);
+
+ CSIO_DEC_STATS(hw, n_evt_freeq);
+ CSIO_INC_STATS(hw, n_evt_activeq);
+
+ return 0;
+}
+
+static int
+csio_enqueue_evt_lock(struct csio_hw *hw, enum csio_evt type, void *evt_msg,
+ uint16_t len, bool msg_sg)
+{
+ struct csio_evt_msg *evt_entry = NULL;
+ struct csio_fl_dma_buf *fl_sg;
+ uint32_t off = 0;
+ unsigned long flags;
+ int n, ret = 0;
+
+ if (type >= CSIO_EVT_MAX)
+ return -EINVAL;
+
+ if (len > CSIO_EVT_MSG_SIZE)
+ return -EINVAL;
+
+ spin_lock_irqsave(&hw->lock, flags);
+ if (hw->flags & CSIO_HWF_FWEVT_STOP) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (list_empty(&hw->evt_free_q)) {
+ csio_err(hw, "Failed to alloc evt entry, msg type %d len %d\n",
+ type, len);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ evt_entry = list_first_entry(&hw->evt_free_q,
+ struct csio_evt_msg, list);
+ list_del_init(&evt_entry->list);
+
+ /* copy event msg and queue the event */
+ evt_entry->type = type;
+
+ /* If Payload in SG list*/
+ if (msg_sg) {
+ fl_sg = (struct csio_fl_dma_buf *) evt_msg;
+ for (n = 0; (n < CSIO_MAX_FLBUF_PER_IQWR && off < len); n++) {
+ memcpy((void *)((uintptr_t)evt_entry->data + off),
+ fl_sg->flbufs[n].vaddr,
+ fl_sg->flbufs[n].len);
+ off += fl_sg->flbufs[n].len;
+ }
+ } else
+ memcpy((void *)evt_entry->data, evt_msg, len);
+
+ list_add_tail(&evt_entry->list, &hw->evt_active_q);
+ CSIO_DEC_STATS(hw, n_evt_freeq);
+ CSIO_INC_STATS(hw, n_evt_activeq);
+out:
+ spin_unlock_irqrestore(&hw->lock, flags);
+ return ret;
+}
+
+static void
+csio_free_evt(struct csio_hw *hw, struct csio_evt_msg *evt_entry)
+{
+ if (evt_entry) {
+ spin_lock_irq(&hw->lock);
+ list_del_init(&evt_entry->list);
+ list_add_tail(&evt_entry->list, &hw->evt_free_q);
+ CSIO_DEC_STATS(hw, n_evt_activeq);
+ CSIO_INC_STATS(hw, n_evt_freeq);
+ spin_unlock_irq(&hw->lock);
+ }
+}
+
+void
+csio_evtq_flush(struct csio_hw *hw)
+{
+ uint32_t count;
+ count = 30;
+ while (hw->flags & CSIO_HWF_FWEVT_PENDING && count--) {
+ spin_unlock_irq(&hw->lock);
+ msleep(2000);
+ spin_lock_irq(&hw->lock);
+ }
+
+ CSIO_DB_ASSERT(!(hw->flags & CSIO_HWF_FWEVT_PENDING));
+}
+
+static void
+csio_evtq_stop(struct csio_hw *hw)
+{
+ hw->flags |= CSIO_HWF_FWEVT_STOP;
+}
+
+static void
+csio_evtq_start(struct csio_hw *hw)
+{
+ hw->flags &= ~CSIO_HWF_FWEVT_STOP;
+}
+
+static void
+csio_evtq_cleanup(struct csio_hw *hw)
+{
+ struct list_head *evt_entry, *next_entry;
+
+ /* Release outstanding events from activeq to freeq*/
+ if (!list_empty(&hw->evt_active_q))
+ list_splice_tail_init(&hw->evt_active_q, &hw->evt_free_q);
+
+ hw->stats.n_evt_activeq = 0;
+ hw->flags &= ~CSIO_HWF_FWEVT_PENDING;
+
+ /* Freeup event entry */
+ list_for_each_safe(evt_entry, next_entry, &hw->evt_free_q) {
+ kfree(evt_entry);
+ CSIO_DEC_STATS(hw, n_evt_freeq);
+ }
+
+ hw->stats.n_evt_freeq = 0;
+}
+
+
+static void
+csio_process_fwevtq_entry(struct csio_hw *hw, void *wr, uint32_t len,
+ struct csio_fl_dma_buf *flb, void *priv)
+{
+ __u8 op;
+ __be64 *data;
+ void *msg = NULL;
+ uint32_t msg_len = 0;
+ bool msg_sg = 0;
+
+ op = ((struct rss_header *) wr)->opcode;
+ if (op == CPL_FW6_PLD) {
+ CSIO_INC_STATS(hw, n_cpl_fw6_pld);
+ if (!flb || !flb->totlen) {
+ CSIO_INC_STATS(hw, n_cpl_unexp);
+ return;
+ }
+
+ msg = (void *) flb;
+ msg_len = flb->totlen;
+ msg_sg = 1;
+
+ data = (__be64 *) msg;
+ } else if (op == CPL_FW6_MSG || op == CPL_FW4_MSG) {
+
+ CSIO_INC_STATS(hw, n_cpl_fw6_msg);
+ /* skip RSS header */
+ msg = (void *)((uintptr_t)wr + sizeof(__be64));
+ msg_len = (op == CPL_FW6_MSG) ? sizeof(struct cpl_fw6_msg) :
+ sizeof(struct cpl_fw4_msg);
+
+ data = (__be64 *) msg;
+ } else {
+ csio_warn(hw, "unexpected CPL %#x on FW event queue\n", op);
+ CSIO_INC_STATS(hw, n_cpl_unexp);
+ return;
+ }
+
+ /*
+ * Enqueue event to EventQ. Events processing happens
+ * in Event worker thread context
+ */
+ if (csio_enqueue_evt_lock(hw, CSIO_EVT_FW, msg,
+ (uint16_t)msg_len, msg_sg))
+ CSIO_INC_STATS(hw, n_evt_drop);
+}
+
+void
+csio_evtq_worker(struct work_struct *work)
+{
+ struct csio_hw *hw = container_of(work, struct csio_hw, evtq_work);
+ struct list_head *evt_entry, *next_entry;
+ LIST_HEAD(evt_q);
+ struct csio_evt_msg *evt_msg;
+ struct cpl_fw6_msg *msg;
+ struct csio_rnode *rn;
+ int rv = 0;
+ uint8_t evtq_stop = 0;
+
+ csio_dbg(hw, "event worker thread active evts#%d\n",
+ hw->stats.n_evt_activeq);
+
+ spin_lock_irq(&hw->lock);
+ while (!list_empty(&hw->evt_active_q)) {
+ list_splice_tail_init(&hw->evt_active_q, &evt_q);
+ spin_unlock_irq(&hw->lock);
+
+ list_for_each_safe(evt_entry, next_entry, &evt_q) {
+ evt_msg = (struct csio_evt_msg *) evt_entry;
+
+ /* Drop events if queue is STOPPED */
+ spin_lock_irq(&hw->lock);
+ if (hw->flags & CSIO_HWF_FWEVT_STOP)
+ evtq_stop = 1;
+ spin_unlock_irq(&hw->lock);
+ if (evtq_stop) {
+ CSIO_INC_STATS(hw, n_evt_drop);
+ goto free_evt;
+ }
+
+ switch (evt_msg->type) {
+ case CSIO_EVT_FW:
+ msg = (struct cpl_fw6_msg *)(evt_msg->data);
+
+ if ((msg->opcode == CPL_FW6_MSG ||
+ msg->opcode == CPL_FW4_MSG) &&
+ !msg->type) {
+ rv = csio_mb_fwevt_handler(hw,
+ msg->data);
+ if (!rv)
+ break;
+ /* Handle any remaining fw events */
+ csio_fcoe_fwevt_handler(hw,
+ msg->opcode, msg->data);
+ } else if (msg->opcode == CPL_FW6_PLD) {
+
+ csio_fcoe_fwevt_handler(hw,
+ msg->opcode, msg->data);
+ } else {
+ csio_warn(hw,
+ "Unhandled FW msg op %x type %x\n",
+ msg->opcode, msg->type);
+ CSIO_INC_STATS(hw, n_evt_drop);
+ }
+ break;
+
+ case CSIO_EVT_MBX:
+ csio_mberr_worker(hw);
+ break;
+
+ case CSIO_EVT_DEV_LOSS:
+ memcpy(&rn, evt_msg->data, sizeof(rn));
+ csio_rnode_devloss_handler(rn);
+ break;
+
+ default:
+ csio_warn(hw, "Unhandled event %x on evtq\n",
+ evt_msg->type);
+ CSIO_INC_STATS(hw, n_evt_unexp);
+ break;
+ }
+free_evt:
+ csio_free_evt(hw, evt_msg);
+ }
+
+ spin_lock_irq(&hw->lock);
+ }
+ hw->flags &= ~CSIO_HWF_FWEVT_PENDING;
+ spin_unlock_irq(&hw->lock);
+}
+
+int
+csio_fwevtq_handler(struct csio_hw *hw)
+{
+ int rv;
+
+ if (csio_q_iqid(hw, hw->fwevt_iq_idx) == CSIO_MAX_QID) {
+ CSIO_INC_STATS(hw, n_int_stray);
+ return -EINVAL;
+ }
+
+ rv = csio_wr_process_iq_idx(hw, hw->fwevt_iq_idx,
+ csio_process_fwevtq_entry, NULL);
+ return rv;
+}
+
+/****************************************************************************
+ * Entry points
+ ****************************************************************************/
+
+/* Management module */
+/*
+ * csio_mgmt_req_lookup - Lookup the given IO req exist in Active Q.
+ * mgmt - mgmt module
+ * @io_req - io request
+ *
+ * Return - 0:if given IO Req exists in active Q.
+ * -EINVAL :if lookup fails.
+ */
+int
+csio_mgmt_req_lookup(struct csio_mgmtm *mgmtm, struct csio_ioreq *io_req)
+{
+ struct list_head *tmp;
+
+ /* Lookup ioreq in the ACTIVEQ */
+ list_for_each(tmp, &mgmtm->active_q) {
+ if (io_req == (struct csio_ioreq *)tmp)
+ return 0;
+ }
+ return -EINVAL;
+}
+
+#define ECM_MIN_TMO 1000 /* Minimum timeout value for req */
+
+/*
+ * csio_mgmts_tmo_handler - MGMT IO Timeout handler.
+ * @data - Event data.
+ *
+ * Return - none.
+ */
+static void
+csio_mgmt_tmo_handler(uintptr_t data)
+{
+ struct csio_mgmtm *mgmtm = (struct csio_mgmtm *) data;
+ struct list_head *tmp;
+ struct csio_ioreq *io_req;
+
+ csio_dbg(mgmtm->hw, "Mgmt timer invoked!\n");
+
+ spin_lock_irq(&mgmtm->hw->lock);
+
+ list_for_each(tmp, &mgmtm->active_q) {
+ io_req = (struct csio_ioreq *) tmp;
+ io_req->tmo -= min_t(uint32_t, io_req->tmo, ECM_MIN_TMO);
+
+ if (!io_req->tmo) {
+ /* Dequeue the request from retry Q. */
+ tmp = csio_list_prev(tmp);
+ list_del_init(&io_req->sm.sm_list);
+ if (io_req->io_cbfn) {
+ /* io_req will be freed by completion handler */
+ io_req->wr_status = -ETIMEDOUT;
+ io_req->io_cbfn(mgmtm->hw, io_req);
+ } else {
+ CSIO_DB_ASSERT(0);
+ }
+ }
+ }
+
+ /* If retry queue is not empty, re-arm timer */
+ if (!list_empty(&mgmtm->active_q))
+ mod_timer(&mgmtm->mgmt_timer,
+ jiffies + msecs_to_jiffies(ECM_MIN_TMO));
+ spin_unlock_irq(&mgmtm->hw->lock);
+}
+
+static void
+csio_mgmtm_cleanup(struct csio_mgmtm *mgmtm)
+{
+ struct csio_hw *hw = mgmtm->hw;
+ struct csio_ioreq *io_req;
+ struct list_head *tmp;
+ uint32_t count;
+
+ count = 30;
+ /* Wait for all outstanding req to complete gracefully */
+ while ((!list_empty(&mgmtm->active_q)) && count--) {
+ spin_unlock_irq(&hw->lock);
+ msleep(2000);
+ spin_lock_irq(&hw->lock);
+ }
+
+ /* release outstanding req from ACTIVEQ */
+ list_for_each(tmp, &mgmtm->active_q) {
+ io_req = (struct csio_ioreq *) tmp;
+ tmp = csio_list_prev(tmp);
+ list_del_init(&io_req->sm.sm_list);
+ mgmtm->stats.n_active--;
+ if (io_req->io_cbfn) {
+ /* io_req will be freed by completion handler */
+ io_req->wr_status = -ETIMEDOUT;
+ io_req->io_cbfn(mgmtm->hw, io_req);
+ }
+ }
+}
+
+/*
+ * csio_mgmt_init - Mgmt module init entry point
+ * @mgmtsm - mgmt module
+ * @hw - HW module
+ *
+ * Initialize mgmt timer, resource wait queue, active queue,
+ * completion q. Allocate Egress and Ingress
+ * WR queues and save off the queue index returned by the WR
+ * module for future use. Allocate and save off mgmt reqs in the
+ * mgmt_req_freelist for future use. Make sure their SM is initialized
+ * to uninit state.
+ * Returns: 0 - on success
+ * -ENOMEM - on error.
+ */
+static int
+csio_mgmtm_init(struct csio_mgmtm *mgmtm, struct csio_hw *hw)
+{
+ struct timer_list *timer = &mgmtm->mgmt_timer;
+
+ init_timer(timer);
+ timer->function = csio_mgmt_tmo_handler;
+ timer->data = (unsigned long)mgmtm;
+
+ INIT_LIST_HEAD(&mgmtm->active_q);
+ INIT_LIST_HEAD(&mgmtm->cbfn_q);
+
+ mgmtm->hw = hw;
+ /*mgmtm->iq_idx = hw->fwevt_iq_idx;*/
+
+ return 0;
+}
+
+/*
+ * csio_mgmtm_exit - MGMT module exit entry point
+ * @mgmtsm - mgmt module
+ *
+ * This function called during MGMT module uninit.
+ * Stop timers, free ioreqs allocated.
+ * Returns: None
+ *
+ */
+static void
+csio_mgmtm_exit(struct csio_mgmtm *mgmtm)
+{
+ del_timer_sync(&mgmtm->mgmt_timer);
+}
+
+
+/**
+ * csio_hw_start - Kicks off the HW State machine
+ * @hw: Pointer to HW module.
+ *
+ * It is assumed that the initialization is a synchronous operation.
+ * So when we return afer posting the event, the HW SM should be in
+ * the ready state, if there were no errors during init.
+ */
+int
+csio_hw_start(struct csio_hw *hw)
+{
+ spin_lock_irq(&hw->lock);
+ csio_post_event(&hw->sm, CSIO_HWE_CFG);
+ spin_unlock_irq(&hw->lock);
+
+ if (csio_is_hw_ready(hw))
+ return 0;
+ else
+ return -EINVAL;
+}
+
+int
+csio_hw_stop(struct csio_hw *hw)
+{
+ csio_post_event(&hw->sm, CSIO_HWE_PCI_REMOVE);
+
+ if (csio_is_hw_removing(hw))
+ return 0;
+ else
+ return -EINVAL;
+}
+
+/* Max reset retries */
+#define CSIO_MAX_RESET_RETRIES 3
+
+/**
+ * csio_hw_reset - Reset the hardware
+ * @hw: HW module.
+ *
+ * Caller should hold lock across this function.
+ */
+int
+csio_hw_reset(struct csio_hw *hw)
+{
+ if (!csio_is_hw_master(hw))
+ return -EPERM;
+
+ if (hw->rst_retries >= CSIO_MAX_RESET_RETRIES) {
+ csio_dbg(hw, "Max hw reset attempts reached..");
+ return -EINVAL;
+ }
+
+ hw->rst_retries++;
+ csio_post_event(&hw->sm, CSIO_HWE_HBA_RESET);
+
+ if (csio_is_hw_ready(hw)) {
+ hw->rst_retries = 0;
+ hw->stats.n_reset_start = jiffies_to_msecs(jiffies);
+ return 0;
+ } else
+ return -EINVAL;
+}
+
+/*
+ * csio_hw_get_device_id - Caches the Adapter's vendor & device id.
+ * @hw: HW module.
+ */
+static void
+csio_hw_get_device_id(struct csio_hw *hw)
+{
+ /* Is the adapter device id cached already ?*/
+ if (csio_is_dev_id_cached(hw))
+ return;
+
+ /* Get the PCI vendor & device id */
+ pci_read_config_word(hw->pdev, PCI_VENDOR_ID,
+ &hw->params.pci.vendor_id);
+ pci_read_config_word(hw->pdev, PCI_DEVICE_ID,
+ &hw->params.pci.device_id);
+
+ csio_dev_id_cached(hw);
+
+} /* csio_hw_get_device_id */
+
+/*
+ * csio_hw_set_description - Set the model, description of the hw.
+ * @hw: HW module.
+ * @ven_id: PCI Vendor ID
+ * @dev_id: PCI Device ID
+ */
+static void
+csio_hw_set_description(struct csio_hw *hw, uint16_t ven_id, uint16_t dev_id)
+{
+ uint32_t adap_type, prot_type;
+
+ if (ven_id == CSIO_VENDOR_ID) {
+ prot_type = (dev_id & CSIO_ASIC_DEVID_PROTO_MASK);
+ adap_type = (dev_id & CSIO_ASIC_DEVID_TYPE_MASK);
+
+ if (prot_type == CSIO_FPGA) {
+ memcpy(hw->model_desc,
+ csio_fcoe_adapters[13].description, 32);
+ } else if (prot_type == CSIO_T4_FCOE_ASIC) {
+ memcpy(hw->hw_ver,
+ csio_fcoe_adapters[adap_type].model_no, 16);
+ memcpy(hw->model_desc,
+ csio_fcoe_adapters[adap_type].description, 32);
+ } else {
+ char tempName[32] = "Chelsio FCoE Controller";
+ memcpy(hw->model_desc, tempName, 32);
+
+ CSIO_DB_ASSERT(0);
+ }
+ }
+} /* csio_hw_set_description */
+
+/**
+ * csio_hw_init - Initialize HW module.
+ * @hw: Pointer to HW module.
+ *
+ * Initialize the members of the HW module.
+ */
+int
+csio_hw_init(struct csio_hw *hw)
+{
+ int rv = -EINVAL;
+ uint32_t i;
+ uint16_t ven_id, dev_id;
+ struct csio_evt_msg *evt_entry;
+
+ INIT_LIST_HEAD(&hw->sm.sm_list);
+ csio_init_state(&hw->sm, csio_hws_uninit);
+ spin_lock_init(&hw->lock);
+ INIT_LIST_HEAD(&hw->sln_head);
+
+ /* Get the PCI vendor & device id */
+ csio_hw_get_device_id(hw);
+
+ strcpy(hw->name, CSIO_HW_NAME);
+
+ /* Set the model & its description */
+
+ ven_id = hw->params.pci.vendor_id;
+ dev_id = hw->params.pci.device_id;
+
+ csio_hw_set_description(hw, ven_id, dev_id);
+
+ /* Initialize default log level */
+ hw->params.log_level = (uint32_t) csio_dbg_level;
+
+ csio_set_fwevt_intr_idx(hw, -1);
+ csio_set_nondata_intr_idx(hw, -1);
+
+ /* Init all the modules: Mailbox, WorkRequest and Transport */
+ if (csio_mbm_init(csio_hw_to_mbm(hw), hw, csio_hw_mb_timer))
+ goto err;
+
+ rv = csio_wrm_init(csio_hw_to_wrm(hw), hw);
+ if (rv)
+ goto err_mbm_exit;
+
+ rv = csio_scsim_init(csio_hw_to_scsim(hw), hw);
+ if (rv)
+ goto err_wrm_exit;
+
+ rv = csio_mgmtm_init(csio_hw_to_mgmtm(hw), hw);
+ if (rv)
+ goto err_scsim_exit;
+ /* Pre-allocate evtq and initialize them */
+ INIT_LIST_HEAD(&hw->evt_active_q);
+ INIT_LIST_HEAD(&hw->evt_free_q);
+ for (i = 0; i < csio_evtq_sz; i++) {
+
+ evt_entry = kzalloc(sizeof(struct csio_evt_msg), GFP_KERNEL);
+ if (!evt_entry) {
+ csio_err(hw, "Failed to initialize eventq");
+ goto err_evtq_cleanup;
+ }
+
+ list_add_tail(&evt_entry->list, &hw->evt_free_q);
+ CSIO_INC_STATS(hw, n_evt_freeq);
+ }
+
+ hw->dev_num = dev_num;
+ dev_num++;
+
+ return 0;
+
+err_evtq_cleanup:
+ csio_evtq_cleanup(hw);
+ csio_mgmtm_exit(csio_hw_to_mgmtm(hw));
+err_scsim_exit:
+ csio_scsim_exit(csio_hw_to_scsim(hw));
+err_wrm_exit:
+ csio_wrm_exit(csio_hw_to_wrm(hw), hw);
+err_mbm_exit:
+ csio_mbm_exit(csio_hw_to_mbm(hw));
+err:
+ return rv;
+}
+
+/**
+ * csio_hw_exit - Un-initialize HW module.
+ * @hw: Pointer to HW module.
+ *
+ */
+void
+csio_hw_exit(struct csio_hw *hw)
+{
+ csio_evtq_cleanup(hw);
+ csio_mgmtm_exit(csio_hw_to_mgmtm(hw));
+ csio_scsim_exit(csio_hw_to_scsim(hw));
+ csio_wrm_exit(csio_hw_to_wrm(hw), hw);
+ csio_mbm_exit(csio_hw_to_mbm(hw));
+}
diff --git a/drivers/scsi/csiostor/csio_hw.h b/drivers/scsi/csiostor/csio_hw.h
new file mode 100644
index 00000000000..9edcca4c71a
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_hw.h
@@ -0,0 +1,665 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CSIO_HW_H__
+#define __CSIO_HW_H__
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/compiler.h>
+#include <linux/cdev.h>
+#include <linux/list.h>
+#include <linux/mempool.h>
+#include <linux/io.h>
+#include <linux/spinlock_types.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include "csio_wr.h"
+#include "csio_mb.h"
+#include "csio_scsi.h"
+#include "csio_defs.h"
+#include "t4_regs.h"
+#include "t4_msg.h"
+
+/*
+ * An error value used by host. Should not clash with FW defined return values.
+ */
+#define FW_HOSTERROR 255
+
+#define CSIO_FW_FNAME "cxgb4/t4fw.bin"
+#define CSIO_CF_FNAME "cxgb4/t4-config.txt"
+
+#define FW_VERSION_MAJOR 1
+#define FW_VERSION_MINOR 2
+#define FW_VERSION_MICRO 8
+
+#define CSIO_HW_NAME "Chelsio FCoE Adapter"
+#define CSIO_MAX_PFN 8
+#define CSIO_MAX_PPORTS 4
+
+#define CSIO_MAX_LUN 0xFFFF
+#define CSIO_MAX_QUEUE 2048
+#define CSIO_MAX_CMD_PER_LUN 32
+#define CSIO_MAX_DDP_BUF_SIZE (1024 * 1024)
+#define CSIO_MAX_SECTOR_SIZE 128
+
+/* Interrupts */
+#define CSIO_EXTRA_MSI_IQS 2 /* Extra iqs for INTX/MSI mode
+ * (Forward intr iq + fw iq) */
+#define CSIO_EXTRA_VECS 2 /* non-data + FW evt */
+#define CSIO_MAX_SCSI_CPU 128
+#define CSIO_MAX_SCSI_QSETS (CSIO_MAX_SCSI_CPU * CSIO_MAX_PPORTS)
+#define CSIO_MAX_MSIX_VECS (CSIO_MAX_SCSI_QSETS + CSIO_EXTRA_VECS)
+
+/* Queues */
+enum {
+ CSIO_INTR_WRSIZE = 128,
+ CSIO_INTR_IQSIZE = ((CSIO_MAX_MSIX_VECS + 1) * CSIO_INTR_WRSIZE),
+ CSIO_FWEVT_WRSIZE = 128,
+ CSIO_FWEVT_IQLEN = 128,
+ CSIO_FWEVT_FLBUFS = 64,
+ CSIO_FWEVT_IQSIZE = (CSIO_FWEVT_WRSIZE * CSIO_FWEVT_IQLEN),
+ CSIO_HW_NIQ = 1,
+ CSIO_HW_NFLQ = 1,
+ CSIO_HW_NEQ = 1,
+ CSIO_HW_NINTXQ = 1,
+};
+
+struct csio_msix_entries {
+ unsigned short vector; /* Vector assigned by pci_enable_msix */
+ void *dev_id; /* Priv object associated w/ this msix*/
+ char desc[24]; /* Description of this vector */
+};
+
+struct csio_scsi_qset {
+ int iq_idx; /* Ingress index */
+ int eq_idx; /* Egress index */
+ uint32_t intr_idx; /* MSIX Vector index */
+};
+
+struct csio_scsi_cpu_info {
+ int16_t max_cpus;
+};
+
+extern int csio_dbg_level;
+extern int csio_force_master;
+extern unsigned int csio_port_mask;
+extern int csio_msi;
+
+#define CSIO_VENDOR_ID 0x1425
+#define CSIO_ASIC_DEVID_PROTO_MASK 0xFF00
+#define CSIO_ASIC_DEVID_TYPE_MASK 0x00FF
+#define CSIO_FPGA 0xA000
+#define CSIO_T4_FCOE_ASIC 0x4600
+
+#define CSIO_GLBL_INTR_MASK (CIM | MPS | PL | PCIE | MC | EDC0 | \
+ EDC1 | LE | TP | MA | PM_TX | PM_RX | \
+ ULP_RX | CPL_SWITCH | SGE | \
+ ULP_TX | SF)
+
+/*
+ * Hard parameters used to initialize the card in the absence of a
+ * configuration file.
+ */
+enum {
+ /* General */
+ CSIO_SGE_DBFIFO_INT_THRESH = 10,
+
+ CSIO_SGE_RX_DMA_OFFSET = 2,
+
+ CSIO_SGE_FLBUF_SIZE1 = 65536,
+ CSIO_SGE_FLBUF_SIZE2 = 1536,
+ CSIO_SGE_FLBUF_SIZE3 = 9024,
+ CSIO_SGE_FLBUF_SIZE4 = 9216,
+ CSIO_SGE_FLBUF_SIZE5 = 2048,
+ CSIO_SGE_FLBUF_SIZE6 = 128,
+ CSIO_SGE_FLBUF_SIZE7 = 8192,
+ CSIO_SGE_FLBUF_SIZE8 = 16384,
+
+ CSIO_SGE_TIMER_VAL_0 = 5,
+ CSIO_SGE_TIMER_VAL_1 = 10,
+ CSIO_SGE_TIMER_VAL_2 = 20,
+ CSIO_SGE_TIMER_VAL_3 = 50,
+ CSIO_SGE_TIMER_VAL_4 = 100,
+ CSIO_SGE_TIMER_VAL_5 = 200,
+
+ CSIO_SGE_INT_CNT_VAL_0 = 1,
+ CSIO_SGE_INT_CNT_VAL_1 = 4,
+ CSIO_SGE_INT_CNT_VAL_2 = 8,
+ CSIO_SGE_INT_CNT_VAL_3 = 16,
+
+ /* Storage specific - used by FW_PFVF_CMD */
+ CSIO_WX_CAPS = FW_CMD_CAP_PF, /* w/x all */
+ CSIO_R_CAPS = FW_CMD_CAP_PF, /* r all */
+ CSIO_NVI = 4,
+ CSIO_NIQ_FLINT = 34,
+ CSIO_NETH_CTRL = 32,
+ CSIO_NEQ = 66,
+ CSIO_NEXACTF = 32,
+ CSIO_CMASK = FW_PFVF_CMD_CMASK_MASK,
+ CSIO_PMASK = FW_PFVF_CMD_PMASK_MASK,
+};
+
+/* Slowpath events */
+enum csio_evt {
+ CSIO_EVT_FW = 0, /* FW event */
+ CSIO_EVT_MBX, /* MBX event */
+ CSIO_EVT_SCN, /* State change notification */
+ CSIO_EVT_DEV_LOSS, /* Device loss event */
+ CSIO_EVT_MAX, /* Max supported event */
+};
+
+#define CSIO_EVT_MSG_SIZE 512
+#define CSIO_EVTQ_SIZE 512
+
+/* Event msg */
+struct csio_evt_msg {
+ struct list_head list; /* evt queue*/
+ enum csio_evt type;
+ uint8_t data[CSIO_EVT_MSG_SIZE];
+};
+
+enum {
+ EEPROMVSIZE = 32768, /* Serial EEPROM virtual address space size */
+ SERNUM_LEN = 16, /* Serial # length */
+ EC_LEN = 16, /* E/C length */
+ ID_LEN = 16, /* ID length */
+ TRACE_LEN = 112, /* length of trace data and mask */
+};
+
+enum {
+ SF_PAGE_SIZE = 256, /* serial flash page size */
+ SF_SEC_SIZE = 64 * 1024, /* serial flash sector size */
+ SF_SIZE = SF_SEC_SIZE * 16, /* serial flash size */
+};
+
+enum { MEM_EDC0, MEM_EDC1, MEM_MC };
+
+enum {
+ MEMWIN0_APERTURE = 2048,
+ MEMWIN0_BASE = 0x1b800,
+ MEMWIN1_APERTURE = 32768,
+ MEMWIN1_BASE = 0x28000,
+ MEMWIN2_APERTURE = 65536,
+ MEMWIN2_BASE = 0x30000,
+};
+
+/* serial flash and firmware constants */
+enum {
+ SF_ATTEMPTS = 10, /* max retries for SF operations */
+
+ /* flash command opcodes */
+ SF_PROG_PAGE = 2, /* program page */
+ SF_WR_DISABLE = 4, /* disable writes */
+ SF_RD_STATUS = 5, /* read status register */
+ SF_WR_ENABLE = 6, /* enable writes */
+ SF_RD_DATA_FAST = 0xb, /* read flash */
+ SF_RD_ID = 0x9f, /* read ID */
+ SF_ERASE_SECTOR = 0xd8, /* erase sector */
+
+ FW_START_SEC = 8, /* first flash sector for FW */
+ FW_END_SEC = 15, /* last flash sector for FW */
+ FW_IMG_START = FW_START_SEC * SF_SEC_SIZE,
+ FW_MAX_SIZE = (FW_END_SEC - FW_START_SEC + 1) * SF_SEC_SIZE,
+
+ FLASH_CFG_MAX_SIZE = 0x10000 , /* max size of the flash config file*/
+ FLASH_CFG_OFFSET = 0x1f0000,
+ FLASH_CFG_START_SEC = FLASH_CFG_OFFSET / SF_SEC_SIZE,
+ FPGA_FLASH_CFG_OFFSET = 0xf0000 , /* if FPGA mode, then cfg file is
+ * at 1MB - 64KB */
+ FPGA_FLASH_CFG_START_SEC = FPGA_FLASH_CFG_OFFSET / SF_SEC_SIZE,
+};
+
+/*
+ * Flash layout.
+ */
+#define FLASH_START(start) ((start) * SF_SEC_SIZE)
+#define FLASH_MAX_SIZE(nsecs) ((nsecs) * SF_SEC_SIZE)
+
+enum {
+ /*
+ * Location of firmware image in FLASH.
+ */
+ FLASH_FW_START_SEC = 8,
+ FLASH_FW_NSECS = 8,
+ FLASH_FW_START = FLASH_START(FLASH_FW_START_SEC),
+ FLASH_FW_MAX_SIZE = FLASH_MAX_SIZE(FLASH_FW_NSECS),
+
+};
+
+#undef FLASH_START
+#undef FLASH_MAX_SIZE
+
+/* Management module */
+enum {
+ CSIO_MGMT_EQ_WRSIZE = 512,
+ CSIO_MGMT_IQ_WRSIZE = 128,
+ CSIO_MGMT_EQLEN = 64,
+ CSIO_MGMT_IQLEN = 64,
+};
+
+#define CSIO_MGMT_EQSIZE (CSIO_MGMT_EQLEN * CSIO_MGMT_EQ_WRSIZE)
+#define CSIO_MGMT_IQSIZE (CSIO_MGMT_IQLEN * CSIO_MGMT_IQ_WRSIZE)
+
+/* mgmt module stats */
+struct csio_mgmtm_stats {
+ uint32_t n_abort_req; /* Total abort request */
+ uint32_t n_abort_rsp; /* Total abort response */
+ uint32_t n_close_req; /* Total close request */
+ uint32_t n_close_rsp; /* Total close response */
+ uint32_t n_err; /* Total Errors */
+ uint32_t n_drop; /* Total request dropped */
+ uint32_t n_active; /* Count of active_q */
+ uint32_t n_cbfn; /* Count of cbfn_q */
+};
+
+/* MGMT module */
+struct csio_mgmtm {
+ struct csio_hw *hw; /* Pointer to HW moduel */
+ int eq_idx; /* Egress queue index */
+ int iq_idx; /* Ingress queue index */
+ int msi_vec; /* MSI vector */
+ struct list_head active_q; /* Outstanding ELS/CT */
+ struct list_head abort_q; /* Outstanding abort req */
+ struct list_head cbfn_q; /* Completion queue */
+ struct list_head mgmt_req_freelist; /* Free poll of reqs */
+ /* ELSCT request freelist*/
+ struct timer_list mgmt_timer; /* MGMT timer */
+ struct csio_mgmtm_stats stats; /* ELS/CT stats */
+};
+
+struct csio_adap_desc {
+ char model_no[16];
+ char description[32];
+};
+
+struct pci_params {
+ uint16_t vendor_id;
+ uint16_t device_id;
+ uint32_t vpd_cap_addr;
+ uint16_t speed;
+ uint8_t width;
+};
+
+/* User configurable hw parameters */
+struct csio_hw_params {
+ uint32_t sf_size; /* serial flash
+ * size in bytes
+ */
+ uint32_t sf_nsec; /* # of flash sectors */
+ struct pci_params pci;
+ uint32_t log_level; /* Module-level for
+ * debug log.
+ */
+};
+
+struct csio_vpd {
+ uint32_t cclk;
+ uint8_t ec[EC_LEN + 1];
+ uint8_t sn[SERNUM_LEN + 1];
+ uint8_t id[ID_LEN + 1];
+};
+
+struct csio_pport {
+ uint16_t pcap;
+ uint8_t portid;
+ uint8_t link_status;
+ uint16_t link_speed;
+ uint8_t mac[6];
+ uint8_t mod_type;
+ uint8_t rsvd1;
+ uint8_t rsvd2;
+ uint8_t rsvd3;
+};
+
+/* fcoe resource information */
+struct csio_fcoe_res_info {
+ uint16_t e_d_tov;
+ uint16_t r_a_tov_seq;
+ uint16_t r_a_tov_els;
+ uint16_t r_r_tov;
+ uint32_t max_xchgs;
+ uint32_t max_ssns;
+ uint32_t used_xchgs;
+ uint32_t used_ssns;
+ uint32_t max_fcfs;
+ uint32_t max_vnps;
+ uint32_t used_fcfs;
+ uint32_t used_vnps;
+};
+
+/* HW State machine Events */
+enum csio_hw_ev {
+ CSIO_HWE_CFG = (uint32_t)1, /* Starts off the State machine */
+ CSIO_HWE_INIT, /* Config done, start Init */
+ CSIO_HWE_INIT_DONE, /* Init Mailboxes sent, HW ready */
+ CSIO_HWE_FATAL, /* Fatal error during initialization */
+ CSIO_HWE_PCIERR_DETECTED,/* PCI error recovery detetced */
+ CSIO_HWE_PCIERR_SLOT_RESET, /* Slot reset after PCI recoviery */
+ CSIO_HWE_PCIERR_RESUME, /* Resume after PCI error recovery */
+ CSIO_HWE_QUIESCED, /* HBA quiesced */
+ CSIO_HWE_HBA_RESET, /* HBA reset requested */
+ CSIO_HWE_HBA_RESET_DONE, /* HBA reset completed */
+ CSIO_HWE_FW_DLOAD, /* FW download requested */
+ CSIO_HWE_PCI_REMOVE, /* PCI de-instantiation */
+ CSIO_HWE_SUSPEND, /* HW suspend for Online(hot) replacement */
+ CSIO_HWE_RESUME, /* HW resume for Online(hot) replacement */
+ CSIO_HWE_MAX, /* Max HW event */
+};
+
+/* hw stats */
+struct csio_hw_stats {
+ uint32_t n_evt_activeq; /* Number of event in active Q */
+ uint32_t n_evt_freeq; /* Number of event in free Q */
+ uint32_t n_evt_drop; /* Number of event droped */
+ uint32_t n_evt_unexp; /* Number of unexpected events */
+ uint32_t n_pcich_offline;/* Number of pci channel offline */
+ uint32_t n_lnlkup_miss; /* Number of lnode lookup miss */
+ uint32_t n_cpl_fw6_msg; /* Number of cpl fw6 message*/
+ uint32_t n_cpl_fw6_pld; /* Number of cpl fw6 payload*/
+ uint32_t n_cpl_unexp; /* Number of unexpected cpl */
+ uint32_t n_mbint_unexp; /* Number of unexpected mbox */
+ /* interrupt */
+ uint32_t n_plint_unexp; /* Number of unexpected PL */
+ /* interrupt */
+ uint32_t n_plint_cnt; /* Number of PL interrupt */
+ uint32_t n_int_stray; /* Number of stray interrupt */
+ uint32_t n_err; /* Number of hw errors */
+ uint32_t n_err_fatal; /* Number of fatal errors */
+ uint32_t n_err_nomem; /* Number of memory alloc failure */
+ uint32_t n_err_io; /* Number of IO failure */
+ enum csio_hw_ev n_evt_sm[CSIO_HWE_MAX]; /* Number of sm events */
+ uint64_t n_reset_start; /* Start time after the reset */
+ uint32_t rsvd1;
+};
+
+/* Defines for hw->flags */
+#define CSIO_HWF_MASTER 0x00000001 /* This is the Master
+ * function for the
+ * card.
+ */
+#define CSIO_HWF_HW_INTR_ENABLED 0x00000002 /* Are HW Interrupt
+ * enable bit set?
+ */
+#define CSIO_HWF_FWEVT_PENDING 0x00000004 /* FW events pending */
+#define CSIO_HWF_Q_MEM_ALLOCED 0x00000008 /* Queues have been
+ * allocated memory.
+ */
+#define CSIO_HWF_Q_FW_ALLOCED 0x00000010 /* Queues have been
+ * allocated in FW.
+ */
+#define CSIO_HWF_VPD_VALID 0x00000020 /* Valid VPD copied */
+#define CSIO_HWF_DEVID_CACHED 0X00000040 /* PCI vendor & device
+ * id cached */
+#define CSIO_HWF_FWEVT_STOP 0x00000080 /* Stop processing
+ * FW events
+ */
+#define CSIO_HWF_USING_SOFT_PARAMS 0x00000100 /* Using FW config
+ * params
+ */
+#define CSIO_HWF_HOST_INTR_ENABLED 0x00000200 /* Are host interrupts
+ * enabled?
+ */
+
+#define csio_is_hw_intr_enabled(__hw) \
+ ((__hw)->flags & CSIO_HWF_HW_INTR_ENABLED)
+#define csio_is_host_intr_enabled(__hw) \
+ ((__hw)->flags & CSIO_HWF_HOST_INTR_ENABLED)
+#define csio_is_hw_master(__hw) ((__hw)->flags & CSIO_HWF_MASTER)
+#define csio_is_valid_vpd(__hw) ((__hw)->flags & CSIO_HWF_VPD_VALID)
+#define csio_is_dev_id_cached(__hw) ((__hw)->flags & CSIO_HWF_DEVID_CACHED)
+#define csio_valid_vpd_copied(__hw) ((__hw)->flags |= CSIO_HWF_VPD_VALID)
+#define csio_dev_id_cached(__hw) ((__hw)->flags |= CSIO_HWF_DEVID_CACHED)
+
+/* Defines for intr_mode */
+enum csio_intr_mode {
+ CSIO_IM_NONE = 0,
+ CSIO_IM_INTX = 1,
+ CSIO_IM_MSI = 2,
+ CSIO_IM_MSIX = 3,
+};
+
+/* Master HW structure: One per function */
+struct csio_hw {
+ struct csio_sm sm; /* State machine: should
+ * be the 1st member.
+ */
+ spinlock_t lock; /* Lock for hw */
+
+ struct csio_scsim scsim; /* SCSI module*/
+ struct csio_wrm wrm; /* Work request module*/
+ struct pci_dev *pdev; /* PCI device */
+
+ void __iomem *regstart; /* Virtual address of
+ * register map
+ */
+ /* SCSI queue sets */
+ uint32_t num_sqsets; /* Number of SCSI
+ * queue sets */
+ uint32_t num_scsi_msix_cpus; /* Number of CPUs that
+ * will be used
+ * for ingress
+ * processing.
+ */
+
+ struct csio_scsi_qset sqset[CSIO_MAX_PPORTS][CSIO_MAX_SCSI_CPU];
+ struct csio_scsi_cpu_info scsi_cpu_info[CSIO_MAX_PPORTS];
+
+ uint32_t evtflag; /* Event flag */
+ uint32_t flags; /* HW flags */
+
+ struct csio_mgmtm mgmtm; /* management module */
+ struct csio_mbm mbm; /* Mailbox module */
+
+ /* Lnodes */
+ uint32_t num_lns; /* Number of lnodes */
+ struct csio_lnode *rln; /* Root lnode */
+ struct list_head sln_head; /* Sibling node list
+ * list
+ */
+ int intr_iq_idx; /* Forward interrupt
+ * queue.
+ */
+ int fwevt_iq_idx; /* FW evt queue */
+ struct work_struct evtq_work; /* Worker thread for
+ * HW events.
+ */
+ struct list_head evt_free_q; /* freelist of evt
+ * elements
+ */
+ struct list_head evt_active_q; /* active evt queue*/
+
+ /* board related info */
+ char name[32];
+ char hw_ver[16];
+ char model_desc[32];
+ char drv_version[32];
+ char fwrev_str[32];
+ uint32_t optrom_ver;
+ uint32_t fwrev;
+ uint32_t tp_vers;
+ char chip_ver;
+ uint32_t cfg_finiver;
+ uint32_t cfg_finicsum;
+ uint32_t cfg_cfcsum;
+ uint8_t cfg_csum_status;
+ uint8_t cfg_store;
+ enum csio_dev_state fw_state;
+ struct csio_vpd vpd;
+
+ uint8_t pfn; /* Physical Function
+ * number
+ */
+ uint32_t port_vec; /* Port vector */
+ uint8_t num_pports; /* Number of physical
+ * ports.
+ */
+ uint8_t rst_retries; /* Reset retries */
+ uint8_t cur_evt; /* current s/m evt */
+ uint8_t prev_evt; /* Previous s/m evt */
+ uint32_t dev_num; /* device number */
+ struct csio_pport pport[CSIO_MAX_PPORTS]; /* Ports (XGMACs) */
+ struct csio_hw_params params; /* Hw parameters */
+
+ struct pci_pool *scsi_pci_pool; /* PCI pool for SCSI */
+ mempool_t *mb_mempool; /* Mailbox memory pool*/
+ mempool_t *rnode_mempool; /* rnode memory pool */
+
+ /* Interrupt */
+ enum csio_intr_mode intr_mode; /* INTx, MSI, MSIX */
+ uint32_t fwevt_intr_idx; /* FW evt MSIX/interrupt
+ * index
+ */
+ uint32_t nondata_intr_idx; /* nondata MSIX/intr
+ * idx
+ */
+
+ uint8_t cfg_neq; /* FW configured no of
+ * egress queues
+ */
+ uint8_t cfg_niq; /* FW configured no of
+ * iq queues.
+ */
+
+ struct csio_fcoe_res_info fres_info; /* Fcoe resource info */
+
+ /* MSIX vectors */
+ struct csio_msix_entries msix_entries[CSIO_MAX_MSIX_VECS];
+
+ struct dentry *debugfs_root; /* Debug FS */
+ struct csio_hw_stats stats; /* Hw statistics */
+};
+
+/* Register access macros */
+#define csio_reg(_b, _r) ((_b) + (_r))
+
+#define csio_rd_reg8(_h, _r) readb(csio_reg((_h)->regstart, (_r)))
+#define csio_rd_reg16(_h, _r) readw(csio_reg((_h)->regstart, (_r)))
+#define csio_rd_reg32(_h, _r) readl(csio_reg((_h)->regstart, (_r)))
+#define csio_rd_reg64(_h, _r) readq(csio_reg((_h)->regstart, (_r)))
+
+#define csio_wr_reg8(_h, _v, _r) writeb((_v), \
+ csio_reg((_h)->regstart, (_r)))
+#define csio_wr_reg16(_h, _v, _r) writew((_v), \
+ csio_reg((_h)->regstart, (_r)))
+#define csio_wr_reg32(_h, _v, _r) writel((_v), \
+ csio_reg((_h)->regstart, (_r)))
+#define csio_wr_reg64(_h, _v, _r) writeq((_v), \
+ csio_reg((_h)->regstart, (_r)))
+
+void csio_set_reg_field(struct csio_hw *, uint32_t, uint32_t, uint32_t);
+
+/* Core clocks <==> uSecs */
+static inline uint32_t
+csio_core_ticks_to_us(struct csio_hw *hw, uint32_t ticks)
+{
+ /* add Core Clock / 2 to round ticks to nearest uS */
+ return (ticks * 1000 + hw->vpd.cclk/2) / hw->vpd.cclk;
+}
+
+static inline uint32_t
+csio_us_to_core_ticks(struct csio_hw *hw, uint32_t us)
+{
+ return (us * hw->vpd.cclk) / 1000;
+}
+
+/* Easy access macros */
+#define csio_hw_to_wrm(hw) ((struct csio_wrm *)(&(hw)->wrm))
+#define csio_hw_to_mbm(hw) ((struct csio_mbm *)(&(hw)->mbm))
+#define csio_hw_to_scsim(hw) ((struct csio_scsim *)(&(hw)->scsim))
+#define csio_hw_to_mgmtm(hw) ((struct csio_mgmtm *)(&(hw)->mgmtm))
+
+#define CSIO_PCI_BUS(hw) ((hw)->pdev->bus->number)
+#define CSIO_PCI_DEV(hw) (PCI_SLOT((hw)->pdev->devfn))
+#define CSIO_PCI_FUNC(hw) (PCI_FUNC((hw)->pdev->devfn))
+
+#define csio_set_fwevt_intr_idx(_h, _i) ((_h)->fwevt_intr_idx = (_i))
+#define csio_get_fwevt_intr_idx(_h) ((_h)->fwevt_intr_idx)
+#define csio_set_nondata_intr_idx(_h, _i) ((_h)->nondata_intr_idx = (_i))
+#define csio_get_nondata_intr_idx(_h) ((_h)->nondata_intr_idx)
+
+/* Printing/logging */
+#define CSIO_DEVID(__dev) ((__dev)->dev_num)
+#define CSIO_DEVID_LO(__dev) (CSIO_DEVID((__dev)) & 0xFFFF)
+#define CSIO_DEVID_HI(__dev) ((CSIO_DEVID((__dev)) >> 16) & 0xFFFF)
+
+#define csio_info(__hw, __fmt, ...) \
+ dev_info(&(__hw)->pdev->dev, __fmt, ##__VA_ARGS__)
+
+#define csio_fatal(__hw, __fmt, ...) \
+ dev_crit(&(__hw)->pdev->dev, __fmt, ##__VA_ARGS__)
+
+#define csio_err(__hw, __fmt, ...) \
+ dev_err(&(__hw)->pdev->dev, __fmt, ##__VA_ARGS__)
+
+#define csio_warn(__hw, __fmt, ...) \
+ dev_warn(&(__hw)->pdev->dev, __fmt, ##__VA_ARGS__)
+
+#ifdef __CSIO_DEBUG__
+#define csio_dbg(__hw, __fmt, ...) \
+ csio_info((__hw), __fmt, ##__VA_ARGS__);
+#else
+#define csio_dbg(__hw, __fmt, ...)
+#endif
+
+int csio_mgmt_req_lookup(struct csio_mgmtm *, struct csio_ioreq *);
+void csio_hw_intr_disable(struct csio_hw *);
+int csio_hw_slow_intr_handler(struct csio_hw *hw);
+int csio_hw_start(struct csio_hw *);
+int csio_hw_stop(struct csio_hw *);
+int csio_hw_reset(struct csio_hw *);
+int csio_is_hw_ready(struct csio_hw *);
+int csio_is_hw_removing(struct csio_hw *);
+
+int csio_fwevtq_handler(struct csio_hw *);
+void csio_evtq_worker(struct work_struct *);
+int csio_enqueue_evt(struct csio_hw *hw, enum csio_evt type,
+ void *evt_msg, uint16_t len);
+void csio_evtq_flush(struct csio_hw *hw);
+
+int csio_request_irqs(struct csio_hw *);
+void csio_intr_enable(struct csio_hw *);
+void csio_intr_disable(struct csio_hw *, bool);
+
+struct csio_lnode *csio_lnode_alloc(struct csio_hw *);
+int csio_config_queues(struct csio_hw *);
+
+int csio_hw_mc_read(struct csio_hw *, uint32_t, __be32 *, uint64_t *);
+int csio_hw_edc_read(struct csio_hw *, int, uint32_t, __be32 *, uint64_t *);
+int csio_hw_init(struct csio_hw *);
+void csio_hw_exit(struct csio_hw *);
+#endif /* ifndef __CSIO_HW_H__ */
diff --git a/drivers/scsi/csiostor/csio_init.c b/drivers/scsi/csiostor/csio_init.c
new file mode 100644
index 00000000000..fdd408ff80a
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_init.c
@@ -0,0 +1,1274 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/aer.h>
+#include <linux/mm.h>
+#include <linux/notifier.h>
+#include <linux/kdebug.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/string.h>
+#include <linux/export.h>
+
+#include "csio_init.h"
+#include "csio_defs.h"
+
+#define CSIO_MIN_MEMPOOL_SZ 64
+
+static struct dentry *csio_debugfs_root;
+
+static struct scsi_transport_template *csio_fcoe_transport;
+static struct scsi_transport_template *csio_fcoe_transport_vport;
+
+/*
+ * debugfs support
+ */
+static int
+csio_mem_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t
+csio_mem_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ loff_t pos = *ppos;
+ loff_t avail = file->f_path.dentry->d_inode->i_size;
+ unsigned int mem = (uintptr_t)file->private_data & 3;
+ struct csio_hw *hw = file->private_data - mem;
+
+ if (pos < 0)
+ return -EINVAL;
+ if (pos >= avail)
+ return 0;
+ if (count > avail - pos)
+ count = avail - pos;
+
+ while (count) {
+ size_t len;
+ int ret, ofst;
+ __be32 data[16];
+
+ if (mem == MEM_MC)
+ ret = csio_hw_mc_read(hw, pos, data, NULL);
+ else
+ ret = csio_hw_edc_read(hw, mem, pos, data, NULL);
+ if (ret)
+ return ret;
+
+ ofst = pos % sizeof(data);
+ len = min(count, sizeof(data) - ofst);
+ if (copy_to_user(buf, (u8 *)data + ofst, len))
+ return -EFAULT;
+
+ buf += len;
+ pos += len;
+ count -= len;
+ }
+ count = pos - *ppos;
+ *ppos = pos;
+ return count;
+}
+
+static const struct file_operations csio_mem_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = csio_mem_open,
+ .read = csio_mem_read,
+ .llseek = default_llseek,
+};
+
+static void __devinit
+csio_add_debugfs_mem(struct csio_hw *hw, const char *name,
+ unsigned int idx, unsigned int size_mb)
+{
+ struct dentry *de;
+
+ de = debugfs_create_file(name, S_IRUSR, hw->debugfs_root,
+ (void *)hw + idx, &csio_mem_debugfs_fops);
+ if (de && de->d_inode)
+ de->d_inode->i_size = size_mb << 20;
+}
+
+static int __devinit
+csio_setup_debugfs(struct csio_hw *hw)
+{
+ int i;
+
+ if (IS_ERR_OR_NULL(hw->debugfs_root))
+ return -1;
+
+ i = csio_rd_reg32(hw, MA_TARGET_MEM_ENABLE);
+ if (i & EDRAM0_ENABLE)
+ csio_add_debugfs_mem(hw, "edc0", MEM_EDC0, 5);
+ if (i & EDRAM1_ENABLE)
+ csio_add_debugfs_mem(hw, "edc1", MEM_EDC1, 5);
+ if (i & EXT_MEM_ENABLE)
+ csio_add_debugfs_mem(hw, "mc", MEM_MC,
+ EXT_MEM_SIZE_GET(csio_rd_reg32(hw, MA_EXT_MEMORY_BAR)));
+ return 0;
+}
+
+/*
+ * csio_dfs_create - Creates and sets up per-hw debugfs.
+ *
+ */
+static int
+csio_dfs_create(struct csio_hw *hw)
+{
+ if (csio_debugfs_root) {
+ hw->debugfs_root = debugfs_create_dir(pci_name(hw->pdev),
+ csio_debugfs_root);
+ csio_setup_debugfs(hw);
+ }
+
+ return 0;
+}
+
+/*
+ * csio_dfs_destroy - Destroys per-hw debugfs.
+ */
+static int
+csio_dfs_destroy(struct csio_hw *hw)
+{
+ if (hw->debugfs_root)
+ debugfs_remove_recursive(hw->debugfs_root);
+
+ return 0;
+}
+
+/*
+ * csio_dfs_init - Debug filesystem initialization for the module.
+ *
+ */
+static int
+csio_dfs_init(void)
+{
+ csio_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
+ if (!csio_debugfs_root)
+ pr_warn("Could not create debugfs entry, continuing\n");
+
+ return 0;
+}
+
+/*
+ * csio_dfs_exit - debugfs cleanup for the module.
+ */
+static void
+csio_dfs_exit(void)
+{
+ debugfs_remove(csio_debugfs_root);
+}
+
+/*
+ * csio_pci_init - PCI initialization.
+ * @pdev: PCI device.
+ * @bars: Bitmask of bars to be requested.
+ *
+ * Initializes the PCI function by enabling MMIO, setting bus
+ * mastership and setting DMA mask.
+ */
+static int
+csio_pci_init(struct pci_dev *pdev, int *bars)
+{
+ int rv = -ENODEV;
+
+ *bars = pci_select_bars(pdev, IORESOURCE_MEM);
+
+ if (pci_enable_device_mem(pdev))
+ goto err;
+
+ if (pci_request_selected_regions(pdev, *bars, KBUILD_MODNAME))
+ goto err_disable_device;
+
+ pci_set_master(pdev);
+ pci_try_set_mwi(pdev);
+
+ if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
+ } else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+ } else {
+ dev_err(&pdev->dev, "No suitable DMA available.\n");
+ goto err_release_regions;
+ }
+
+ return 0;
+
+err_release_regions:
+ pci_release_selected_regions(pdev, *bars);
+err_disable_device:
+ pci_disable_device(pdev);
+err:
+ return rv;
+
+}
+
+/*
+ * csio_pci_exit - PCI unitialization.
+ * @pdev: PCI device.
+ * @bars: Bars to be released.
+ *
+ */
+static void
+csio_pci_exit(struct pci_dev *pdev, int *bars)
+{
+ pci_release_selected_regions(pdev, *bars);
+ pci_disable_device(pdev);
+}
+
+/*
+ * csio_hw_init_workers - Initialize the HW module's worker threads.
+ * @hw: HW module.
+ *
+ */
+static void
+csio_hw_init_workers(struct csio_hw *hw)
+{
+ INIT_WORK(&hw->evtq_work, csio_evtq_worker);
+}
+
+static void
+csio_hw_exit_workers(struct csio_hw *hw)
+{
+ cancel_work_sync(&hw->evtq_work);
+ flush_scheduled_work();
+}
+
+static int
+csio_create_queues(struct csio_hw *hw)
+{
+ int i, j;
+ struct csio_mgmtm *mgmtm = csio_hw_to_mgmtm(hw);
+ int rv;
+ struct csio_scsi_cpu_info *info;
+
+ if (hw->flags & CSIO_HWF_Q_FW_ALLOCED)
+ return 0;
+
+ if (hw->intr_mode != CSIO_IM_MSIX) {
+ rv = csio_wr_iq_create(hw, NULL, hw->intr_iq_idx,
+ 0, hw->pport[0].portid, false, NULL);
+ if (rv != 0) {
+ csio_err(hw, " Forward Interrupt IQ failed!: %d\n", rv);
+ return rv;
+ }
+ }
+
+ /* FW event queue */
+ rv = csio_wr_iq_create(hw, NULL, hw->fwevt_iq_idx,
+ csio_get_fwevt_intr_idx(hw),
+ hw->pport[0].portid, true, NULL);
+ if (rv != 0) {
+ csio_err(hw, "FW event IQ config failed!: %d\n", rv);
+ return rv;
+ }
+
+ /* Create mgmt queue */
+ rv = csio_wr_eq_create(hw, NULL, mgmtm->eq_idx,
+ mgmtm->iq_idx, hw->pport[0].portid, NULL);
+
+ if (rv != 0) {
+ csio_err(hw, "Mgmt EQ create failed!: %d\n", rv);
+ goto err;
+ }
+
+ /* Create SCSI queues */
+ for (i = 0; i < hw->num_pports; i++) {
+ info = &hw->scsi_cpu_info[i];
+
+ for (j = 0; j < info->max_cpus; j++) {
+ struct csio_scsi_qset *sqset = &hw->sqset[i][j];
+
+ rv = csio_wr_iq_create(hw, NULL, sqset->iq_idx,
+ sqset->intr_idx, i, false, NULL);
+ if (rv != 0) {
+ csio_err(hw,
+ "SCSI module IQ config failed [%d][%d]:%d\n",
+ i, j, rv);
+ goto err;
+ }
+ rv = csio_wr_eq_create(hw, NULL, sqset->eq_idx,
+ sqset->iq_idx, i, NULL);
+ if (rv != 0) {
+ csio_err(hw,
+ "SCSI module EQ config failed [%d][%d]:%d\n",
+ i, j, rv);
+ goto err;
+ }
+ } /* for all CPUs */
+ } /* For all ports */
+
+ hw->flags |= CSIO_HWF_Q_FW_ALLOCED;
+ return 0;
+err:
+ csio_wr_destroy_queues(hw, true);
+ return -EINVAL;
+}
+
+/*
+ * csio_config_queues - Configure the DMA queues.
+ * @hw: HW module.
+ *
+ * Allocates memory for queues are registers them with FW.
+ */
+int
+csio_config_queues(struct csio_hw *hw)
+{
+ int i, j, idx, k = 0;
+ int rv;
+ struct csio_scsi_qset *sqset;
+ struct csio_mgmtm *mgmtm = csio_hw_to_mgmtm(hw);
+ struct csio_scsi_qset *orig;
+ struct csio_scsi_cpu_info *info;
+
+ if (hw->flags & CSIO_HWF_Q_MEM_ALLOCED)
+ return csio_create_queues(hw);
+
+ /* Calculate number of SCSI queues for MSIX we would like */
+ hw->num_scsi_msix_cpus = num_online_cpus();
+ hw->num_sqsets = num_online_cpus() * hw->num_pports;
+
+ if (hw->num_sqsets > CSIO_MAX_SCSI_QSETS) {
+ hw->num_sqsets = CSIO_MAX_SCSI_QSETS;
+ hw->num_scsi_msix_cpus = CSIO_MAX_SCSI_CPU;
+ }
+
+ /* Initialize max_cpus, may get reduced during msix allocations */
+ for (i = 0; i < hw->num_pports; i++)
+ hw->scsi_cpu_info[i].max_cpus = hw->num_scsi_msix_cpus;
+
+ csio_dbg(hw, "nsqsets:%d scpus:%d\n",
+ hw->num_sqsets, hw->num_scsi_msix_cpus);
+
+ csio_intr_enable(hw);
+
+ if (hw->intr_mode != CSIO_IM_MSIX) {
+
+ /* Allocate Forward interrupt iq. */
+ hw->intr_iq_idx = csio_wr_alloc_q(hw, CSIO_INTR_IQSIZE,
+ CSIO_INTR_WRSIZE, CSIO_INGRESS,
+ (void *)hw, 0, 0, NULL);
+ if (hw->intr_iq_idx == -1) {
+ csio_err(hw,
+ "Forward interrupt queue creation failed\n");
+ goto intr_disable;
+ }
+ }
+
+ /* Allocate the FW evt queue */
+ hw->fwevt_iq_idx = csio_wr_alloc_q(hw, CSIO_FWEVT_IQSIZE,
+ CSIO_FWEVT_WRSIZE,
+ CSIO_INGRESS, (void *)hw,
+ CSIO_FWEVT_FLBUFS, 0,
+ csio_fwevt_intx_handler);
+ if (hw->fwevt_iq_idx == -1) {
+ csio_err(hw, "FW evt queue creation failed\n");
+ goto intr_disable;
+ }
+
+ /* Allocate the mgmt queue */
+ mgmtm->eq_idx = csio_wr_alloc_q(hw, CSIO_MGMT_EQSIZE,
+ CSIO_MGMT_EQ_WRSIZE,
+ CSIO_EGRESS, (void *)hw, 0, 0, NULL);
+ if (mgmtm->eq_idx == -1) {
+ csio_err(hw, "Failed to alloc egress queue for mgmt module\n");
+ goto intr_disable;
+ }
+
+ /* Use FW IQ for MGMT req completion */
+ mgmtm->iq_idx = hw->fwevt_iq_idx;
+
+ /* Allocate SCSI queues */
+ for (i = 0; i < hw->num_pports; i++) {
+ info = &hw->scsi_cpu_info[i];
+
+ for (j = 0; j < hw->num_scsi_msix_cpus; j++) {
+ sqset = &hw->sqset[i][j];
+
+ if (j >= info->max_cpus) {
+ k = j % info->max_cpus;
+ orig = &hw->sqset[i][k];
+ sqset->eq_idx = orig->eq_idx;
+ sqset->iq_idx = orig->iq_idx;
+ continue;
+ }
+
+ idx = csio_wr_alloc_q(hw, csio_scsi_eqsize, 0,
+ CSIO_EGRESS, (void *)hw, 0, 0,
+ NULL);
+ if (idx == -1) {
+ csio_err(hw, "EQ creation failed for idx:%d\n",
+ idx);
+ goto intr_disable;
+ }
+
+ sqset->eq_idx = idx;
+
+ idx = csio_wr_alloc_q(hw, CSIO_SCSI_IQSIZE,
+ CSIO_SCSI_IQ_WRSZ, CSIO_INGRESS,
+ (void *)hw, 0, 0,
+ csio_scsi_intx_handler);
+ if (idx == -1) {
+ csio_err(hw, "IQ creation failed for idx:%d\n",
+ idx);
+ goto intr_disable;
+ }
+ sqset->iq_idx = idx;
+ } /* for all CPUs */
+ } /* For all ports */
+
+ hw->flags |= CSIO_HWF_Q_MEM_ALLOCED;
+
+ rv = csio_create_queues(hw);
+ if (rv != 0)
+ goto intr_disable;
+
+ /*
+ * Now request IRQs for the vectors. In the event of a failure,
+ * cleanup is handled internally by this function.
+ */
+ rv = csio_request_irqs(hw);
+ if (rv != 0)
+ return -EINVAL;
+
+ return 0;
+
+intr_disable:
+ csio_intr_disable(hw, false);
+
+ return -EINVAL;
+}
+
+static int
+csio_resource_alloc(struct csio_hw *hw)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ int rv = -ENOMEM;
+
+ wrm->num_q = ((CSIO_MAX_SCSI_QSETS * 2) + CSIO_HW_NIQ +
+ CSIO_HW_NEQ + CSIO_HW_NFLQ + CSIO_HW_NINTXQ);
+
+ hw->mb_mempool = mempool_create_kmalloc_pool(CSIO_MIN_MEMPOOL_SZ,
+ sizeof(struct csio_mb));
+ if (!hw->mb_mempool)
+ goto err;
+
+ hw->rnode_mempool = mempool_create_kmalloc_pool(CSIO_MIN_MEMPOOL_SZ,
+ sizeof(struct csio_rnode));
+ if (!hw->rnode_mempool)
+ goto err_free_mb_mempool;
+
+ hw->scsi_pci_pool = pci_pool_create("csio_scsi_pci_pool", hw->pdev,
+ CSIO_SCSI_RSP_LEN, 8, 0);
+ if (!hw->scsi_pci_pool)
+ goto err_free_rn_pool;
+
+ return 0;
+
+err_free_rn_pool:
+ mempool_destroy(hw->rnode_mempool);
+ hw->rnode_mempool = NULL;
+err_free_mb_mempool:
+ mempool_destroy(hw->mb_mempool);
+ hw->mb_mempool = NULL;
+err:
+ return rv;
+}
+
+static void
+csio_resource_free(struct csio_hw *hw)
+{
+ pci_pool_destroy(hw->scsi_pci_pool);
+ hw->scsi_pci_pool = NULL;
+ mempool_destroy(hw->rnode_mempool);
+ hw->rnode_mempool = NULL;
+ mempool_destroy(hw->mb_mempool);
+ hw->mb_mempool = NULL;
+}
+
+/*
+ * csio_hw_alloc - Allocate and initialize the HW module.
+ * @pdev: PCI device.
+ *
+ * Allocates HW structure, DMA, memory resources, maps BARS to
+ * host memory and initializes HW module.
+ */
+static struct csio_hw * __devinit
+csio_hw_alloc(struct pci_dev *pdev)
+{
+ struct csio_hw *hw;
+
+ hw = kzalloc(sizeof(struct csio_hw), GFP_KERNEL);
+ if (!hw)
+ goto err;
+
+ hw->pdev = pdev;
+ strncpy(hw->drv_version, CSIO_DRV_VERSION, 32);
+
+ /* memory pool/DMA pool allocation */
+ if (csio_resource_alloc(hw))
+ goto err_free_hw;
+
+ /* Get the start address of registers from BAR 0 */
+ hw->regstart = ioremap_nocache(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ if (!hw->regstart) {
+ csio_err(hw, "Could not map BAR 0, regstart = %p\n",
+ hw->regstart);
+ goto err_resource_free;
+ }
+
+ csio_hw_init_workers(hw);
+
+ if (csio_hw_init(hw))
+ goto err_unmap_bar;
+
+ csio_dfs_create(hw);
+
+ csio_dbg(hw, "hw:%p\n", hw);
+
+ return hw;
+
+err_unmap_bar:
+ csio_hw_exit_workers(hw);
+ iounmap(hw->regstart);
+err_resource_free:
+ csio_resource_free(hw);
+err_free_hw:
+ kfree(hw);
+err:
+ return NULL;
+}
+
+/*
+ * csio_hw_free - Uninitialize and free the HW module.
+ * @hw: The HW module
+ *
+ * Disable interrupts, uninit the HW module, free resources, free hw.
+ */
+static void
+csio_hw_free(struct csio_hw *hw)
+{
+ csio_intr_disable(hw, true);
+ csio_hw_exit_workers(hw);
+ csio_hw_exit(hw);
+ iounmap(hw->regstart);
+ csio_dfs_destroy(hw);
+ csio_resource_free(hw);
+ kfree(hw);
+}
+
+/**
+ * csio_shost_init - Create and initialize the lnode module.
+ * @hw: The HW module.
+ * @dev: The device associated with this invocation.
+ * @probe: Called from probe context or not?
+ * @os_pln: Parent lnode if any.
+ *
+ * Allocates lnode structure via scsi_host_alloc, initializes
+ * shost, initializes lnode module and registers with SCSI ML
+ * via scsi_host_add. This function is shared between physical and
+ * virtual node ports.
+ */
+struct csio_lnode *
+csio_shost_init(struct csio_hw *hw, struct device *dev,
+ bool probe, struct csio_lnode *pln)
+{
+ struct Scsi_Host *shost = NULL;
+ struct csio_lnode *ln;
+
+ csio_fcoe_shost_template.cmd_per_lun = csio_lun_qdepth;
+ csio_fcoe_shost_vport_template.cmd_per_lun = csio_lun_qdepth;
+
+ /*
+ * hw->pdev is the physical port's PCI dev structure,
+ * which will be different from the NPIV dev structure.
+ */
+ if (dev == &hw->pdev->dev)
+ shost = scsi_host_alloc(
+ &csio_fcoe_shost_template,
+ sizeof(struct csio_lnode));
+ else
+ shost = scsi_host_alloc(
+ &csio_fcoe_shost_vport_template,
+ sizeof(struct csio_lnode));
+
+ if (!shost)
+ goto err;
+
+ ln = shost_priv(shost);
+ memset(ln, 0, sizeof(struct csio_lnode));
+
+ /* Link common lnode to this lnode */
+ ln->dev_num = (shost->host_no << 16);
+
+ shost->can_queue = CSIO_MAX_QUEUE;
+ shost->this_id = -1;
+ shost->unique_id = shost->host_no;
+ shost->max_cmd_len = 16; /* Max CDB length supported */
+ shost->max_id = min_t(uint32_t, csio_fcoe_rnodes,
+ hw->fres_info.max_ssns);
+ shost->max_lun = CSIO_MAX_LUN;
+ if (dev == &hw->pdev->dev)
+ shost->transportt = csio_fcoe_transport;
+ else
+ shost->transportt = csio_fcoe_transport_vport;
+
+ /* root lnode */
+ if (!hw->rln)
+ hw->rln = ln;
+
+ /* Other initialization here: Common, Transport specific */
+ if (csio_lnode_init(ln, hw, pln))
+ goto err_shost_put;
+
+ if (scsi_add_host(shost, dev))
+ goto err_lnode_exit;
+
+ return ln;
+
+err_lnode_exit:
+ csio_lnode_exit(ln);
+err_shost_put:
+ scsi_host_put(shost);
+err:
+ return NULL;
+}
+
+/**
+ * csio_shost_exit - De-instantiate the shost.
+ * @ln: The lnode module corresponding to the shost.
+ *
+ */
+void
+csio_shost_exit(struct csio_lnode *ln)
+{
+ struct Scsi_Host *shost = csio_ln_to_shost(ln);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ /* Inform transport */
+ fc_remove_host(shost);
+
+ /* Inform SCSI ML */
+ scsi_remove_host(shost);
+
+ /* Flush all the events, so that any rnode removal events
+ * already queued are all handled, before we remove the lnode.
+ */
+ spin_lock_irq(&hw->lock);
+ csio_evtq_flush(hw);
+ spin_unlock_irq(&hw->lock);
+
+ csio_lnode_exit(ln);
+ scsi_host_put(shost);
+}
+
+struct csio_lnode *
+csio_lnode_alloc(struct csio_hw *hw)
+{
+ return csio_shost_init(hw, &hw->pdev->dev, false, NULL);
+}
+
+void
+csio_lnodes_block_request(struct csio_hw *hw)
+{
+ struct Scsi_Host *shost;
+ struct csio_lnode *sln;
+ struct csio_lnode *ln;
+ struct list_head *cur_ln, *cur_cln;
+ struct csio_lnode **lnode_list;
+ int cur_cnt = 0, ii;
+
+ lnode_list = kzalloc((sizeof(struct csio_lnode *) * hw->num_lns),
+ GFP_KERNEL);
+ if (!lnode_list) {
+ csio_err(hw, "Failed to allocate lnodes_list");
+ return;
+ }
+
+ spin_lock_irq(&hw->lock);
+ /* Traverse sibling lnodes */
+ list_for_each(cur_ln, &hw->sln_head) {
+ sln = (struct csio_lnode *) cur_ln;
+ lnode_list[cur_cnt++] = sln;
+
+ /* Traverse children lnodes */
+ list_for_each(cur_cln, &sln->cln_head)
+ lnode_list[cur_cnt++] = (struct csio_lnode *) cur_cln;
+ }
+ spin_unlock_irq(&hw->lock);
+
+ for (ii = 0; ii < cur_cnt; ii++) {
+ csio_dbg(hw, "Blocking IOs on lnode: %p\n", lnode_list[ii]);
+ ln = lnode_list[ii];
+ shost = csio_ln_to_shost(ln);
+ scsi_block_requests(shost);
+
+ }
+ kfree(lnode_list);
+}
+
+void
+csio_lnodes_unblock_request(struct csio_hw *hw)
+{
+ struct csio_lnode *ln;
+ struct Scsi_Host *shost;
+ struct csio_lnode *sln;
+ struct list_head *cur_ln, *cur_cln;
+ struct csio_lnode **lnode_list;
+ int cur_cnt = 0, ii;
+
+ lnode_list = kzalloc((sizeof(struct csio_lnode *) * hw->num_lns),
+ GFP_KERNEL);
+ if (!lnode_list) {
+ csio_err(hw, "Failed to allocate lnodes_list");
+ return;
+ }
+
+ spin_lock_irq(&hw->lock);
+ /* Traverse sibling lnodes */
+ list_for_each(cur_ln, &hw->sln_head) {
+ sln = (struct csio_lnode *) cur_ln;
+ lnode_list[cur_cnt++] = sln;
+
+ /* Traverse children lnodes */
+ list_for_each(cur_cln, &sln->cln_head)
+ lnode_list[cur_cnt++] = (struct csio_lnode *) cur_cln;
+ }
+ spin_unlock_irq(&hw->lock);
+
+ for (ii = 0; ii < cur_cnt; ii++) {
+ csio_dbg(hw, "unblocking IOs on lnode: %p\n", lnode_list[ii]);
+ ln = lnode_list[ii];
+ shost = csio_ln_to_shost(ln);
+ scsi_unblock_requests(shost);
+ }
+ kfree(lnode_list);
+}
+
+void
+csio_lnodes_block_by_port(struct csio_hw *hw, uint8_t portid)
+{
+ struct csio_lnode *ln;
+ struct Scsi_Host *shost;
+ struct csio_lnode *sln;
+ struct list_head *cur_ln, *cur_cln;
+ struct csio_lnode **lnode_list;
+ int cur_cnt = 0, ii;
+
+ lnode_list = kzalloc((sizeof(struct csio_lnode *) * hw->num_lns),
+ GFP_KERNEL);
+ if (!lnode_list) {
+ csio_err(hw, "Failed to allocate lnodes_list");
+ return;
+ }
+
+ spin_lock_irq(&hw->lock);
+ /* Traverse sibling lnodes */
+ list_for_each(cur_ln, &hw->sln_head) {
+ sln = (struct csio_lnode *) cur_ln;
+ if (sln->portid != portid)
+ continue;
+
+ lnode_list[cur_cnt++] = sln;
+
+ /* Traverse children lnodes */
+ list_for_each(cur_cln, &sln->cln_head)
+ lnode_list[cur_cnt++] = (struct csio_lnode *) cur_cln;
+ }
+ spin_unlock_irq(&hw->lock);
+
+ for (ii = 0; ii < cur_cnt; ii++) {
+ csio_dbg(hw, "Blocking IOs on lnode: %p\n", lnode_list[ii]);
+ ln = lnode_list[ii];
+ shost = csio_ln_to_shost(ln);
+ scsi_block_requests(shost);
+ }
+ kfree(lnode_list);
+}
+
+void
+csio_lnodes_unblock_by_port(struct csio_hw *hw, uint8_t portid)
+{
+ struct csio_lnode *ln;
+ struct Scsi_Host *shost;
+ struct csio_lnode *sln;
+ struct list_head *cur_ln, *cur_cln;
+ struct csio_lnode **lnode_list;
+ int cur_cnt = 0, ii;
+
+ lnode_list = kzalloc((sizeof(struct csio_lnode *) * hw->num_lns),
+ GFP_KERNEL);
+ if (!lnode_list) {
+ csio_err(hw, "Failed to allocate lnodes_list");
+ return;
+ }
+
+ spin_lock_irq(&hw->lock);
+ /* Traverse sibling lnodes */
+ list_for_each(cur_ln, &hw->sln_head) {
+ sln = (struct csio_lnode *) cur_ln;
+ if (sln->portid != portid)
+ continue;
+ lnode_list[cur_cnt++] = sln;
+
+ /* Traverse children lnodes */
+ list_for_each(cur_cln, &sln->cln_head)
+ lnode_list[cur_cnt++] = (struct csio_lnode *) cur_cln;
+ }
+ spin_unlock_irq(&hw->lock);
+
+ for (ii = 0; ii < cur_cnt; ii++) {
+ csio_dbg(hw, "unblocking IOs on lnode: %p\n", lnode_list[ii]);
+ ln = lnode_list[ii];
+ shost = csio_ln_to_shost(ln);
+ scsi_unblock_requests(shost);
+ }
+ kfree(lnode_list);
+}
+
+void
+csio_lnodes_exit(struct csio_hw *hw, bool npiv)
+{
+ struct csio_lnode *sln;
+ struct csio_lnode *ln;
+ struct list_head *cur_ln, *cur_cln;
+ struct csio_lnode **lnode_list;
+ int cur_cnt = 0, ii;
+
+ lnode_list = kzalloc((sizeof(struct csio_lnode *) * hw->num_lns),
+ GFP_KERNEL);
+ if (!lnode_list) {
+ csio_err(hw, "lnodes_exit: Failed to allocate lnodes_list.\n");
+ return;
+ }
+
+ /* Get all child lnodes(NPIV ports) */
+ spin_lock_irq(&hw->lock);
+ list_for_each(cur_ln, &hw->sln_head) {
+ sln = (struct csio_lnode *) cur_ln;
+
+ /* Traverse children lnodes */
+ list_for_each(cur_cln, &sln->cln_head)
+ lnode_list[cur_cnt++] = (struct csio_lnode *) cur_cln;
+ }
+ spin_unlock_irq(&hw->lock);
+
+ /* Delete NPIV lnodes */
+ for (ii = 0; ii < cur_cnt; ii++) {
+ csio_dbg(hw, "Deleting child lnode: %p\n", lnode_list[ii]);
+ ln = lnode_list[ii];
+ fc_vport_terminate(ln->fc_vport);
+ }
+
+ /* Delete only npiv lnodes */
+ if (npiv)
+ goto free_lnodes;
+
+ cur_cnt = 0;
+ /* Get all physical lnodes */
+ spin_lock_irq(&hw->lock);
+ /* Traverse sibling lnodes */
+ list_for_each(cur_ln, &hw->sln_head) {
+ sln = (struct csio_lnode *) cur_ln;
+ lnode_list[cur_cnt++] = sln;
+ }
+ spin_unlock_irq(&hw->lock);
+
+ /* Delete physical lnodes */
+ for (ii = 0; ii < cur_cnt; ii++) {
+ csio_dbg(hw, "Deleting parent lnode: %p\n", lnode_list[ii]);
+ csio_shost_exit(lnode_list[ii]);
+ }
+
+free_lnodes:
+ kfree(lnode_list);
+}
+
+/*
+ * csio_lnode_init_post: Set lnode attributes after starting HW.
+ * @ln: lnode.
+ *
+ */
+static void
+csio_lnode_init_post(struct csio_lnode *ln)
+{
+ struct Scsi_Host *shost = csio_ln_to_shost(ln);
+
+ csio_fchost_attr_init(ln);
+
+ scsi_scan_host(shost);
+}
+
+/*
+ * csio_probe_one - Instantiate this function.
+ * @pdev: PCI device
+ * @id: Device ID
+ *
+ * This is the .probe() callback of the driver. This function:
+ * - Initializes the PCI function by enabling MMIO, setting bus
+ * mastership and setting DMA mask.
+ * - Allocates HW structure, DMA, memory resources, maps BARS to
+ * host memory and initializes HW module.
+ * - Allocates lnode structure via scsi_host_alloc, initializes
+ * shost, initialized lnode module and registers with SCSI ML
+ * via scsi_host_add.
+ * - Enables interrupts, and starts the chip by kicking off the
+ * HW state machine.
+ * - Once hardware is ready, initiated scan of the host via
+ * scsi_scan_host.
+ */
+static int __devinit
+csio_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ int rv;
+ int bars;
+ int i;
+ struct csio_hw *hw;
+ struct csio_lnode *ln;
+
+ rv = csio_pci_init(pdev, &bars);
+ if (rv)
+ goto err;
+
+ hw = csio_hw_alloc(pdev);
+ if (!hw) {
+ rv = -ENODEV;
+ goto err_pci_exit;
+ }
+
+ pci_set_drvdata(pdev, hw);
+
+ if (csio_hw_start(hw) != 0) {
+ dev_err(&pdev->dev,
+ "Failed to start FW, continuing in debug mode.\n");
+ return 0;
+ }
+
+ sprintf(hw->fwrev_str, "%u.%u.%u.%u\n",
+ FW_HDR_FW_VER_MAJOR_GET(hw->fwrev),
+ FW_HDR_FW_VER_MINOR_GET(hw->fwrev),
+ FW_HDR_FW_VER_MICRO_GET(hw->fwrev),
+ FW_HDR_FW_VER_BUILD_GET(hw->fwrev));
+
+ for (i = 0; i < hw->num_pports; i++) {
+ ln = csio_shost_init(hw, &pdev->dev, true, NULL);
+ if (!ln) {
+ rv = -ENODEV;
+ break;
+ }
+ /* Initialize portid */
+ ln->portid = hw->pport[i].portid;
+
+ spin_lock_irq(&hw->lock);
+ if (csio_lnode_start(ln) != 0)
+ rv = -ENODEV;
+ spin_unlock_irq(&hw->lock);
+
+ if (rv)
+ break;
+
+ csio_lnode_init_post(ln);
+ }
+
+ if (rv)
+ goto err_lnode_exit;
+
+ return 0;
+
+err_lnode_exit:
+ csio_lnodes_block_request(hw);
+ spin_lock_irq(&hw->lock);
+ csio_hw_stop(hw);
+ spin_unlock_irq(&hw->lock);
+ csio_lnodes_unblock_request(hw);
+ pci_set_drvdata(hw->pdev, NULL);
+ csio_lnodes_exit(hw, 0);
+ csio_hw_free(hw);
+err_pci_exit:
+ csio_pci_exit(pdev, &bars);
+err:
+ dev_err(&pdev->dev, "probe of device failed: %d\n", rv);
+ return rv;
+}
+
+/*
+ * csio_remove_one - Remove one instance of the driver at this PCI function.
+ * @pdev: PCI device
+ *
+ * Used during hotplug operation.
+ */
+static void __devexit
+csio_remove_one(struct pci_dev *pdev)
+{
+ struct csio_hw *hw = pci_get_drvdata(pdev);
+ int bars = pci_select_bars(pdev, IORESOURCE_MEM);
+
+ csio_lnodes_block_request(hw);
+ spin_lock_irq(&hw->lock);
+
+ /* Stops lnode, Rnode s/m
+ * Quiesce IOs.
+ * All sessions with remote ports are unregistered.
+ */
+ csio_hw_stop(hw);
+ spin_unlock_irq(&hw->lock);
+ csio_lnodes_unblock_request(hw);
+
+ csio_lnodes_exit(hw, 0);
+ csio_hw_free(hw);
+ pci_set_drvdata(pdev, NULL);
+ csio_pci_exit(pdev, &bars);
+}
+
+/*
+ * csio_pci_error_detected - PCI error was detected
+ * @pdev: PCI device
+ *
+ */
+static pci_ers_result_t
+csio_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
+{
+ struct csio_hw *hw = pci_get_drvdata(pdev);
+
+ csio_lnodes_block_request(hw);
+ spin_lock_irq(&hw->lock);
+
+ /* Post PCI error detected evt to HW s/m
+ * HW s/m handles this evt by quiescing IOs, unregisters rports
+ * and finally takes the device to offline.
+ */
+ csio_post_event(&hw->sm, CSIO_HWE_PCIERR_DETECTED);
+ spin_unlock_irq(&hw->lock);
+ csio_lnodes_unblock_request(hw);
+ csio_lnodes_exit(hw, 0);
+ csio_intr_disable(hw, true);
+ pci_disable_device(pdev);
+ return state == pci_channel_io_perm_failure ?
+ PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_NEED_RESET;
+}
+
+/*
+ * csio_pci_slot_reset - PCI slot has been reset.
+ * @pdev: PCI device
+ *
+ */
+static pci_ers_result_t
+csio_pci_slot_reset(struct pci_dev *pdev)
+{
+ struct csio_hw *hw = pci_get_drvdata(pdev);
+ int ready;
+
+ if (pci_enable_device(pdev)) {
+ dev_err(&pdev->dev, "cannot re-enable device in slot reset\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ pci_set_master(pdev);
+ pci_restore_state(pdev);
+ pci_save_state(pdev);
+ pci_cleanup_aer_uncorrect_error_status(pdev);
+
+ /* Bring HW s/m to ready state.
+ * but don't resume IOs.
+ */
+ spin_lock_irq(&hw->lock);
+ csio_post_event(&hw->sm, CSIO_HWE_PCIERR_SLOT_RESET);
+ ready = csio_is_hw_ready(hw);
+ spin_unlock_irq(&hw->lock);
+
+ if (ready) {
+ return PCI_ERS_RESULT_RECOVERED;
+ } else {
+ dev_err(&pdev->dev, "Can't initialize HW when in slot reset\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+}
+
+/*
+ * csio_pci_resume - Resume normal operations
+ * @pdev: PCI device
+ *
+ */
+static void
+csio_pci_resume(struct pci_dev *pdev)
+{
+ struct csio_hw *hw = pci_get_drvdata(pdev);
+ struct csio_lnode *ln;
+ int rv = 0;
+ int i;
+
+ /* Bring the LINK UP and Resume IO */
+
+ for (i = 0; i < hw->num_pports; i++) {
+ ln = csio_shost_init(hw, &pdev->dev, true, NULL);
+ if (!ln) {
+ rv = -ENODEV;
+ break;
+ }
+ /* Initialize portid */
+ ln->portid = hw->pport[i].portid;
+
+ spin_lock_irq(&hw->lock);
+ if (csio_lnode_start(ln) != 0)
+ rv = -ENODEV;
+ spin_unlock_irq(&hw->lock);
+
+ if (rv)
+ break;
+
+ csio_lnode_init_post(ln);
+ }
+
+ if (rv)
+ goto err_resume_exit;
+
+ return;
+
+err_resume_exit:
+ csio_lnodes_block_request(hw);
+ spin_lock_irq(&hw->lock);
+ csio_hw_stop(hw);
+ spin_unlock_irq(&hw->lock);
+ csio_lnodes_unblock_request(hw);
+ csio_lnodes_exit(hw, 0);
+ csio_hw_free(hw);
+ dev_err(&pdev->dev, "resume of device failed: %d\n", rv);
+}
+
+static struct pci_error_handlers csio_err_handler = {
+ .error_detected = csio_pci_error_detected,
+ .slot_reset = csio_pci_slot_reset,
+ .resume = csio_pci_resume,
+};
+
+static DEFINE_PCI_DEVICE_TABLE(csio_pci_tbl) = {
+ CSIO_DEVICE(CSIO_DEVID_T440DBG_FCOE, 0), /* T440DBG FCOE */
+ CSIO_DEVICE(CSIO_DEVID_T420CR_FCOE, 0), /* T420CR FCOE */
+ CSIO_DEVICE(CSIO_DEVID_T422CR_FCOE, 0), /* T422CR FCOE */
+ CSIO_DEVICE(CSIO_DEVID_T440CR_FCOE, 0), /* T440CR FCOE */
+ CSIO_DEVICE(CSIO_DEVID_T420BCH_FCOE, 0), /* T420BCH FCOE */
+ CSIO_DEVICE(CSIO_DEVID_T440BCH_FCOE, 0), /* T440BCH FCOE */
+ CSIO_DEVICE(CSIO_DEVID_T440CH_FCOE, 0), /* T440CH FCOE */
+ CSIO_DEVICE(CSIO_DEVID_T420SO_FCOE, 0), /* T420SO FCOE */
+ CSIO_DEVICE(CSIO_DEVID_T420CX_FCOE, 0), /* T420CX FCOE */
+ CSIO_DEVICE(CSIO_DEVID_T420BT_FCOE, 0), /* T420BT FCOE */
+ CSIO_DEVICE(CSIO_DEVID_T404BT_FCOE, 0), /* T404BT FCOE */
+ CSIO_DEVICE(CSIO_DEVID_B420_FCOE, 0), /* B420 FCOE */
+ CSIO_DEVICE(CSIO_DEVID_B404_FCOE, 0), /* B404 FCOE */
+ CSIO_DEVICE(CSIO_DEVID_T480CR_FCOE, 0), /* T480 CR FCOE */
+ CSIO_DEVICE(CSIO_DEVID_T440LPCR_FCOE, 0), /* T440 LP-CR FCOE */
+ CSIO_DEVICE(CSIO_DEVID_PE10K, 0), /* PE10K FCOE */
+ CSIO_DEVICE(CSIO_DEVID_PE10K_PF1, 0), /* PE10K FCOE on PF1 */
+ { 0, 0, 0, 0, 0, 0, 0 }
+};
+
+
+static struct pci_driver csio_pci_driver = {
+ .name = KBUILD_MODNAME,
+ .driver = {
+ .owner = THIS_MODULE,
+ },
+ .id_table = csio_pci_tbl,
+ .probe = csio_probe_one,
+ .remove = csio_remove_one,
+ .err_handler = &csio_err_handler,
+};
+
+/*
+ * csio_init - Chelsio storage driver initialization function.
+ *
+ */
+static int __init
+csio_init(void)
+{
+ int rv = -ENOMEM;
+
+ pr_info("%s %s\n", CSIO_DRV_DESC, CSIO_DRV_VERSION);
+
+ csio_dfs_init();
+
+ csio_fcoe_transport = fc_attach_transport(&csio_fc_transport_funcs);
+ if (!csio_fcoe_transport)
+ goto err;
+
+ csio_fcoe_transport_vport =
+ fc_attach_transport(&csio_fc_transport_vport_funcs);
+ if (!csio_fcoe_transport_vport)
+ goto err_vport;
+
+ rv = pci_register_driver(&csio_pci_driver);
+ if (rv)
+ goto err_pci;
+
+ return 0;
+
+err_pci:
+ fc_release_transport(csio_fcoe_transport_vport);
+err_vport:
+ fc_release_transport(csio_fcoe_transport);
+err:
+ csio_dfs_exit();
+ return rv;
+}
+
+/*
+ * csio_exit - Chelsio storage driver uninitialization .
+ *
+ * Function that gets called in the unload path.
+ */
+static void __exit
+csio_exit(void)
+{
+ pci_unregister_driver(&csio_pci_driver);
+ csio_dfs_exit();
+ fc_release_transport(csio_fcoe_transport_vport);
+ fc_release_transport(csio_fcoe_transport);
+}
+
+module_init(csio_init);
+module_exit(csio_exit);
+MODULE_AUTHOR(CSIO_DRV_AUTHOR);
+MODULE_DESCRIPTION(CSIO_DRV_DESC);
+MODULE_LICENSE(CSIO_DRV_LICENSE);
+MODULE_DEVICE_TABLE(pci, csio_pci_tbl);
+MODULE_VERSION(CSIO_DRV_VERSION);
+MODULE_FIRMWARE(CSIO_FW_FNAME);
diff --git a/drivers/scsi/csiostor/csio_init.h b/drivers/scsi/csiostor/csio_init.h
new file mode 100644
index 00000000000..0838fd7ec9c
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_init.h
@@ -0,0 +1,158 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CSIO_INIT_H__
+#define __CSIO_INIT_H__
+
+#include <linux/pci.h>
+#include <linux/if_ether.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include "csio_scsi.h"
+#include "csio_lnode.h"
+#include "csio_rnode.h"
+#include "csio_hw.h"
+
+#define CSIO_DRV_AUTHOR "Chelsio Communications"
+#define CSIO_DRV_LICENSE "Dual BSD/GPL"
+#define CSIO_DRV_DESC "Chelsio FCoE driver"
+#define CSIO_DRV_VERSION "1.0.0"
+
+#define CSIO_DEVICE(devid, idx) \
+{ PCI_VENDOR_ID_CHELSIO, (devid), PCI_ANY_ID, PCI_ANY_ID, 0, 0, (idx) }
+
+#define CSIO_IS_T4_FPGA(_dev) (((_dev) == CSIO_DEVID_PE10K) ||\
+ ((_dev) == CSIO_DEVID_PE10K_PF1))
+
+/* FCoE device IDs */
+#define CSIO_DEVID_PE10K 0xA000
+#define CSIO_DEVID_PE10K_PF1 0xA001
+#define CSIO_DEVID_T440DBG_FCOE 0x4600
+#define CSIO_DEVID_T420CR_FCOE 0x4601
+#define CSIO_DEVID_T422CR_FCOE 0x4602
+#define CSIO_DEVID_T440CR_FCOE 0x4603
+#define CSIO_DEVID_T420BCH_FCOE 0x4604
+#define CSIO_DEVID_T440BCH_FCOE 0x4605
+#define CSIO_DEVID_T440CH_FCOE 0x4606
+#define CSIO_DEVID_T420SO_FCOE 0x4607
+#define CSIO_DEVID_T420CX_FCOE 0x4608
+#define CSIO_DEVID_T420BT_FCOE 0x4609
+#define CSIO_DEVID_T404BT_FCOE 0x460A
+#define CSIO_DEVID_B420_FCOE 0x460B
+#define CSIO_DEVID_B404_FCOE 0x460C
+#define CSIO_DEVID_T480CR_FCOE 0x460D
+#define CSIO_DEVID_T440LPCR_FCOE 0x460E
+
+extern struct fc_function_template csio_fc_transport_funcs;
+extern struct fc_function_template csio_fc_transport_vport_funcs;
+
+void csio_fchost_attr_init(struct csio_lnode *);
+
+/* INTx handlers */
+void csio_scsi_intx_handler(struct csio_hw *, void *, uint32_t,
+ struct csio_fl_dma_buf *, void *);
+
+void csio_fwevt_intx_handler(struct csio_hw *, void *, uint32_t,
+ struct csio_fl_dma_buf *, void *);
+
+/* Common os lnode APIs */
+void csio_lnodes_block_request(struct csio_hw *);
+void csio_lnodes_unblock_request(struct csio_hw *);
+void csio_lnodes_block_by_port(struct csio_hw *, uint8_t);
+void csio_lnodes_unblock_by_port(struct csio_hw *, uint8_t);
+
+struct csio_lnode *csio_shost_init(struct csio_hw *, struct device *, bool,
+ struct csio_lnode *);
+void csio_shost_exit(struct csio_lnode *);
+void csio_lnodes_exit(struct csio_hw *, bool);
+
+static inline struct Scsi_Host *
+csio_ln_to_shost(struct csio_lnode *ln)
+{
+ return container_of((void *)ln, struct Scsi_Host, hostdata[0]);
+}
+
+/* SCSI -- locking version of get/put ioreqs */
+static inline struct csio_ioreq *
+csio_get_scsi_ioreq_lock(struct csio_hw *hw, struct csio_scsim *scsim)
+{
+ struct csio_ioreq *ioreq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&scsim->freelist_lock, flags);
+ ioreq = csio_get_scsi_ioreq(scsim);
+ spin_unlock_irqrestore(&scsim->freelist_lock, flags);
+
+ return ioreq;
+}
+
+static inline void
+csio_put_scsi_ioreq_lock(struct csio_hw *hw, struct csio_scsim *scsim,
+ struct csio_ioreq *ioreq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&scsim->freelist_lock, flags);
+ csio_put_scsi_ioreq(scsim, ioreq);
+ spin_unlock_irqrestore(&scsim->freelist_lock, flags);
+}
+
+/* Called in interrupt context */
+static inline void
+csio_put_scsi_ioreq_list_lock(struct csio_hw *hw, struct csio_scsim *scsim,
+ struct list_head *reqlist, int n)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&scsim->freelist_lock, flags);
+ csio_put_scsi_ioreq_list(scsim, reqlist, n);
+ spin_unlock_irqrestore(&scsim->freelist_lock, flags);
+}
+
+/* Called in interrupt context */
+static inline void
+csio_put_scsi_ddp_list_lock(struct csio_hw *hw, struct csio_scsim *scsim,
+ struct list_head *reqlist, int n)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hw->lock, flags);
+ csio_put_scsi_ddp_list(scsim, reqlist, n);
+ spin_unlock_irqrestore(&hw->lock, flags);
+}
+
+#endif /* ifndef __CSIO_INIT_H__ */
diff --git a/drivers/scsi/csiostor/csio_isr.c b/drivers/scsi/csiostor/csio_isr.c
new file mode 100644
index 00000000000..7ee9777ae2c
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_isr.c
@@ -0,0 +1,624 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/cpumask.h>
+#include <linux/string.h>
+
+#include "csio_init.h"
+#include "csio_hw.h"
+
+static irqreturn_t
+csio_nondata_isr(int irq, void *dev_id)
+{
+ struct csio_hw *hw = (struct csio_hw *) dev_id;
+ int rv;
+ unsigned long flags;
+
+ if (unlikely(!hw))
+ return IRQ_NONE;
+
+ if (unlikely(pci_channel_offline(hw->pdev))) {
+ CSIO_INC_STATS(hw, n_pcich_offline);
+ return IRQ_NONE;
+ }
+
+ spin_lock_irqsave(&hw->lock, flags);
+ csio_hw_slow_intr_handler(hw);
+ rv = csio_mb_isr_handler(hw);
+
+ if (rv == 0 && !(hw->flags & CSIO_HWF_FWEVT_PENDING)) {
+ hw->flags |= CSIO_HWF_FWEVT_PENDING;
+ spin_unlock_irqrestore(&hw->lock, flags);
+ schedule_work(&hw->evtq_work);
+ return IRQ_HANDLED;
+ }
+ spin_unlock_irqrestore(&hw->lock, flags);
+ return IRQ_HANDLED;
+}
+
+/*
+ * csio_fwevt_handler - Common FW event handler routine.
+ * @hw: HW module.
+ *
+ * This is the ISR for FW events. It is shared b/w MSIX
+ * and INTx handlers.
+ */
+static void
+csio_fwevt_handler(struct csio_hw *hw)
+{
+ int rv;
+ unsigned long flags;
+
+ rv = csio_fwevtq_handler(hw);
+
+ spin_lock_irqsave(&hw->lock, flags);
+ if (rv == 0 && !(hw->flags & CSIO_HWF_FWEVT_PENDING)) {
+ hw->flags |= CSIO_HWF_FWEVT_PENDING;
+ spin_unlock_irqrestore(&hw->lock, flags);
+ schedule_work(&hw->evtq_work);
+ return;
+ }
+ spin_unlock_irqrestore(&hw->lock, flags);
+
+} /* csio_fwevt_handler */
+
+/*
+ * csio_fwevt_isr() - FW events MSIX ISR
+ * @irq:
+ * @dev_id:
+ *
+ * Process WRs on the FW event queue.
+ *
+ */
+static irqreturn_t
+csio_fwevt_isr(int irq, void *dev_id)
+{
+ struct csio_hw *hw = (struct csio_hw *) dev_id;
+
+ if (unlikely(!hw))
+ return IRQ_NONE;
+
+ if (unlikely(pci_channel_offline(hw->pdev))) {
+ CSIO_INC_STATS(hw, n_pcich_offline);
+ return IRQ_NONE;
+ }
+
+ csio_fwevt_handler(hw);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * csio_fwevt_isr() - INTx wrapper for handling FW events.
+ * @irq:
+ * @dev_id:
+ */
+void
+csio_fwevt_intx_handler(struct csio_hw *hw, void *wr, uint32_t len,
+ struct csio_fl_dma_buf *flb, void *priv)
+{
+ csio_fwevt_handler(hw);
+} /* csio_fwevt_intx_handler */
+
+/*
+ * csio_process_scsi_cmpl - Process a SCSI WR completion.
+ * @hw: HW module.
+ * @wr: The completed WR from the ingress queue.
+ * @len: Length of the WR.
+ * @flb: Freelist buffer array.
+ *
+ */
+static void
+csio_process_scsi_cmpl(struct csio_hw *hw, void *wr, uint32_t len,
+ struct csio_fl_dma_buf *flb, void *cbfn_q)
+{
+ struct csio_ioreq *ioreq;
+ uint8_t *scsiwr;
+ uint8_t subop;
+ void *cmnd;
+ unsigned long flags;
+
+ ioreq = csio_scsi_cmpl_handler(hw, wr, len, flb, NULL, &scsiwr);
+ if (likely(ioreq)) {
+ if (unlikely(*scsiwr == FW_SCSI_ABRT_CLS_WR)) {
+ subop = FW_SCSI_ABRT_CLS_WR_SUB_OPCODE_GET(
+ ((struct fw_scsi_abrt_cls_wr *)
+ scsiwr)->sub_opcode_to_chk_all_io);
+
+ csio_dbg(hw, "%s cmpl recvd ioreq:%p status:%d\n",
+ subop ? "Close" : "Abort",
+ ioreq, ioreq->wr_status);
+
+ spin_lock_irqsave(&hw->lock, flags);
+ if (subop)
+ csio_scsi_closed(ioreq,
+ (struct list_head *)cbfn_q);
+ else
+ csio_scsi_aborted(ioreq,
+ (struct list_head *)cbfn_q);
+ /*
+ * We call scsi_done for I/Os that driver thinks aborts
+ * have timed out. If there is a race caused by FW
+ * completing abort at the exact same time that the
+ * driver has deteced the abort timeout, the following
+ * check prevents calling of scsi_done twice for the
+ * same command: once from the eh_abort_handler, another
+ * from csio_scsi_isr_handler(). This also avoids the
+ * need to check if csio_scsi_cmnd(req) is NULL in the
+ * fast path.
+ */
+ cmnd = csio_scsi_cmnd(ioreq);
+ if (unlikely(cmnd == NULL))
+ list_del_init(&ioreq->sm.sm_list);
+
+ spin_unlock_irqrestore(&hw->lock, flags);
+
+ if (unlikely(cmnd == NULL))
+ csio_put_scsi_ioreq_lock(hw,
+ csio_hw_to_scsim(hw), ioreq);
+ } else {
+ spin_lock_irqsave(&hw->lock, flags);
+ csio_scsi_completed(ioreq, (struct list_head *)cbfn_q);
+ spin_unlock_irqrestore(&hw->lock, flags);
+ }
+ }
+}
+
+/*
+ * csio_scsi_isr_handler() - Common SCSI ISR handler.
+ * @iq: Ingress queue pointer.
+ *
+ * Processes SCSI completions on the SCSI IQ indicated by scm->iq_idx
+ * by calling csio_wr_process_iq_idx. If there are completions on the
+ * isr_cbfn_q, yank them out into a local queue and call their io_cbfns.
+ * Once done, add these completions onto the freelist.
+ * This routine is shared b/w MSIX and INTx.
+ */
+static inline irqreturn_t
+csio_scsi_isr_handler(struct csio_q *iq)
+{
+ struct csio_hw *hw = (struct csio_hw *)iq->owner;
+ LIST_HEAD(cbfn_q);
+ struct list_head *tmp;
+ struct csio_scsim *scm;
+ struct csio_ioreq *ioreq;
+ int isr_completions = 0;
+
+ scm = csio_hw_to_scsim(hw);
+
+ if (unlikely(csio_wr_process_iq(hw, iq, csio_process_scsi_cmpl,
+ &cbfn_q) != 0))
+ return IRQ_NONE;
+
+ /* Call back the completion routines */
+ list_for_each(tmp, &cbfn_q) {
+ ioreq = (struct csio_ioreq *)tmp;
+ isr_completions++;
+ ioreq->io_cbfn(hw, ioreq);
+ /* Release ddp buffer if used for this req */
+ if (unlikely(ioreq->dcopy))
+ csio_put_scsi_ddp_list_lock(hw, scm, &ioreq->gen_list,
+ ioreq->nsge);
+ }
+
+ if (isr_completions) {
+ /* Return the ioreqs back to ioreq->freelist */
+ csio_put_scsi_ioreq_list_lock(hw, scm, &cbfn_q,
+ isr_completions);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * csio_scsi_isr() - SCSI MSIX handler
+ * @irq:
+ * @dev_id:
+ *
+ * This is the top level SCSI MSIX handler. Calls csio_scsi_isr_handler()
+ * for handling SCSI completions.
+ */
+static irqreturn_t
+csio_scsi_isr(int irq, void *dev_id)
+{
+ struct csio_q *iq = (struct csio_q *) dev_id;
+ struct csio_hw *hw;
+
+ if (unlikely(!iq))
+ return IRQ_NONE;
+
+ hw = (struct csio_hw *)iq->owner;
+
+ if (unlikely(pci_channel_offline(hw->pdev))) {
+ CSIO_INC_STATS(hw, n_pcich_offline);
+ return IRQ_NONE;
+ }
+
+ csio_scsi_isr_handler(iq);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * csio_scsi_intx_handler() - SCSI INTx handler
+ * @irq:
+ * @dev_id:
+ *
+ * This is the top level SCSI INTx handler. Calls csio_scsi_isr_handler()
+ * for handling SCSI completions.
+ */
+void
+csio_scsi_intx_handler(struct csio_hw *hw, void *wr, uint32_t len,
+ struct csio_fl_dma_buf *flb, void *priv)
+{
+ struct csio_q *iq = priv;
+
+ csio_scsi_isr_handler(iq);
+
+} /* csio_scsi_intx_handler */
+
+/*
+ * csio_fcoe_isr() - INTx/MSI interrupt service routine for FCoE.
+ * @irq:
+ * @dev_id:
+ *
+ *
+ */
+static irqreturn_t
+csio_fcoe_isr(int irq, void *dev_id)
+{
+ struct csio_hw *hw = (struct csio_hw *) dev_id;
+ struct csio_q *intx_q = NULL;
+ int rv;
+ irqreturn_t ret = IRQ_NONE;
+ unsigned long flags;
+
+ if (unlikely(!hw))
+ return IRQ_NONE;
+
+ if (unlikely(pci_channel_offline(hw->pdev))) {
+ CSIO_INC_STATS(hw, n_pcich_offline);
+ return IRQ_NONE;
+ }
+
+ /* Disable the interrupt for this PCI function. */
+ if (hw->intr_mode == CSIO_IM_INTX)
+ csio_wr_reg32(hw, 0, MYPF_REG(PCIE_PF_CLI));
+
+ /*
+ * The read in the following function will flush the
+ * above write.
+ */
+ if (csio_hw_slow_intr_handler(hw))
+ ret = IRQ_HANDLED;
+
+ /* Get the INTx Forward interrupt IQ. */
+ intx_q = csio_get_q(hw, hw->intr_iq_idx);
+
+ CSIO_DB_ASSERT(intx_q);
+
+ /* IQ handler is not possible for intx_q, hence pass in NULL */
+ if (likely(csio_wr_process_iq(hw, intx_q, NULL, NULL) == 0))
+ ret = IRQ_HANDLED;
+
+ spin_lock_irqsave(&hw->lock, flags);
+ rv = csio_mb_isr_handler(hw);
+ if (rv == 0 && !(hw->flags & CSIO_HWF_FWEVT_PENDING)) {
+ hw->flags |= CSIO_HWF_FWEVT_PENDING;
+ spin_unlock_irqrestore(&hw->lock, flags);
+ schedule_work(&hw->evtq_work);
+ return IRQ_HANDLED;
+ }
+ spin_unlock_irqrestore(&hw->lock, flags);
+
+ return ret;
+}
+
+static void
+csio_add_msix_desc(struct csio_hw *hw)
+{
+ int i;
+ struct csio_msix_entries *entryp = &hw->msix_entries[0];
+ int k = CSIO_EXTRA_VECS;
+ int len = sizeof(entryp->desc) - 1;
+ int cnt = hw->num_sqsets + k;
+
+ /* Non-data vector */
+ memset(entryp->desc, 0, len + 1);
+ snprintf(entryp->desc, len, "csio-%02x:%02x:%x-nondata",
+ CSIO_PCI_BUS(hw), CSIO_PCI_DEV(hw), CSIO_PCI_FUNC(hw));
+
+ entryp++;
+ memset(entryp->desc, 0, len + 1);
+ snprintf(entryp->desc, len, "csio-%02x:%02x:%x-fwevt",
+ CSIO_PCI_BUS(hw), CSIO_PCI_DEV(hw), CSIO_PCI_FUNC(hw));
+ entryp++;
+
+ /* Name SCSI vecs */
+ for (i = k; i < cnt; i++, entryp++) {
+ memset(entryp->desc, 0, len + 1);
+ snprintf(entryp->desc, len, "csio-%02x:%02x:%x-scsi%d",
+ CSIO_PCI_BUS(hw), CSIO_PCI_DEV(hw),
+ CSIO_PCI_FUNC(hw), i - CSIO_EXTRA_VECS);
+ }
+}
+
+int
+csio_request_irqs(struct csio_hw *hw)
+{
+ int rv, i, j, k = 0;
+ struct csio_msix_entries *entryp = &hw->msix_entries[0];
+ struct csio_scsi_cpu_info *info;
+
+ if (hw->intr_mode != CSIO_IM_MSIX) {
+ rv = request_irq(hw->pdev->irq, csio_fcoe_isr,
+ (hw->intr_mode == CSIO_IM_MSI) ?
+ 0 : IRQF_SHARED,
+ KBUILD_MODNAME, hw);
+ if (rv) {
+ if (hw->intr_mode == CSIO_IM_MSI)
+ pci_disable_msi(hw->pdev);
+ csio_err(hw, "Failed to allocate interrupt line.\n");
+ return -EINVAL;
+ }
+
+ goto out;
+ }
+
+ /* Add the MSIX vector descriptions */
+ csio_add_msix_desc(hw);
+
+ rv = request_irq(entryp[k].vector, csio_nondata_isr, 0,
+ entryp[k].desc, hw);
+ if (rv) {
+ csio_err(hw, "IRQ request failed for vec %d err:%d\n",
+ entryp[k].vector, rv);
+ goto err;
+ }
+
+ entryp[k++].dev_id = (void *)hw;
+
+ rv = request_irq(entryp[k].vector, csio_fwevt_isr, 0,
+ entryp[k].desc, hw);
+ if (rv) {
+ csio_err(hw, "IRQ request failed for vec %d err:%d\n",
+ entryp[k].vector, rv);
+ goto err;
+ }
+
+ entryp[k++].dev_id = (void *)hw;
+
+ /* Allocate IRQs for SCSI */
+ for (i = 0; i < hw->num_pports; i++) {
+ info = &hw->scsi_cpu_info[i];
+ for (j = 0; j < info->max_cpus; j++, k++) {
+ struct csio_scsi_qset *sqset = &hw->sqset[i][j];
+ struct csio_q *q = hw->wrm.q_arr[sqset->iq_idx];
+
+ rv = request_irq(entryp[k].vector, csio_scsi_isr, 0,
+ entryp[k].desc, q);
+ if (rv) {
+ csio_err(hw,
+ "IRQ request failed for vec %d err:%d\n",
+ entryp[k].vector, rv);
+ goto err;
+ }
+
+ entryp[k].dev_id = (void *)q;
+
+ } /* for all scsi cpus */
+ } /* for all ports */
+
+out:
+ hw->flags |= CSIO_HWF_HOST_INTR_ENABLED;
+
+ return 0;
+
+err:
+ for (i = 0; i < k; i++) {
+ entryp = &hw->msix_entries[i];
+ free_irq(entryp->vector, entryp->dev_id);
+ }
+ pci_disable_msix(hw->pdev);
+
+ return -EINVAL;
+}
+
+static void
+csio_disable_msix(struct csio_hw *hw, bool free)
+{
+ int i;
+ struct csio_msix_entries *entryp;
+ int cnt = hw->num_sqsets + CSIO_EXTRA_VECS;
+
+ if (free) {
+ for (i = 0; i < cnt; i++) {
+ entryp = &hw->msix_entries[i];
+ free_irq(entryp->vector, entryp->dev_id);
+ }
+ }
+ pci_disable_msix(hw->pdev);
+}
+
+/* Reduce per-port max possible CPUs */
+static void
+csio_reduce_sqsets(struct csio_hw *hw, int cnt)
+{
+ int i;
+ struct csio_scsi_cpu_info *info;
+
+ while (cnt < hw->num_sqsets) {
+ for (i = 0; i < hw->num_pports; i++) {
+ info = &hw->scsi_cpu_info[i];
+ if (info->max_cpus > 1) {
+ info->max_cpus--;
+ hw->num_sqsets--;
+ if (hw->num_sqsets <= cnt)
+ break;
+ }
+ }
+ }
+
+ csio_dbg(hw, "Reduced sqsets to %d\n", hw->num_sqsets);
+}
+
+static int
+csio_enable_msix(struct csio_hw *hw)
+{
+ int rv, i, j, k, n, min, cnt;
+ struct csio_msix_entries *entryp;
+ struct msix_entry *entries;
+ int extra = CSIO_EXTRA_VECS;
+ struct csio_scsi_cpu_info *info;
+
+ min = hw->num_pports + extra;
+ cnt = hw->num_sqsets + extra;
+
+ /* Max vectors required based on #niqs configured in fw */
+ if (hw->flags & CSIO_HWF_USING_SOFT_PARAMS || !csio_is_hw_master(hw))
+ cnt = min_t(uint8_t, hw->cfg_niq, cnt);
+
+ entries = kzalloc(sizeof(struct msix_entry) * cnt, GFP_KERNEL);
+ if (!entries)
+ return -ENOMEM;
+
+ for (i = 0; i < cnt; i++)
+ entries[i].entry = (uint16_t)i;
+
+ csio_dbg(hw, "FW supp #niq:%d, trying %d msix's\n", hw->cfg_niq, cnt);
+
+ while ((rv = pci_enable_msix(hw->pdev, entries, cnt)) >= min)
+ cnt = rv;
+ if (!rv) {
+ if (cnt < (hw->num_sqsets + extra)) {
+ csio_dbg(hw, "Reducing sqsets to %d\n", cnt - extra);
+ csio_reduce_sqsets(hw, cnt - extra);
+ }
+ } else {
+ if (rv > 0) {
+ pci_disable_msix(hw->pdev);
+ csio_info(hw, "Not using MSI-X, remainder:%d\n", rv);
+ }
+
+ kfree(entries);
+ return -ENOMEM;
+ }
+
+ /* Save off vectors */
+ for (i = 0; i < cnt; i++) {
+ entryp = &hw->msix_entries[i];
+ entryp->vector = entries[i].vector;
+ }
+
+ /* Distribute vectors */
+ k = 0;
+ csio_set_nondata_intr_idx(hw, entries[k].entry);
+ csio_set_mb_intr_idx(csio_hw_to_mbm(hw), entries[k++].entry);
+ csio_set_fwevt_intr_idx(hw, entries[k++].entry);
+
+ for (i = 0; i < hw->num_pports; i++) {
+ info = &hw->scsi_cpu_info[i];
+
+ for (j = 0; j < hw->num_scsi_msix_cpus; j++) {
+ n = (j % info->max_cpus) + k;
+ hw->sqset[i][j].intr_idx = entries[n].entry;
+ }
+
+ k += info->max_cpus;
+ }
+
+ kfree(entries);
+ return 0;
+}
+
+void
+csio_intr_enable(struct csio_hw *hw)
+{
+ hw->intr_mode = CSIO_IM_NONE;
+ hw->flags &= ~CSIO_HWF_HOST_INTR_ENABLED;
+
+ /* Try MSIX, then MSI or fall back to INTx */
+ if ((csio_msi == 2) && !csio_enable_msix(hw))
+ hw->intr_mode = CSIO_IM_MSIX;
+ else {
+ /* Max iqs required based on #niqs configured in fw */
+ if (hw->flags & CSIO_HWF_USING_SOFT_PARAMS ||
+ !csio_is_hw_master(hw)) {
+ int extra = CSIO_EXTRA_MSI_IQS;
+
+ if (hw->cfg_niq < (hw->num_sqsets + extra)) {
+ csio_dbg(hw, "Reducing sqsets to %d\n",
+ hw->cfg_niq - extra);
+ csio_reduce_sqsets(hw, hw->cfg_niq - extra);
+ }
+ }
+
+ if ((csio_msi == 1) && !pci_enable_msi(hw->pdev))
+ hw->intr_mode = CSIO_IM_MSI;
+ else
+ hw->intr_mode = CSIO_IM_INTX;
+ }
+
+ csio_dbg(hw, "Using %s interrupt mode.\n",
+ (hw->intr_mode == CSIO_IM_MSIX) ? "MSIX" :
+ ((hw->intr_mode == CSIO_IM_MSI) ? "MSI" : "INTx"));
+}
+
+void
+csio_intr_disable(struct csio_hw *hw, bool free)
+{
+ csio_hw_intr_disable(hw);
+
+ switch (hw->intr_mode) {
+ case CSIO_IM_MSIX:
+ csio_disable_msix(hw, free);
+ break;
+ case CSIO_IM_MSI:
+ if (free)
+ free_irq(hw->pdev->irq, hw);
+ pci_disable_msi(hw->pdev);
+ break;
+ case CSIO_IM_INTX:
+ if (free)
+ free_irq(hw->pdev->irq, hw);
+ break;
+ default:
+ break;
+ }
+ hw->intr_mode = CSIO_IM_NONE;
+ hw->flags &= ~CSIO_HWF_HOST_INTR_ENABLED;
+}
diff --git a/drivers/scsi/csiostor/csio_lnode.c b/drivers/scsi/csiostor/csio_lnode.c
new file mode 100644
index 00000000000..ffe9be04dc3
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_lnode.c
@@ -0,0 +1,2135 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/utsname.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport_fc.h>
+#include <asm/unaligned.h>
+#include <scsi/fc/fc_els.h>
+#include <scsi/fc/fc_fs.h>
+#include <scsi/fc/fc_gs.h>
+#include <scsi/fc/fc_ms.h>
+
+#include "csio_hw.h"
+#include "csio_mb.h"
+#include "csio_lnode.h"
+#include "csio_rnode.h"
+
+int csio_fcoe_rnodes = 1024;
+int csio_fdmi_enable = 1;
+
+#define PORT_ID_PTR(_x) ((uint8_t *)(&_x) + 1)
+
+/* Lnode SM declarations */
+static void csio_lns_uninit(struct csio_lnode *, enum csio_ln_ev);
+static void csio_lns_online(struct csio_lnode *, enum csio_ln_ev);
+static void csio_lns_ready(struct csio_lnode *, enum csio_ln_ev);
+static void csio_lns_offline(struct csio_lnode *, enum csio_ln_ev);
+
+static int csio_ln_mgmt_submit_req(struct csio_ioreq *,
+ void (*io_cbfn) (struct csio_hw *, struct csio_ioreq *),
+ enum fcoe_cmn_type, struct csio_dma_buf *, uint32_t);
+
+/* LN event mapping */
+static enum csio_ln_ev fwevt_to_lnevt[] = {
+ CSIO_LNE_NONE, /* None */
+ CSIO_LNE_NONE, /* PLOGI_ACC_RCVD */
+ CSIO_LNE_NONE, /* PLOGI_RJT_RCVD */
+ CSIO_LNE_NONE, /* PLOGI_RCVD */
+ CSIO_LNE_NONE, /* PLOGO_RCVD */
+ CSIO_LNE_NONE, /* PRLI_ACC_RCVD */
+ CSIO_LNE_NONE, /* PRLI_RJT_RCVD */
+ CSIO_LNE_NONE, /* PRLI_RCVD */
+ CSIO_LNE_NONE, /* PRLO_RCVD */
+ CSIO_LNE_NONE, /* NPORT_ID_CHGD */
+ CSIO_LNE_LOGO, /* FLOGO_RCVD */
+ CSIO_LNE_LOGO, /* CLR_VIRT_LNK_RCVD */
+ CSIO_LNE_FAB_INIT_DONE,/* FLOGI_ACC_RCVD */
+ CSIO_LNE_NONE, /* FLOGI_RJT_RCVD */
+ CSIO_LNE_FAB_INIT_DONE,/* FDISC_ACC_RCVD */
+ CSIO_LNE_NONE, /* FDISC_RJT_RCVD */
+ CSIO_LNE_NONE, /* FLOGI_TMO_MAX_RETRY */
+ CSIO_LNE_NONE, /* IMPL_LOGO_ADISC_ACC */
+ CSIO_LNE_NONE, /* IMPL_LOGO_ADISC_RJT */
+ CSIO_LNE_NONE, /* IMPL_LOGO_ADISC_CNFLT */
+ CSIO_LNE_NONE, /* PRLI_TMO */
+ CSIO_LNE_NONE, /* ADISC_TMO */
+ CSIO_LNE_NONE, /* RSCN_DEV_LOST */
+ CSIO_LNE_NONE, /* SCR_ACC_RCVD */
+ CSIO_LNE_NONE, /* ADISC_RJT_RCVD */
+ CSIO_LNE_NONE, /* LOGO_SNT */
+ CSIO_LNE_NONE, /* PROTO_ERR_IMPL_LOGO */
+};
+
+#define CSIO_FWE_TO_LNE(_evt) ((_evt > PROTO_ERR_IMPL_LOGO) ? \
+ CSIO_LNE_NONE : \
+ fwevt_to_lnevt[_evt])
+
+#define csio_ct_rsp(cp) (((struct fc_ct_hdr *)cp)->ct_cmd)
+#define csio_ct_reason(cp) (((struct fc_ct_hdr *)cp)->ct_reason)
+#define csio_ct_expl(cp) (((struct fc_ct_hdr *)cp)->ct_explan)
+#define csio_ct_get_pld(cp) ((void *)(((uint8_t *)cp) + FC_CT_HDR_LEN))
+
+/*
+ * csio_ln_match_by_portid - lookup lnode using given portid.
+ * @hw: HW module
+ * @portid: port-id.
+ *
+ * If found, returns lnode matching given portid otherwise returns NULL.
+ */
+static struct csio_lnode *
+csio_ln_lookup_by_portid(struct csio_hw *hw, uint8_t portid)
+{
+ struct csio_lnode *ln = hw->rln;
+ struct list_head *tmp;
+
+ /* Match siblings lnode with portid */
+ list_for_each(tmp, &hw->sln_head) {
+ ln = (struct csio_lnode *) tmp;
+ if (ln->portid == portid)
+ return ln;
+ }
+
+ return NULL;
+}
+
+/*
+ * csio_ln_lookup_by_vnpi - Lookup lnode using given vnp id.
+ * @hw - HW module
+ * @vnpi - vnp index.
+ * Returns - If found, returns lnode matching given vnp id
+ * otherwise returns NULL.
+ */
+static struct csio_lnode *
+csio_ln_lookup_by_vnpi(struct csio_hw *hw, uint32_t vnp_id)
+{
+ struct list_head *tmp1, *tmp2;
+ struct csio_lnode *sln = NULL, *cln = NULL;
+
+ if (list_empty(&hw->sln_head)) {
+ CSIO_INC_STATS(hw, n_lnlkup_miss);
+ return NULL;
+ }
+ /* Traverse sibling lnodes */
+ list_for_each(tmp1, &hw->sln_head) {
+ sln = (struct csio_lnode *) tmp1;
+
+ /* Match sibling lnode */
+ if (sln->vnp_flowid == vnp_id)
+ return sln;
+
+ if (list_empty(&sln->cln_head))
+ continue;
+
+ /* Traverse children lnodes */
+ list_for_each(tmp2, &sln->cln_head) {
+ cln = (struct csio_lnode *) tmp2;
+
+ if (cln->vnp_flowid == vnp_id)
+ return cln;
+ }
+ }
+ CSIO_INC_STATS(hw, n_lnlkup_miss);
+ return NULL;
+}
+
+/**
+ * csio_lnode_lookup_by_wwpn - Lookup lnode using given wwpn.
+ * @hw: HW module.
+ * @wwpn: WWPN.
+ *
+ * If found, returns lnode matching given wwpn, returns NULL otherwise.
+ */
+struct csio_lnode *
+csio_lnode_lookup_by_wwpn(struct csio_hw *hw, uint8_t *wwpn)
+{
+ struct list_head *tmp1, *tmp2;
+ struct csio_lnode *sln = NULL, *cln = NULL;
+
+ if (list_empty(&hw->sln_head)) {
+ CSIO_INC_STATS(hw, n_lnlkup_miss);
+ return NULL;
+ }
+ /* Traverse sibling lnodes */
+ list_for_each(tmp1, &hw->sln_head) {
+ sln = (struct csio_lnode *) tmp1;
+
+ /* Match sibling lnode */
+ if (!memcmp(csio_ln_wwpn(sln), wwpn, 8))
+ return sln;
+
+ if (list_empty(&sln->cln_head))
+ continue;
+
+ /* Traverse children lnodes */
+ list_for_each(tmp2, &sln->cln_head) {
+ cln = (struct csio_lnode *) tmp2;
+
+ if (!memcmp(csio_ln_wwpn(cln), wwpn, 8))
+ return cln;
+ }
+ }
+ return NULL;
+}
+
+/* FDMI */
+static void
+csio_fill_ct_iu(void *buf, uint8_t type, uint8_t sub_type, uint16_t op)
+{
+ struct fc_ct_hdr *cmd = (struct fc_ct_hdr *)buf;
+ cmd->ct_rev = FC_CT_REV;
+ cmd->ct_fs_type = type;
+ cmd->ct_fs_subtype = sub_type;
+ cmd->ct_cmd = htons(op);
+}
+
+static int
+csio_hostname(uint8_t *buf, size_t buf_len)
+{
+ if (snprintf(buf, buf_len, "%s", init_utsname()->nodename) > 0)
+ return 0;
+ return -1;
+}
+
+static int
+csio_osname(uint8_t *buf, size_t buf_len)
+{
+ if (snprintf(buf, buf_len, "%s %s %s",
+ init_utsname()->sysname,
+ init_utsname()->release,
+ init_utsname()->version) > 0)
+ return 0;
+
+ return -1;
+}
+
+static inline void
+csio_append_attrib(uint8_t **ptr, uint16_t type, uint8_t *val, uint16_t len)
+{
+ struct fc_fdmi_attr_entry *ae = (struct fc_fdmi_attr_entry *)*ptr;
+ ae->type = htons(type);
+ len += 4; /* includes attribute type and length */
+ len = (len + 3) & ~3; /* should be multiple of 4 bytes */
+ ae->len = htons(len);
+ memcpy(ae->value, val, len);
+ *ptr += len;
+}
+
+/*
+ * csio_ln_fdmi_done - FDMI registeration completion
+ * @hw: HW context
+ * @fdmi_req: fdmi request
+ */
+static void
+csio_ln_fdmi_done(struct csio_hw *hw, struct csio_ioreq *fdmi_req)
+{
+ void *cmd;
+ struct csio_lnode *ln = fdmi_req->lnode;
+
+ if (fdmi_req->wr_status != FW_SUCCESS) {
+ csio_ln_dbg(ln, "WR error:%x in processing fdmi rpa cmd\n",
+ fdmi_req->wr_status);
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ }
+
+ cmd = fdmi_req->dma_buf.vaddr;
+ if (ntohs(csio_ct_rsp(cmd)) != FC_FS_ACC) {
+ csio_ln_dbg(ln, "fdmi rpa cmd rejected reason %x expl %x\n",
+ csio_ct_reason(cmd), csio_ct_expl(cmd));
+ }
+}
+
+/*
+ * csio_ln_fdmi_rhba_cbfn - RHBA completion
+ * @hw: HW context
+ * @fdmi_req: fdmi request
+ */
+static void
+csio_ln_fdmi_rhba_cbfn(struct csio_hw *hw, struct csio_ioreq *fdmi_req)
+{
+ void *cmd;
+ uint8_t *pld;
+ uint32_t len = 0;
+ __be32 val;
+ __be16 mfs;
+ uint32_t numattrs = 0;
+ struct csio_lnode *ln = fdmi_req->lnode;
+ struct fs_fdmi_attrs *attrib_blk;
+ struct fc_fdmi_port_name *port_name;
+ uint8_t buf[64];
+ uint8_t *fc4_type;
+
+ if (fdmi_req->wr_status != FW_SUCCESS) {
+ csio_ln_dbg(ln, "WR error:%x in processing fdmi rhba cmd\n",
+ fdmi_req->wr_status);
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ }
+
+ cmd = fdmi_req->dma_buf.vaddr;
+ if (ntohs(csio_ct_rsp(cmd)) != FC_FS_ACC) {
+ csio_ln_dbg(ln, "fdmi rhba cmd rejected reason %x expl %x\n",
+ csio_ct_reason(cmd), csio_ct_expl(cmd));
+ }
+
+ if (!csio_is_rnode_ready(fdmi_req->rnode)) {
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ return;
+ }
+
+ /* Prepare CT hdr for RPA cmd */
+ memset(cmd, 0, FC_CT_HDR_LEN);
+ csio_fill_ct_iu(cmd, FC_FST_MGMT, FC_FDMI_SUBTYPE, FC_FDMI_RPA);
+
+ /* Prepare RPA payload */
+ pld = (uint8_t *)csio_ct_get_pld(cmd);
+ port_name = (struct fc_fdmi_port_name *)pld;
+ memcpy(&port_name->portname, csio_ln_wwpn(ln), 8);
+ pld += sizeof(*port_name);
+
+ /* Start appending Port attributes */
+ attrib_blk = (struct fs_fdmi_attrs *)pld;
+ attrib_blk->numattrs = 0;
+ len += sizeof(attrib_blk->numattrs);
+ pld += sizeof(attrib_blk->numattrs);
+
+ fc4_type = &buf[0];
+ memset(fc4_type, 0, FC_FDMI_PORT_ATTR_FC4TYPES_LEN);
+ fc4_type[2] = 1;
+ fc4_type[7] = 1;
+ csio_append_attrib(&pld, FC_FDMI_PORT_ATTR_FC4TYPES,
+ fc4_type, FC_FDMI_PORT_ATTR_FC4TYPES_LEN);
+ numattrs++;
+ val = htonl(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
+ csio_append_attrib(&pld, FC_FDMI_PORT_ATTR_SUPPORTEDSPEED,
+ (uint8_t *)&val,
+ FC_FDMI_PORT_ATTR_SUPPORTEDSPEED_LEN);
+ numattrs++;
+
+ if (hw->pport[ln->portid].link_speed == FW_PORT_CAP_SPEED_1G)
+ val = htonl(FC_PORTSPEED_1GBIT);
+ else if (hw->pport[ln->portid].link_speed == FW_PORT_CAP_SPEED_10G)
+ val = htonl(FC_PORTSPEED_10GBIT);
+ else
+ val = htonl(CSIO_HBA_PORTSPEED_UNKNOWN);
+ csio_append_attrib(&pld, FC_FDMI_PORT_ATTR_CURRENTPORTSPEED,
+ (uint8_t *)&val,
+ FC_FDMI_PORT_ATTR_CURRENTPORTSPEED_LEN);
+ numattrs++;
+
+ mfs = ln->ln_sparm.csp.sp_bb_data;
+ csio_append_attrib(&pld, FC_FDMI_PORT_ATTR_MAXFRAMESIZE,
+ (uint8_t *)&mfs, FC_FDMI_PORT_ATTR_MAXFRAMESIZE_LEN);
+ numattrs++;
+
+ strcpy(buf, "csiostor");
+ csio_append_attrib(&pld, FC_FDMI_PORT_ATTR_OSDEVICENAME, buf,
+ (uint16_t)strlen(buf));
+ numattrs++;
+
+ if (!csio_hostname(buf, sizeof(buf))) {
+ csio_append_attrib(&pld, FC_FDMI_PORT_ATTR_HOSTNAME,
+ buf, (uint16_t)strlen(buf));
+ numattrs++;
+ }
+ attrib_blk->numattrs = htonl(numattrs);
+ len = (uint32_t)(pld - (uint8_t *)cmd);
+
+ /* Submit FDMI RPA request */
+ spin_lock_irq(&hw->lock);
+ if (csio_ln_mgmt_submit_req(fdmi_req, csio_ln_fdmi_done,
+ FCOE_CT, &fdmi_req->dma_buf, len)) {
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ csio_ln_dbg(ln, "Failed to issue fdmi rpa req\n");
+ }
+ spin_unlock_irq(&hw->lock);
+}
+
+/*
+ * csio_ln_fdmi_dprt_cbfn - DPRT completion
+ * @hw: HW context
+ * @fdmi_req: fdmi request
+ */
+static void
+csio_ln_fdmi_dprt_cbfn(struct csio_hw *hw, struct csio_ioreq *fdmi_req)
+{
+ void *cmd;
+ uint8_t *pld;
+ uint32_t len = 0;
+ uint32_t numattrs = 0;
+ __be32 maxpayload = htonl(65536);
+ struct fc_fdmi_hba_identifier *hbaid;
+ struct csio_lnode *ln = fdmi_req->lnode;
+ struct fc_fdmi_rpl *reg_pl;
+ struct fs_fdmi_attrs *attrib_blk;
+ uint8_t buf[64];
+
+ if (fdmi_req->wr_status != FW_SUCCESS) {
+ csio_ln_dbg(ln, "WR error:%x in processing fdmi dprt cmd\n",
+ fdmi_req->wr_status);
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ }
+
+ if (!csio_is_rnode_ready(fdmi_req->rnode)) {
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ return;
+ }
+ cmd = fdmi_req->dma_buf.vaddr;
+ if (ntohs(csio_ct_rsp(cmd)) != FC_FS_ACC) {
+ csio_ln_dbg(ln, "fdmi dprt cmd rejected reason %x expl %x\n",
+ csio_ct_reason(cmd), csio_ct_expl(cmd));
+ }
+
+ /* Prepare CT hdr for RHBA cmd */
+ memset(cmd, 0, FC_CT_HDR_LEN);
+ csio_fill_ct_iu(cmd, FC_FST_MGMT, FC_FDMI_SUBTYPE, FC_FDMI_RHBA);
+ len = FC_CT_HDR_LEN;
+
+ /* Prepare RHBA payload */
+ pld = (uint8_t *)csio_ct_get_pld(cmd);
+ hbaid = (struct fc_fdmi_hba_identifier *)pld;
+ memcpy(&hbaid->id, csio_ln_wwpn(ln), 8); /* HBA identifer */
+ pld += sizeof(*hbaid);
+
+ /* Register one port per hba */
+ reg_pl = (struct fc_fdmi_rpl *)pld;
+ reg_pl->numport = htonl(1);
+ memcpy(&reg_pl->port[0].portname, csio_ln_wwpn(ln), 8);
+ pld += sizeof(*reg_pl);
+
+ /* Start appending HBA attributes hba */
+ attrib_blk = (struct fs_fdmi_attrs *)pld;
+ attrib_blk->numattrs = 0;
+ len += sizeof(attrib_blk->numattrs);
+ pld += sizeof(attrib_blk->numattrs);
+
+ csio_append_attrib(&pld, FC_FDMI_HBA_ATTR_NODENAME, csio_ln_wwnn(ln),
+ FC_FDMI_HBA_ATTR_NODENAME_LEN);
+ numattrs++;
+
+ memset(buf, 0, sizeof(buf));
+
+ strcpy(buf, "Chelsio Communications");
+ csio_append_attrib(&pld, FC_FDMI_HBA_ATTR_MANUFACTURER, buf,
+ (uint16_t)strlen(buf));
+ numattrs++;
+ csio_append_attrib(&pld, FC_FDMI_HBA_ATTR_SERIALNUMBER,
+ hw->vpd.sn, (uint16_t)sizeof(hw->vpd.sn));
+ numattrs++;
+ csio_append_attrib(&pld, FC_FDMI_HBA_ATTR_MODEL, hw->vpd.id,
+ (uint16_t)sizeof(hw->vpd.id));
+ numattrs++;
+ csio_append_attrib(&pld, FC_FDMI_HBA_ATTR_MODELDESCRIPTION,
+ hw->model_desc, (uint16_t)strlen(hw->model_desc));
+ numattrs++;
+ csio_append_attrib(&pld, FC_FDMI_HBA_ATTR_HARDWAREVERSION,
+ hw->hw_ver, (uint16_t)sizeof(hw->hw_ver));
+ numattrs++;
+ csio_append_attrib(&pld, FC_FDMI_HBA_ATTR_FIRMWAREVERSION,
+ hw->fwrev_str, (uint16_t)strlen(hw->fwrev_str));
+ numattrs++;
+
+ if (!csio_osname(buf, sizeof(buf))) {
+ csio_append_attrib(&pld, FC_FDMI_HBA_ATTR_OSNAMEVERSION,
+ buf, (uint16_t)strlen(buf));
+ numattrs++;
+ }
+
+ csio_append_attrib(&pld, FC_FDMI_HBA_ATTR_MAXCTPAYLOAD,
+ (uint8_t *)&maxpayload,
+ FC_FDMI_HBA_ATTR_MAXCTPAYLOAD_LEN);
+ len = (uint32_t)(pld - (uint8_t *)cmd);
+ numattrs++;
+ attrib_blk->numattrs = htonl(numattrs);
+
+ /* Submit FDMI RHBA request */
+ spin_lock_irq(&hw->lock);
+ if (csio_ln_mgmt_submit_req(fdmi_req, csio_ln_fdmi_rhba_cbfn,
+ FCOE_CT, &fdmi_req->dma_buf, len)) {
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ csio_ln_dbg(ln, "Failed to issue fdmi rhba req\n");
+ }
+ spin_unlock_irq(&hw->lock);
+}
+
+/*
+ * csio_ln_fdmi_dhba_cbfn - DHBA completion
+ * @hw: HW context
+ * @fdmi_req: fdmi request
+ */
+static void
+csio_ln_fdmi_dhba_cbfn(struct csio_hw *hw, struct csio_ioreq *fdmi_req)
+{
+ struct csio_lnode *ln = fdmi_req->lnode;
+ void *cmd;
+ struct fc_fdmi_port_name *port_name;
+ uint32_t len;
+
+ if (fdmi_req->wr_status != FW_SUCCESS) {
+ csio_ln_dbg(ln, "WR error:%x in processing fdmi dhba cmd\n",
+ fdmi_req->wr_status);
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ }
+
+ if (!csio_is_rnode_ready(fdmi_req->rnode)) {
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ return;
+ }
+ cmd = fdmi_req->dma_buf.vaddr;
+ if (ntohs(csio_ct_rsp(cmd)) != FC_FS_ACC) {
+ csio_ln_dbg(ln, "fdmi dhba cmd rejected reason %x expl %x\n",
+ csio_ct_reason(cmd), csio_ct_expl(cmd));
+ }
+
+ /* Send FDMI cmd to de-register any Port attributes if registered
+ * before
+ */
+
+ /* Prepare FDMI DPRT cmd */
+ memset(cmd, 0, FC_CT_HDR_LEN);
+ csio_fill_ct_iu(cmd, FC_FST_MGMT, FC_FDMI_SUBTYPE, FC_FDMI_DPRT);
+ len = FC_CT_HDR_LEN;
+ port_name = (struct fc_fdmi_port_name *)csio_ct_get_pld(cmd);
+ memcpy(&port_name->portname, csio_ln_wwpn(ln), 8);
+ len += sizeof(*port_name);
+
+ /* Submit FDMI request */
+ spin_lock_irq(&hw->lock);
+ if (csio_ln_mgmt_submit_req(fdmi_req, csio_ln_fdmi_dprt_cbfn,
+ FCOE_CT, &fdmi_req->dma_buf, len)) {
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ csio_ln_dbg(ln, "Failed to issue fdmi dprt req\n");
+ }
+ spin_unlock_irq(&hw->lock);
+}
+
+/**
+ * csio_ln_fdmi_start - Start an FDMI request.
+ * @ln: lnode
+ * @context: session context
+ *
+ * Issued with lock held.
+ */
+int
+csio_ln_fdmi_start(struct csio_lnode *ln, void *context)
+{
+ struct csio_ioreq *fdmi_req;
+ struct csio_rnode *fdmi_rn = (struct csio_rnode *)context;
+ void *cmd;
+ struct fc_fdmi_hba_identifier *hbaid;
+ uint32_t len;
+
+ if (!(ln->flags & CSIO_LNF_FDMI_ENABLE))
+ return -EPROTONOSUPPORT;
+
+ if (!csio_is_rnode_ready(fdmi_rn))
+ CSIO_INC_STATS(ln, n_fdmi_err);
+
+ /* Send FDMI cmd to de-register any HBA attributes if registered
+ * before
+ */
+
+ fdmi_req = ln->mgmt_req;
+ fdmi_req->lnode = ln;
+ fdmi_req->rnode = fdmi_rn;
+
+ /* Prepare FDMI DHBA cmd */
+ cmd = fdmi_req->dma_buf.vaddr;
+ memset(cmd, 0, FC_CT_HDR_LEN);
+ csio_fill_ct_iu(cmd, FC_FST_MGMT, FC_FDMI_SUBTYPE, FC_FDMI_DHBA);
+ len = FC_CT_HDR_LEN;
+
+ hbaid = (struct fc_fdmi_hba_identifier *)csio_ct_get_pld(cmd);
+ memcpy(&hbaid->id, csio_ln_wwpn(ln), 8);
+ len += sizeof(*hbaid);
+
+ /* Submit FDMI request */
+ if (csio_ln_mgmt_submit_req(fdmi_req, csio_ln_fdmi_dhba_cbfn,
+ FCOE_CT, &fdmi_req->dma_buf, len)) {
+ CSIO_INC_STATS(ln, n_fdmi_err);
+ csio_ln_dbg(ln, "Failed to issue fdmi dhba req\n");
+ }
+
+ return 0;
+}
+
+/*
+ * csio_ln_vnp_read_cbfn - vnp read completion handler.
+ * @hw: HW lnode
+ * @cbfn: Completion handler.
+ *
+ * Reads vnp response and updates ln parameters.
+ */
+static void
+csio_ln_vnp_read_cbfn(struct csio_hw *hw, struct csio_mb *mbp)
+{
+ struct csio_lnode *ln = ((struct csio_lnode *)mbp->priv);
+ struct fw_fcoe_vnp_cmd *rsp = (struct fw_fcoe_vnp_cmd *)(mbp->mb);
+ struct fc_els_csp *csp;
+ struct fc_els_cssp *clsp;
+ enum fw_retval retval;
+ __be32 nport_id;
+
+ retval = FW_CMD_RETVAL_GET(ntohl(rsp->alloc_to_len16));
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "FCOE VNP read cmd returned error:0x%x\n", retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return;
+ }
+
+ spin_lock_irq(&hw->lock);
+
+ memcpy(ln->mac, rsp->vnport_mac, sizeof(ln->mac));
+ memcpy(&nport_id, &rsp->vnport_mac[3], sizeof(uint8_t)*3);
+ ln->nport_id = ntohl(nport_id);
+ ln->nport_id = ln->nport_id >> 8;
+
+ /* Update WWNs */
+ /*
+ * This may look like a duplication of what csio_fcoe_enable_link()
+ * does, but is absolutely necessary if the vnpi changes between
+ * a FCOE LINK UP and FCOE LINK DOWN.
+ */
+ memcpy(csio_ln_wwnn(ln), rsp->vnport_wwnn, 8);
+ memcpy(csio_ln_wwpn(ln), rsp->vnport_wwpn, 8);
+
+ /* Copy common sparam */
+ csp = (struct fc_els_csp *)rsp->cmn_srv_parms;
+ ln->ln_sparm.csp.sp_hi_ver = csp->sp_hi_ver;
+ ln->ln_sparm.csp.sp_lo_ver = csp->sp_lo_ver;
+ ln->ln_sparm.csp.sp_bb_cred = csp->sp_bb_cred;
+ ln->ln_sparm.csp.sp_features = csp->sp_features;
+ ln->ln_sparm.csp.sp_bb_data = csp->sp_bb_data;
+ ln->ln_sparm.csp.sp_r_a_tov = csp->sp_r_a_tov;
+ ln->ln_sparm.csp.sp_e_d_tov = csp->sp_e_d_tov;
+
+ /* Copy word 0 & word 1 of class sparam */
+ clsp = (struct fc_els_cssp *)rsp->clsp_word_0_1;
+ ln->ln_sparm.clsp[2].cp_class = clsp->cp_class;
+ ln->ln_sparm.clsp[2].cp_init = clsp->cp_init;
+ ln->ln_sparm.clsp[2].cp_recip = clsp->cp_recip;
+ ln->ln_sparm.clsp[2].cp_rdfs = clsp->cp_rdfs;
+
+ spin_unlock_irq(&hw->lock);
+
+ mempool_free(mbp, hw->mb_mempool);
+
+ /* Send an event to update local attribs */
+ csio_lnode_async_event(ln, CSIO_LN_FC_ATTRIB_UPDATE);
+}
+
+/*
+ * csio_ln_vnp_read - Read vnp params.
+ * @ln: lnode
+ * @cbfn: Completion handler.
+ *
+ * Issued with lock held.
+ */
+static int
+csio_ln_vnp_read(struct csio_lnode *ln,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct csio_hw *hw = ln->hwp;
+ struct csio_mb *mbp;
+
+ /* Allocate Mbox request */
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ /* Prepare VNP Command */
+ csio_fcoe_vnp_read_init_mb(ln, mbp,
+ CSIO_MB_DEFAULT_TMO,
+ ln->fcf_flowid,
+ ln->vnp_flowid,
+ cbfn);
+
+ /* Issue MBOX cmd */
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Failed to issue mbox FCoE VNP command\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * csio_fcoe_enable_link - Enable fcoe link.
+ * @ln: lnode
+ * @enable: enable/disable
+ * Issued with lock held.
+ * Issues mbox cmd to bring up FCOE link on port associated with given ln.
+ */
+static int
+csio_fcoe_enable_link(struct csio_lnode *ln, bool enable)
+{
+ struct csio_hw *hw = ln->hwp;
+ struct csio_mb *mbp;
+ enum fw_retval retval;
+ uint8_t portid;
+ uint8_t sub_op;
+ struct fw_fcoe_link_cmd *lcmd;
+ int i;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ portid = ln->portid;
+ sub_op = enable ? FCOE_LINK_UP : FCOE_LINK_DOWN;
+
+ csio_dbg(hw, "bringing FCOE LINK %s on Port:%d\n",
+ sub_op ? "UP" : "DOWN", portid);
+
+ csio_write_fcoe_link_cond_init_mb(ln, mbp, CSIO_MB_DEFAULT_TMO,
+ portid, sub_op, 0, 0, 0, NULL);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "failed to issue FCOE LINK cmd on port[%d]\n",
+ portid);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ retval = csio_mb_fw_retval(mbp);
+ if (retval != FW_SUCCESS) {
+ csio_err(hw,
+ "FCOE LINK %s cmd on port[%d] failed with "
+ "ret:x%x\n", sub_op ? "UP" : "DOWN", portid, retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ if (!enable)
+ goto out;
+
+ lcmd = (struct fw_fcoe_link_cmd *)mbp->mb;
+
+ memcpy(csio_ln_wwnn(ln), lcmd->vnport_wwnn, 8);
+ memcpy(csio_ln_wwpn(ln), lcmd->vnport_wwpn, 8);
+
+ for (i = 0; i < CSIO_MAX_PPORTS; i++)
+ if (hw->pport[i].portid == portid)
+ memcpy(hw->pport[i].mac, lcmd->phy_mac, 6);
+
+out:
+ mempool_free(mbp, hw->mb_mempool);
+ return 0;
+}
+
+/*
+ * csio_ln_read_fcf_cbfn - Read fcf parameters
+ * @ln: lnode
+ *
+ * read fcf response and Update ln fcf information.
+ */
+static void
+csio_ln_read_fcf_cbfn(struct csio_hw *hw, struct csio_mb *mbp)
+{
+ struct csio_lnode *ln = (struct csio_lnode *)mbp->priv;
+ struct csio_fcf_info *fcf_info;
+ struct fw_fcoe_fcf_cmd *rsp =
+ (struct fw_fcoe_fcf_cmd *)(mbp->mb);
+ enum fw_retval retval;
+
+ retval = FW_CMD_RETVAL_GET(ntohl(rsp->retval_len16));
+ if (retval != FW_SUCCESS) {
+ csio_ln_err(ln, "FCOE FCF cmd failed with ret x%x\n",
+ retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return;
+ }
+
+ spin_lock_irq(&hw->lock);
+ fcf_info = ln->fcfinfo;
+ fcf_info->priority = FW_FCOE_FCF_CMD_PRIORITY_GET(
+ ntohs(rsp->priority_pkd));
+ fcf_info->vf_id = ntohs(rsp->vf_id);
+ fcf_info->vlan_id = rsp->vlan_id;
+ fcf_info->max_fcoe_size = ntohs(rsp->max_fcoe_size);
+ fcf_info->fka_adv = be32_to_cpu(rsp->fka_adv);
+ fcf_info->fcfi = FW_FCOE_FCF_CMD_FCFI_GET(ntohl(rsp->op_to_fcfi));
+ fcf_info->fpma = FW_FCOE_FCF_CMD_FPMA_GET(rsp->fpma_to_portid);
+ fcf_info->spma = FW_FCOE_FCF_CMD_SPMA_GET(rsp->fpma_to_portid);
+ fcf_info->login = FW_FCOE_FCF_CMD_LOGIN_GET(rsp->fpma_to_portid);
+ fcf_info->portid = FW_FCOE_FCF_CMD_PORTID_GET(rsp->fpma_to_portid);
+ memcpy(fcf_info->fc_map, rsp->fc_map, sizeof(fcf_info->fc_map));
+ memcpy(fcf_info->mac, rsp->mac, sizeof(fcf_info->mac));
+ memcpy(fcf_info->name_id, rsp->name_id, sizeof(fcf_info->name_id));
+ memcpy(fcf_info->fabric, rsp->fabric, sizeof(fcf_info->fabric));
+ memcpy(fcf_info->spma_mac, rsp->spma_mac, sizeof(fcf_info->spma_mac));
+
+ spin_unlock_irq(&hw->lock);
+
+ mempool_free(mbp, hw->mb_mempool);
+}
+
+/*
+ * csio_ln_read_fcf_entry - Read fcf entry.
+ * @ln: lnode
+ * @cbfn: Completion handler.
+ *
+ * Issued with lock held.
+ */
+static int
+csio_ln_read_fcf_entry(struct csio_lnode *ln,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct csio_hw *hw = ln->hwp;
+ struct csio_mb *mbp;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ /* Get FCoE FCF information */
+ csio_fcoe_read_fcf_init_mb(ln, mbp, CSIO_MB_DEFAULT_TMO,
+ ln->portid, ln->fcf_flowid, cbfn);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "failed to issue FCOE FCF cmd\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * csio_handle_link_up - Logical Linkup event.
+ * @hw - HW module.
+ * @portid - Physical port number
+ * @fcfi - FCF index.
+ * @vnpi - VNP index.
+ * Returns - none.
+ *
+ * This event is received from FW, when virtual link is established between
+ * Physical port[ENode] and FCF. If its new vnpi, then local node object is
+ * created on this FCF and set to [ONLINE] state.
+ * Lnode waits for FW_RDEV_CMD event to be received indicating that
+ * Fabric login is completed and lnode moves to [READY] state.
+ *
+ * This called with hw lock held
+ */
+static void
+csio_handle_link_up(struct csio_hw *hw, uint8_t portid, uint32_t fcfi,
+ uint32_t vnpi)
+{
+ struct csio_lnode *ln = NULL;
+
+ /* Lookup lnode based on vnpi */
+ ln = csio_ln_lookup_by_vnpi(hw, vnpi);
+ if (!ln) {
+ /* Pick lnode based on portid */
+ ln = csio_ln_lookup_by_portid(hw, portid);
+ if (!ln) {
+ csio_err(hw, "failed to lookup fcoe lnode on port:%d\n",
+ portid);
+ CSIO_DB_ASSERT(0);
+ return;
+ }
+
+ /* Check if lnode has valid vnp flowid */
+ if (ln->vnp_flowid != CSIO_INVALID_IDX) {
+ /* New VN-Port */
+ spin_unlock_irq(&hw->lock);
+ csio_lnode_alloc(hw);
+ spin_lock_irq(&hw->lock);
+ if (!ln) {
+ csio_err(hw,
+ "failed to allocate fcoe lnode"
+ "for port:%d vnpi:x%x\n",
+ portid, vnpi);
+ CSIO_DB_ASSERT(0);
+ return;
+ }
+ ln->portid = portid;
+ }
+ ln->vnp_flowid = vnpi;
+ ln->dev_num &= ~0xFFFF;
+ ln->dev_num |= vnpi;
+ }
+
+ /*Initialize fcfi */
+ ln->fcf_flowid = fcfi;
+
+ csio_info(hw, "Port:%d - FCOE LINK UP\n", portid);
+
+ CSIO_INC_STATS(ln, n_link_up);
+
+ /* Send LINKUP event to SM */
+ csio_post_event(&ln->sm, CSIO_LNE_LINKUP);
+}
+
+/*
+ * csio_post_event_rns
+ * @ln - FCOE lnode
+ * @evt - Given rnode event
+ * Returns - none
+ *
+ * Posts given rnode event to all FCOE rnodes connected with given Lnode.
+ * This routine is invoked when lnode receives LINK_DOWN/DOWN_LINK/CLOSE
+ * event.
+ *
+ * This called with hw lock held
+ */
+static void
+csio_post_event_rns(struct csio_lnode *ln, enum csio_rn_ev evt)
+{
+ struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead;
+ struct list_head *tmp, *next;
+ struct csio_rnode *rn;
+
+ list_for_each_safe(tmp, next, &rnhead->sm.sm_list) {
+ rn = (struct csio_rnode *) tmp;
+ csio_post_event(&rn->sm, evt);
+ }
+}
+
+/*
+ * csio_cleanup_rns
+ * @ln - FCOE lnode
+ * Returns - none
+ *
+ * Frees all FCOE rnodes connected with given Lnode.
+ *
+ * This called with hw lock held
+ */
+static void
+csio_cleanup_rns(struct csio_lnode *ln)
+{
+ struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead;
+ struct list_head *tmp, *next_rn;
+ struct csio_rnode *rn;
+
+ list_for_each_safe(tmp, next_rn, &rnhead->sm.sm_list) {
+ rn = (struct csio_rnode *) tmp;
+ csio_put_rnode(ln, rn);
+ }
+
+}
+
+/*
+ * csio_post_event_lns
+ * @ln - FCOE lnode
+ * @evt - Given lnode event
+ * Returns - none
+ *
+ * Posts given lnode event to all FCOE lnodes connected with given Lnode.
+ * This routine is invoked when lnode receives LINK_DOWN/DOWN_LINK/CLOSE
+ * event.
+ *
+ * This called with hw lock held
+ */
+static void
+csio_post_event_lns(struct csio_lnode *ln, enum csio_ln_ev evt)
+{
+ struct list_head *tmp;
+ struct csio_lnode *cln, *sln;
+
+ /* If NPIV lnode, send evt only to that and return */
+ if (csio_is_npiv_ln(ln)) {
+ csio_post_event(&ln->sm, evt);
+ return;
+ }
+
+ sln = ln;
+ /* Traverse children lnodes list and send evt */
+ list_for_each(tmp, &sln->cln_head) {
+ cln = (struct csio_lnode *) tmp;
+ csio_post_event(&cln->sm, evt);
+ }
+
+ /* Send evt to parent lnode */
+ csio_post_event(&ln->sm, evt);
+}
+
+/*
+ * csio_ln_down - Lcoal nport is down
+ * @ln - FCOE Lnode
+ * Returns - none
+ *
+ * Sends LINK_DOWN events to Lnode and its associated NPIVs lnodes.
+ *
+ * This called with hw lock held
+ */
+static void
+csio_ln_down(struct csio_lnode *ln)
+{
+ csio_post_event_lns(ln, CSIO_LNE_LINK_DOWN);
+}
+
+/*
+ * csio_handle_link_down - Logical Linkdown event.
+ * @hw - HW module.
+ * @portid - Physical port number
+ * @fcfi - FCF index.
+ * @vnpi - VNP index.
+ * Returns - none
+ *
+ * This event is received from FW, when virtual link goes down between
+ * Physical port[ENode] and FCF. Lnode and its associated NPIVs lnode hosted on
+ * this vnpi[VN-Port] will be de-instantiated.
+ *
+ * This called with hw lock held
+ */
+static void
+csio_handle_link_down(struct csio_hw *hw, uint8_t portid, uint32_t fcfi,
+ uint32_t vnpi)
+{
+ struct csio_fcf_info *fp;
+ struct csio_lnode *ln;
+
+ /* Lookup lnode based on vnpi */
+ ln = csio_ln_lookup_by_vnpi(hw, vnpi);
+ if (ln) {
+ fp = ln->fcfinfo;
+ CSIO_INC_STATS(ln, n_link_down);
+
+ /*Warn if linkdown received if lnode is not in ready state */
+ if (!csio_is_lnode_ready(ln)) {
+ csio_ln_warn(ln,
+ "warn: FCOE link is already in offline "
+ "Ignoring Fcoe linkdown event on portid %d\n",
+ portid);
+ CSIO_INC_STATS(ln, n_evt_drop);
+ return;
+ }
+
+ /* Verify portid */
+ if (fp->portid != portid) {
+ csio_ln_warn(ln,
+ "warn: FCOE linkdown recv with "
+ "invalid port %d\n", portid);
+ CSIO_INC_STATS(ln, n_evt_drop);
+ return;
+ }
+
+ /* verify fcfi */
+ if (ln->fcf_flowid != fcfi) {
+ csio_ln_warn(ln,
+ "warn: FCOE linkdown recv with "
+ "invalid fcfi x%x\n", fcfi);
+ CSIO_INC_STATS(ln, n_evt_drop);
+ return;
+ }
+
+ csio_info(hw, "Port:%d - FCOE LINK DOWN\n", portid);
+
+ /* Send LINK_DOWN event to lnode s/m */
+ csio_ln_down(ln);
+
+ return;
+ } else {
+ csio_warn(hw,
+ "warn: FCOE linkdown recv with invalid vnpi x%x\n",
+ vnpi);
+ CSIO_INC_STATS(hw, n_evt_drop);
+ }
+}
+
+/*
+ * csio_is_lnode_ready - Checks FCOE lnode is in ready state.
+ * @ln: Lnode module
+ *
+ * Returns True if FCOE lnode is in ready state.
+ */
+int
+csio_is_lnode_ready(struct csio_lnode *ln)
+{
+ return (csio_get_state(ln) == ((csio_sm_state_t)csio_lns_ready));
+}
+
+/*****************************************************************************/
+/* START: Lnode SM */
+/*****************************************************************************/
+/*
+ * csio_lns_uninit - The request in uninit state.
+ * @ln - FCOE lnode.
+ * @evt - Event to be processed.
+ *
+ * Process the given lnode event which is currently in "uninit" state.
+ * Invoked with HW lock held.
+ * Return - none.
+ */
+static void
+csio_lns_uninit(struct csio_lnode *ln, enum csio_ln_ev evt)
+{
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ struct csio_lnode *rln = hw->rln;
+ int rv;
+
+ CSIO_INC_STATS(ln, n_evt_sm[evt]);
+ switch (evt) {
+ case CSIO_LNE_LINKUP:
+ csio_set_state(&ln->sm, csio_lns_online);
+ /* Read FCF only for physical lnode */
+ if (csio_is_phys_ln(ln)) {
+ rv = csio_ln_read_fcf_entry(ln,
+ csio_ln_read_fcf_cbfn);
+ if (rv != 0) {
+ /* TODO: Send HW RESET event */
+ CSIO_INC_STATS(ln, n_err);
+ break;
+ }
+
+ /* Add FCF record */
+ list_add_tail(&ln->fcfinfo->list, &rln->fcf_lsthead);
+ }
+
+ rv = csio_ln_vnp_read(ln, csio_ln_vnp_read_cbfn);
+ if (rv != 0) {
+ /* TODO: Send HW RESET event */
+ CSIO_INC_STATS(ln, n_err);
+ }
+ break;
+
+ case CSIO_LNE_DOWN_LINK:
+ break;
+
+ default:
+ csio_ln_dbg(ln,
+ "unexp ln event %d recv from did:x%x in "
+ "ln state[uninit].\n", evt, ln->nport_id);
+ CSIO_INC_STATS(ln, n_evt_unexp);
+ break;
+ } /* switch event */
+}
+
+/*
+ * csio_lns_online - The request in online state.
+ * @ln - FCOE lnode.
+ * @evt - Event to be processed.
+ *
+ * Process the given lnode event which is currently in "online" state.
+ * Invoked with HW lock held.
+ * Return - none.
+ */
+static void
+csio_lns_online(struct csio_lnode *ln, enum csio_ln_ev evt)
+{
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ CSIO_INC_STATS(ln, n_evt_sm[evt]);
+ switch (evt) {
+ case CSIO_LNE_LINKUP:
+ csio_ln_warn(ln,
+ "warn: FCOE link is up already "
+ "Ignoring linkup on port:%d\n", ln->portid);
+ CSIO_INC_STATS(ln, n_evt_drop);
+ break;
+
+ case CSIO_LNE_FAB_INIT_DONE:
+ csio_set_state(&ln->sm, csio_lns_ready);
+
+ spin_unlock_irq(&hw->lock);
+ csio_lnode_async_event(ln, CSIO_LN_FC_LINKUP);
+ spin_lock_irq(&hw->lock);
+
+ break;
+
+ case CSIO_LNE_LINK_DOWN:
+ /* Fall through */
+ case CSIO_LNE_DOWN_LINK:
+ csio_set_state(&ln->sm, csio_lns_uninit);
+ if (csio_is_phys_ln(ln)) {
+ /* Remove FCF entry */
+ list_del_init(&ln->fcfinfo->list);
+ }
+ break;
+
+ default:
+ csio_ln_dbg(ln,
+ "unexp ln event %d recv from did:x%x in "
+ "ln state[uninit].\n", evt, ln->nport_id);
+ CSIO_INC_STATS(ln, n_evt_unexp);
+
+ break;
+ } /* switch event */
+}
+
+/*
+ * csio_lns_ready - The request in ready state.
+ * @ln - FCOE lnode.
+ * @evt - Event to be processed.
+ *
+ * Process the given lnode event which is currently in "ready" state.
+ * Invoked with HW lock held.
+ * Return - none.
+ */
+static void
+csio_lns_ready(struct csio_lnode *ln, enum csio_ln_ev evt)
+{
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ CSIO_INC_STATS(ln, n_evt_sm[evt]);
+ switch (evt) {
+ case CSIO_LNE_FAB_INIT_DONE:
+ csio_ln_dbg(ln,
+ "ignoring event %d recv from did x%x"
+ "in ln state[ready].\n", evt, ln->nport_id);
+ CSIO_INC_STATS(ln, n_evt_drop);
+ break;
+
+ case CSIO_LNE_LINK_DOWN:
+ csio_set_state(&ln->sm, csio_lns_offline);
+ csio_post_event_rns(ln, CSIO_RNFE_DOWN);
+
+ spin_unlock_irq(&hw->lock);
+ csio_lnode_async_event(ln, CSIO_LN_FC_LINKDOWN);
+ spin_lock_irq(&hw->lock);
+
+ if (csio_is_phys_ln(ln)) {
+ /* Remove FCF entry */
+ list_del_init(&ln->fcfinfo->list);
+ }
+ break;
+
+ case CSIO_LNE_DOWN_LINK:
+ csio_set_state(&ln->sm, csio_lns_offline);
+ csio_post_event_rns(ln, CSIO_RNFE_DOWN);
+
+ /* Host need to issue aborts in case if FW has not returned
+ * WRs with status "ABORTED"
+ */
+ spin_unlock_irq(&hw->lock);
+ csio_lnode_async_event(ln, CSIO_LN_FC_LINKDOWN);
+ spin_lock_irq(&hw->lock);
+
+ if (csio_is_phys_ln(ln)) {
+ /* Remove FCF entry */
+ list_del_init(&ln->fcfinfo->list);
+ }
+ break;
+
+ case CSIO_LNE_CLOSE:
+ csio_set_state(&ln->sm, csio_lns_uninit);
+ csio_post_event_rns(ln, CSIO_RNFE_CLOSE);
+ break;
+
+ case CSIO_LNE_LOGO:
+ csio_set_state(&ln->sm, csio_lns_offline);
+ csio_post_event_rns(ln, CSIO_RNFE_DOWN);
+ break;
+
+ default:
+ csio_ln_dbg(ln,
+ "unexp ln event %d recv from did:x%x in "
+ "ln state[uninit].\n", evt, ln->nport_id);
+ CSIO_INC_STATS(ln, n_evt_unexp);
+ CSIO_DB_ASSERT(0);
+ break;
+ } /* switch event */
+}
+
+/*
+ * csio_lns_offline - The request in offline state.
+ * @ln - FCOE lnode.
+ * @evt - Event to be processed.
+ *
+ * Process the given lnode event which is currently in "offline" state.
+ * Invoked with HW lock held.
+ * Return - none.
+ */
+static void
+csio_lns_offline(struct csio_lnode *ln, enum csio_ln_ev evt)
+{
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ struct csio_lnode *rln = hw->rln;
+ int rv;
+
+ CSIO_INC_STATS(ln, n_evt_sm[evt]);
+ switch (evt) {
+ case CSIO_LNE_LINKUP:
+ csio_set_state(&ln->sm, csio_lns_online);
+ /* Read FCF only for physical lnode */
+ if (csio_is_phys_ln(ln)) {
+ rv = csio_ln_read_fcf_entry(ln,
+ csio_ln_read_fcf_cbfn);
+ if (rv != 0) {
+ /* TODO: Send HW RESET event */
+ CSIO_INC_STATS(ln, n_err);
+ break;
+ }
+
+ /* Add FCF record */
+ list_add_tail(&ln->fcfinfo->list, &rln->fcf_lsthead);
+ }
+
+ rv = csio_ln_vnp_read(ln, csio_ln_vnp_read_cbfn);
+ if (rv != 0) {
+ /* TODO: Send HW RESET event */
+ CSIO_INC_STATS(ln, n_err);
+ }
+ break;
+
+ case CSIO_LNE_LINK_DOWN:
+ case CSIO_LNE_DOWN_LINK:
+ case CSIO_LNE_LOGO:
+ csio_ln_dbg(ln,
+ "ignoring event %d recv from did x%x"
+ "in ln state[offline].\n", evt, ln->nport_id);
+ CSIO_INC_STATS(ln, n_evt_drop);
+ break;
+
+ case CSIO_LNE_CLOSE:
+ csio_set_state(&ln->sm, csio_lns_uninit);
+ csio_post_event_rns(ln, CSIO_RNFE_CLOSE);
+ break;
+
+ default:
+ csio_ln_dbg(ln,
+ "unexp ln event %d recv from did:x%x in "
+ "ln state[offline]\n", evt, ln->nport_id);
+ CSIO_INC_STATS(ln, n_evt_unexp);
+ CSIO_DB_ASSERT(0);
+ break;
+ } /* switch event */
+}
+
+/*****************************************************************************/
+/* END: Lnode SM */
+/*****************************************************************************/
+
+static void
+csio_free_fcfinfo(struct kref *kref)
+{
+ struct csio_fcf_info *fcfinfo = container_of(kref,
+ struct csio_fcf_info, kref);
+ kfree(fcfinfo);
+}
+
+/* Helper routines for attributes */
+/*
+ * csio_lnode_state_to_str - Get current state of FCOE lnode.
+ * @ln - lnode
+ * @str - state of lnode.
+ *
+ */
+void
+csio_lnode_state_to_str(struct csio_lnode *ln, int8_t *str)
+{
+ if (csio_get_state(ln) == ((csio_sm_state_t)csio_lns_uninit)) {
+ strcpy(str, "UNINIT");
+ return;
+ }
+ if (csio_get_state(ln) == ((csio_sm_state_t)csio_lns_ready)) {
+ strcpy(str, "READY");
+ return;
+ }
+ if (csio_get_state(ln) == ((csio_sm_state_t)csio_lns_offline)) {
+ strcpy(str, "OFFLINE");
+ return;
+ }
+ strcpy(str, "UNKNOWN");
+} /* csio_lnode_state_to_str */
+
+
+int
+csio_get_phy_port_stats(struct csio_hw *hw, uint8_t portid,
+ struct fw_fcoe_port_stats *port_stats)
+{
+ struct csio_mb *mbp;
+ struct fw_fcoe_port_cmd_params portparams;
+ enum fw_retval retval;
+ int idx;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ csio_err(hw, "FCoE FCF PARAMS command out of memory!\n");
+ return -EINVAL;
+ }
+ portparams.portid = portid;
+
+ for (idx = 1; idx <= 3; idx++) {
+ portparams.idx = (idx-1)*6 + 1;
+ portparams.nstats = 6;
+ if (idx == 3)
+ portparams.nstats = 4;
+ csio_fcoe_read_portparams_init_mb(hw, mbp, CSIO_MB_DEFAULT_TMO,
+ &portparams, NULL);
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of FCoE port params failed!\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+ csio_mb_process_portparams_rsp(hw, mbp, &retval,
+ &portparams, port_stats);
+ }
+
+ mempool_free(mbp, hw->mb_mempool);
+ return 0;
+}
+
+/*
+ * csio_ln_mgmt_wr_handler -Mgmt Work Request handler.
+ * @wr - WR.
+ * @len - WR len.
+ * This handler is invoked when an outstanding mgmt WR is completed.
+ * Its invoked in the context of FW event worker thread for every
+ * mgmt event received.
+ * Return - none.
+ */
+
+static void
+csio_ln_mgmt_wr_handler(struct csio_hw *hw, void *wr, uint32_t len)
+{
+ struct csio_mgmtm *mgmtm = csio_hw_to_mgmtm(hw);
+ struct csio_ioreq *io_req = NULL;
+ struct fw_fcoe_els_ct_wr *wr_cmd;
+
+
+ wr_cmd = (struct fw_fcoe_els_ct_wr *) wr;
+
+ if (len < sizeof(struct fw_fcoe_els_ct_wr)) {
+ csio_err(mgmtm->hw,
+ "Invalid ELS CT WR length recvd, len:%x\n", len);
+ mgmtm->stats.n_err++;
+ return;
+ }
+
+ io_req = (struct csio_ioreq *) ((uintptr_t) wr_cmd->cookie);
+ io_req->wr_status = csio_wr_status(wr_cmd);
+
+ /* lookup ioreq exists in our active Q */
+ spin_lock_irq(&hw->lock);
+ if (csio_mgmt_req_lookup(mgmtm, io_req) != 0) {
+ csio_err(mgmtm->hw,
+ "Error- Invalid IO handle recv in WR. handle: %p\n",
+ io_req);
+ mgmtm->stats.n_err++;
+ spin_unlock_irq(&hw->lock);
+ return;
+ }
+
+ mgmtm = csio_hw_to_mgmtm(hw);
+
+ /* Dequeue from active queue */
+ list_del_init(&io_req->sm.sm_list);
+ mgmtm->stats.n_active--;
+ spin_unlock_irq(&hw->lock);
+
+ /* io_req will be freed by completion handler */
+ if (io_req->io_cbfn)
+ io_req->io_cbfn(hw, io_req);
+}
+
+/**
+ * csio_fcoe_fwevt_handler - Event handler for Firmware FCoE events.
+ * @hw: HW module
+ * @cpl_op: CPL opcode
+ * @cmd: FW cmd/WR.
+ *
+ * Process received FCoE cmd/WR event from FW.
+ */
+void
+csio_fcoe_fwevt_handler(struct csio_hw *hw, __u8 cpl_op, __be64 *cmd)
+{
+ struct csio_lnode *ln;
+ struct csio_rnode *rn;
+ uint8_t portid, opcode = *(uint8_t *)cmd;
+ struct fw_fcoe_link_cmd *lcmd;
+ struct fw_wr_hdr *wr;
+ struct fw_rdev_wr *rdev_wr;
+ enum fw_fcoe_link_status lstatus;
+ uint32_t fcfi, rdev_flowid, vnpi;
+ enum csio_ln_ev evt;
+
+ if (cpl_op == CPL_FW6_MSG && opcode == FW_FCOE_LINK_CMD) {
+
+ lcmd = (struct fw_fcoe_link_cmd *)cmd;
+ lstatus = lcmd->lstatus;
+ portid = FW_FCOE_LINK_CMD_PORTID_GET(
+ ntohl(lcmd->op_to_portid));
+ fcfi = FW_FCOE_LINK_CMD_FCFI_GET(ntohl(lcmd->sub_opcode_fcfi));
+ vnpi = FW_FCOE_LINK_CMD_VNPI_GET(ntohl(lcmd->vnpi_pkd));
+
+ if (lstatus == FCOE_LINKUP) {
+
+ /* HW lock here */
+ spin_lock_irq(&hw->lock);
+ csio_handle_link_up(hw, portid, fcfi, vnpi);
+ spin_unlock_irq(&hw->lock);
+ /* HW un lock here */
+
+ } else if (lstatus == FCOE_LINKDOWN) {
+
+ /* HW lock here */
+ spin_lock_irq(&hw->lock);
+ csio_handle_link_down(hw, portid, fcfi, vnpi);
+ spin_unlock_irq(&hw->lock);
+ /* HW un lock here */
+ } else {
+ csio_warn(hw, "Unexpected FCOE LINK status:0x%x\n",
+ lcmd->lstatus);
+ CSIO_INC_STATS(hw, n_cpl_unexp);
+ }
+ } else if (cpl_op == CPL_FW6_PLD) {
+ wr = (struct fw_wr_hdr *) (cmd + 4);
+ if (FW_WR_OP_GET(be32_to_cpu(wr->hi))
+ == FW_RDEV_WR) {
+
+ rdev_wr = (struct fw_rdev_wr *) (cmd + 4);
+
+ rdev_flowid = FW_RDEV_WR_FLOWID_GET(
+ ntohl(rdev_wr->alloc_to_len16));
+ vnpi = FW_RDEV_WR_ASSOC_FLOWID_GET(
+ ntohl(rdev_wr->flags_to_assoc_flowid));
+
+ csio_dbg(hw,
+ "FW_RDEV_WR: flowid:x%x ev_cause:x%x "
+ "vnpi:0x%x\n", rdev_flowid,
+ rdev_wr->event_cause, vnpi);
+
+ if (rdev_wr->protocol != PROT_FCOE) {
+ csio_err(hw,
+ "FW_RDEV_WR: invalid proto:x%x "
+ "received with flowid:x%x\n",
+ rdev_wr->protocol,
+ rdev_flowid);
+ CSIO_INC_STATS(hw, n_evt_drop);
+ return;
+ }
+
+ /* HW lock here */
+ spin_lock_irq(&hw->lock);
+ ln = csio_ln_lookup_by_vnpi(hw, vnpi);
+ if (!ln) {
+ csio_err(hw,
+ "FW_DEV_WR: invalid vnpi:x%x received "
+ "with flowid:x%x\n", vnpi, rdev_flowid);
+ CSIO_INC_STATS(hw, n_evt_drop);
+ goto out_pld;
+ }
+
+ rn = csio_confirm_rnode(ln, rdev_flowid,
+ &rdev_wr->u.fcoe_rdev);
+ if (!rn) {
+ csio_ln_dbg(ln,
+ "Failed to confirm rnode "
+ "for flowid:x%x\n", rdev_flowid);
+ CSIO_INC_STATS(hw, n_evt_drop);
+ goto out_pld;
+ }
+
+ /* save previous event for debugging */
+ ln->prev_evt = ln->cur_evt;
+ ln->cur_evt = rdev_wr->event_cause;
+ CSIO_INC_STATS(ln, n_evt_fw[rdev_wr->event_cause]);
+
+ /* Translate all the fabric events to lnode SM events */
+ evt = CSIO_FWE_TO_LNE(rdev_wr->event_cause);
+ if (evt) {
+ csio_ln_dbg(ln,
+ "Posting event to lnode event:%d "
+ "cause:%d flowid:x%x\n", evt,
+ rdev_wr->event_cause, rdev_flowid);
+ csio_post_event(&ln->sm, evt);
+ }
+
+ /* Handover event to rn SM here. */
+ csio_rnode_fwevt_handler(rn, rdev_wr->event_cause);
+out_pld:
+ spin_unlock_irq(&hw->lock);
+ return;
+ } else {
+ csio_warn(hw, "unexpected WR op(0x%x) recv\n",
+ FW_WR_OP_GET(be32_to_cpu((wr->hi))));
+ CSIO_INC_STATS(hw, n_cpl_unexp);
+ }
+ } else if (cpl_op == CPL_FW6_MSG) {
+ wr = (struct fw_wr_hdr *) (cmd);
+ if (FW_WR_OP_GET(be32_to_cpu(wr->hi)) == FW_FCOE_ELS_CT_WR) {
+ csio_ln_mgmt_wr_handler(hw, wr,
+ sizeof(struct fw_fcoe_els_ct_wr));
+ } else {
+ csio_warn(hw, "unexpected WR op(0x%x) recv\n",
+ FW_WR_OP_GET(be32_to_cpu((wr->hi))));
+ CSIO_INC_STATS(hw, n_cpl_unexp);
+ }
+ } else {
+ csio_warn(hw, "unexpected CPL op(0x%x) recv\n", opcode);
+ CSIO_INC_STATS(hw, n_cpl_unexp);
+ }
+}
+
+/**
+ * csio_lnode_start - Kickstart lnode discovery.
+ * @ln: lnode
+ *
+ * This routine kickstarts the discovery by issuing an FCOE_LINK (up) command.
+ */
+int
+csio_lnode_start(struct csio_lnode *ln)
+{
+ int rv = 0;
+ if (csio_is_phys_ln(ln) && !(ln->flags & CSIO_LNF_LINK_ENABLE)) {
+ rv = csio_fcoe_enable_link(ln, 1);
+ ln->flags |= CSIO_LNF_LINK_ENABLE;
+ }
+
+ return rv;
+}
+
+/**
+ * csio_lnode_stop - Stop the lnode.
+ * @ln: lnode
+ *
+ * This routine is invoked by HW module to stop lnode and its associated NPIV
+ * lnodes.
+ */
+void
+csio_lnode_stop(struct csio_lnode *ln)
+{
+ csio_post_event_lns(ln, CSIO_LNE_DOWN_LINK);
+ if (csio_is_phys_ln(ln) && (ln->flags & CSIO_LNF_LINK_ENABLE)) {
+ csio_fcoe_enable_link(ln, 0);
+ ln->flags &= ~CSIO_LNF_LINK_ENABLE;
+ }
+ csio_ln_dbg(ln, "stopping ln :%p\n", ln);
+}
+
+/**
+ * csio_lnode_close - Close an lnode.
+ * @ln: lnode
+ *
+ * This routine is invoked by HW module to close an lnode and its
+ * associated NPIV lnodes. Lnode and its associated NPIV lnodes are
+ * set to uninitialized state.
+ */
+void
+csio_lnode_close(struct csio_lnode *ln)
+{
+ csio_post_event_lns(ln, CSIO_LNE_CLOSE);
+ if (csio_is_phys_ln(ln))
+ ln->vnp_flowid = CSIO_INVALID_IDX;
+
+ csio_ln_dbg(ln, "closed ln :%p\n", ln);
+}
+
+/*
+ * csio_ln_prep_ecwr - Prepare ELS/CT WR.
+ * @io_req - IO request.
+ * @wr_len - WR len
+ * @immd_len - WR immediate data
+ * @sub_op - Sub opcode
+ * @sid - source portid.
+ * @did - destination portid
+ * @flow_id - flowid
+ * @fw_wr - ELS/CT WR to be prepared.
+ * Returns: 0 - on success
+ */
+static int
+csio_ln_prep_ecwr(struct csio_ioreq *io_req, uint32_t wr_len,
+ uint32_t immd_len, uint8_t sub_op, uint32_t sid,
+ uint32_t did, uint32_t flow_id, uint8_t *fw_wr)
+{
+ struct fw_fcoe_els_ct_wr *wr;
+ __be32 port_id;
+
+ wr = (struct fw_fcoe_els_ct_wr *)fw_wr;
+ wr->op_immdlen = cpu_to_be32(FW_WR_OP(FW_FCOE_ELS_CT_WR) |
+ FW_FCOE_ELS_CT_WR_IMMDLEN(immd_len));
+
+ wr_len = DIV_ROUND_UP(wr_len, 16);
+ wr->flowid_len16 = cpu_to_be32(FW_WR_FLOWID(flow_id) |
+ FW_WR_LEN16(wr_len));
+ wr->els_ct_type = sub_op;
+ wr->ctl_pri = 0;
+ wr->cp_en_class = 0;
+ wr->cookie = io_req->fw_handle;
+ wr->iqid = cpu_to_be16(csio_q_physiqid(
+ io_req->lnode->hwp, io_req->iq_idx));
+ wr->fl_to_sp = FW_FCOE_ELS_CT_WR_SP(1);
+ wr->tmo_val = (uint8_t) io_req->tmo;
+ port_id = htonl(sid);
+ memcpy(wr->l_id, PORT_ID_PTR(port_id), 3);
+ port_id = htonl(did);
+ memcpy(wr->r_id, PORT_ID_PTR(port_id), 3);
+
+ /* Prepare RSP SGL */
+ wr->rsp_dmalen = cpu_to_be32(io_req->dma_buf.len);
+ wr->rsp_dmaaddr = cpu_to_be64(io_req->dma_buf.paddr);
+ return 0;
+}
+
+/*
+ * csio_ln_mgmt_submit_wr - Post elsct work request.
+ * @mgmtm - mgmtm
+ * @io_req - io request.
+ * @sub_op - ELS or CT request type
+ * @pld - Dma Payload buffer
+ * @pld_len - Payload len
+ * Prepares ELSCT Work request and sents it to FW.
+ * Returns: 0 - on success
+ */
+static int
+csio_ln_mgmt_submit_wr(struct csio_mgmtm *mgmtm, struct csio_ioreq *io_req,
+ uint8_t sub_op, struct csio_dma_buf *pld,
+ uint32_t pld_len)
+{
+ struct csio_wr_pair wrp;
+ struct csio_lnode *ln = io_req->lnode;
+ struct csio_rnode *rn = io_req->rnode;
+ struct csio_hw *hw = mgmtm->hw;
+ uint8_t fw_wr[64];
+ struct ulptx_sgl dsgl;
+ uint32_t wr_size = 0;
+ uint8_t im_len = 0;
+ uint32_t wr_off = 0;
+
+ int ret = 0;
+
+ /* Calculate WR Size for this ELS REQ */
+ wr_size = sizeof(struct fw_fcoe_els_ct_wr);
+
+ /* Send as immediate data if pld < 256 */
+ if (pld_len < 256) {
+ wr_size += ALIGN(pld_len, 8);
+ im_len = (uint8_t)pld_len;
+ } else
+ wr_size += sizeof(struct ulptx_sgl);
+
+ /* Roundup WR size in units of 16 bytes */
+ wr_size = ALIGN(wr_size, 16);
+
+ /* Get WR to send ELS REQ */
+ ret = csio_wr_get(hw, mgmtm->eq_idx, wr_size, &wrp);
+ if (ret != 0) {
+ csio_err(hw, "Failed to get WR for ec_req %p ret:%d\n",
+ io_req, ret);
+ return ret;
+ }
+
+ /* Prepare Generic WR used by all ELS/CT cmd */
+ csio_ln_prep_ecwr(io_req, wr_size, im_len, sub_op,
+ ln->nport_id, rn->nport_id,
+ csio_rn_flowid(rn),
+ &fw_wr[0]);
+
+ /* Copy ELS/CT WR CMD */
+ csio_wr_copy_to_wrp(&fw_wr[0], &wrp, wr_off,
+ sizeof(struct fw_fcoe_els_ct_wr));
+ wr_off += sizeof(struct fw_fcoe_els_ct_wr);
+
+ /* Copy payload to Immediate section of WR */
+ if (im_len)
+ csio_wr_copy_to_wrp(pld->vaddr, &wrp, wr_off, im_len);
+ else {
+ /* Program DSGL to dma payload */
+ dsgl.cmd_nsge = htonl(ULPTX_CMD(ULP_TX_SC_DSGL) |
+ ULPTX_MORE | ULPTX_NSGE(1));
+ dsgl.len0 = cpu_to_be32(pld_len);
+ dsgl.addr0 = cpu_to_be64(pld->paddr);
+ csio_wr_copy_to_wrp(&dsgl, &wrp, ALIGN(wr_off, 8),
+ sizeof(struct ulptx_sgl));
+ }
+
+ /* Issue work request to xmit ELS/CT req to FW */
+ csio_wr_issue(mgmtm->hw, mgmtm->eq_idx, false);
+ return ret;
+}
+
+/*
+ * csio_ln_mgmt_submit_req - Submit FCOE Mgmt request.
+ * @io_req - IO Request
+ * @io_cbfn - Completion handler.
+ * @req_type - ELS or CT request type
+ * @pld - Dma Payload buffer
+ * @pld_len - Payload len
+ *
+ *
+ * This API used submit managment ELS/CT request.
+ * This called with hw lock held
+ * Returns: 0 - on success
+ * -ENOMEM - on error.
+ */
+static int
+csio_ln_mgmt_submit_req(struct csio_ioreq *io_req,
+ void (*io_cbfn) (struct csio_hw *, struct csio_ioreq *),
+ enum fcoe_cmn_type req_type, struct csio_dma_buf *pld,
+ uint32_t pld_len)
+{
+ struct csio_hw *hw = csio_lnode_to_hw(io_req->lnode);
+ struct csio_mgmtm *mgmtm = csio_hw_to_mgmtm(hw);
+ int rv;
+
+ io_req->io_cbfn = io_cbfn; /* Upper layer callback handler */
+ io_req->fw_handle = (uintptr_t) (io_req);
+ io_req->eq_idx = mgmtm->eq_idx;
+ io_req->iq_idx = mgmtm->iq_idx;
+
+ rv = csio_ln_mgmt_submit_wr(mgmtm, io_req, req_type, pld, pld_len);
+ if (rv == 0) {
+ list_add_tail(&io_req->sm.sm_list, &mgmtm->active_q);
+ mgmtm->stats.n_active++;
+ }
+ return rv;
+}
+
+/*
+ * csio_ln_fdmi_init - FDMI Init entry point.
+ * @ln: lnode
+ */
+static int
+csio_ln_fdmi_init(struct csio_lnode *ln)
+{
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ struct csio_dma_buf *dma_buf;
+
+ /* Allocate MGMT request required for FDMI */
+ ln->mgmt_req = kzalloc(sizeof(struct csio_ioreq), GFP_KERNEL);
+ if (!ln->mgmt_req) {
+ csio_ln_err(ln, "Failed to alloc ioreq for FDMI\n");
+ CSIO_INC_STATS(hw, n_err_nomem);
+ return -ENOMEM;
+ }
+
+ /* Allocate Dma buffers for FDMI response Payload */
+ dma_buf = &ln->mgmt_req->dma_buf;
+ dma_buf->len = 2048;
+ dma_buf->vaddr = pci_alloc_consistent(hw->pdev, dma_buf->len,
+ &dma_buf->paddr);
+ if (!dma_buf->vaddr) {
+ csio_err(hw, "Failed to alloc DMA buffer for FDMI!\n");
+ kfree(ln->mgmt_req);
+ ln->mgmt_req = NULL;
+ return -ENOMEM;
+ }
+
+ ln->flags |= CSIO_LNF_FDMI_ENABLE;
+ return 0;
+}
+
+/*
+ * csio_ln_fdmi_exit - FDMI exit entry point.
+ * @ln: lnode
+ */
+static int
+csio_ln_fdmi_exit(struct csio_lnode *ln)
+{
+ struct csio_dma_buf *dma_buf;
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ if (!ln->mgmt_req)
+ return 0;
+
+ dma_buf = &ln->mgmt_req->dma_buf;
+ if (dma_buf->vaddr)
+ pci_free_consistent(hw->pdev, dma_buf->len, dma_buf->vaddr,
+ dma_buf->paddr);
+
+ kfree(ln->mgmt_req);
+ return 0;
+}
+
+int
+csio_scan_done(struct csio_lnode *ln, unsigned long ticks,
+ unsigned long time, unsigned long max_scan_ticks,
+ unsigned long delta_scan_ticks)
+{
+ int rv = 0;
+
+ if (time >= max_scan_ticks)
+ return 1;
+
+ if (!ln->tgt_scan_tick)
+ ln->tgt_scan_tick = ticks;
+
+ if (((ticks - ln->tgt_scan_tick) >= delta_scan_ticks)) {
+ if (!ln->last_scan_ntgts)
+ ln->last_scan_ntgts = ln->n_scsi_tgts;
+ else {
+ if (ln->last_scan_ntgts == ln->n_scsi_tgts)
+ return 1;
+
+ ln->last_scan_ntgts = ln->n_scsi_tgts;
+ }
+ ln->tgt_scan_tick = ticks;
+ }
+ return rv;
+}
+
+/*
+ * csio_notify_lnodes:
+ * @hw: HW module
+ * @note: Notification
+ *
+ * Called from the HW SM to fan out notifications to the
+ * Lnode SM. Since the HW SM is entered with lock held,
+ * there is no need to hold locks here.
+ *
+ */
+void
+csio_notify_lnodes(struct csio_hw *hw, enum csio_ln_notify note)
+{
+ struct list_head *tmp;
+ struct csio_lnode *ln;
+
+ csio_dbg(hw, "Notifying all nodes of event %d\n", note);
+
+ /* Traverse children lnodes list and send evt */
+ list_for_each(tmp, &hw->sln_head) {
+ ln = (struct csio_lnode *) tmp;
+
+ switch (note) {
+ case CSIO_LN_NOTIFY_HWREADY:
+ csio_lnode_start(ln);
+ break;
+
+ case CSIO_LN_NOTIFY_HWRESET:
+ case CSIO_LN_NOTIFY_HWREMOVE:
+ csio_lnode_close(ln);
+ break;
+
+ case CSIO_LN_NOTIFY_HWSTOP:
+ csio_lnode_stop(ln);
+ break;
+
+ default:
+ break;
+
+ }
+ }
+}
+
+/*
+ * csio_disable_lnodes:
+ * @hw: HW module
+ * @portid:port id
+ * @disable: disable/enable flag.
+ * If disable=1, disables all lnode hosted on given physical port.
+ * otherwise enables all the lnodes on given phsysical port.
+ * This routine need to called with hw lock held.
+ */
+void
+csio_disable_lnodes(struct csio_hw *hw, uint8_t portid, bool disable)
+{
+ struct list_head *tmp;
+ struct csio_lnode *ln;
+
+ csio_dbg(hw, "Notifying event to all nodes of port:%d\n", portid);
+
+ /* Traverse sibling lnodes list and send evt */
+ list_for_each(tmp, &hw->sln_head) {
+ ln = (struct csio_lnode *) tmp;
+ if (ln->portid != portid)
+ continue;
+
+ if (disable)
+ csio_lnode_stop(ln);
+ else
+ csio_lnode_start(ln);
+ }
+}
+
+/*
+ * csio_ln_init - Initialize an lnode.
+ * @ln: lnode
+ *
+ */
+static int
+csio_ln_init(struct csio_lnode *ln)
+{
+ int rv = -EINVAL;
+ struct csio_lnode *rln, *pln;
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ csio_init_state(&ln->sm, csio_lns_uninit);
+ ln->vnp_flowid = CSIO_INVALID_IDX;
+ ln->fcf_flowid = CSIO_INVALID_IDX;
+
+ if (csio_is_root_ln(ln)) {
+
+ /* This is the lnode used during initialization */
+
+ ln->fcfinfo = kzalloc(sizeof(struct csio_fcf_info), GFP_KERNEL);
+ if (!ln->fcfinfo) {
+ csio_ln_err(ln, "Failed to alloc FCF record\n");
+ CSIO_INC_STATS(hw, n_err_nomem);
+ goto err;
+ }
+
+ INIT_LIST_HEAD(&ln->fcf_lsthead);
+ kref_init(&ln->fcfinfo->kref);
+
+ if (csio_fdmi_enable && csio_ln_fdmi_init(ln))
+ goto err;
+
+ } else { /* Either a non-root physical or a virtual lnode */
+
+ /*
+ * THe rest is common for non-root physical and NPIV lnodes.
+ * Just get references to all other modules
+ */
+ rln = csio_root_lnode(ln);
+
+ if (csio_is_npiv_ln(ln)) {
+ /* NPIV */
+ pln = csio_parent_lnode(ln);
+ kref_get(&pln->fcfinfo->kref);
+ ln->fcfinfo = pln->fcfinfo;
+ } else {
+ /* Another non-root physical lnode (FCF) */
+ ln->fcfinfo = kzalloc(sizeof(struct csio_fcf_info),
+ GFP_KERNEL);
+ if (!ln->fcfinfo) {
+ csio_ln_err(ln, "Failed to alloc FCF info\n");
+ CSIO_INC_STATS(hw, n_err_nomem);
+ goto err;
+ }
+
+ kref_init(&ln->fcfinfo->kref);
+
+ if (csio_fdmi_enable && csio_ln_fdmi_init(ln))
+ goto err;
+ }
+
+ } /* if (!csio_is_root_ln(ln)) */
+
+ return 0;
+err:
+ return rv;
+}
+
+static void
+csio_ln_exit(struct csio_lnode *ln)
+{
+ struct csio_lnode *pln;
+
+ csio_cleanup_rns(ln);
+ if (csio_is_npiv_ln(ln)) {
+ pln = csio_parent_lnode(ln);
+ kref_put(&pln->fcfinfo->kref, csio_free_fcfinfo);
+ } else {
+ kref_put(&ln->fcfinfo->kref, csio_free_fcfinfo);
+ if (csio_fdmi_enable)
+ csio_ln_fdmi_exit(ln);
+ }
+ ln->fcfinfo = NULL;
+}
+
+/**
+ * csio_lnode_init - Initialize the members of an lnode.
+ * @ln: lnode
+ *
+ */
+int
+csio_lnode_init(struct csio_lnode *ln, struct csio_hw *hw,
+ struct csio_lnode *pln)
+{
+ int rv = -EINVAL;
+
+ /* Link this lnode to hw */
+ csio_lnode_to_hw(ln) = hw;
+
+ /* Link child to parent if child lnode */
+ if (pln)
+ ln->pln = pln;
+ else
+ ln->pln = NULL;
+
+ /* Initialize scsi_tgt and timers to zero */
+ ln->n_scsi_tgts = 0;
+ ln->last_scan_ntgts = 0;
+ ln->tgt_scan_tick = 0;
+
+ /* Initialize rnode list */
+ INIT_LIST_HEAD(&ln->rnhead);
+ INIT_LIST_HEAD(&ln->cln_head);
+
+ /* Initialize log level for debug */
+ ln->params.log_level = hw->params.log_level;
+
+ if (csio_ln_init(ln))
+ goto err;
+
+ /* Add lnode to list of sibling or children lnodes */
+ spin_lock_irq(&hw->lock);
+ list_add_tail(&ln->sm.sm_list, pln ? &pln->cln_head : &hw->sln_head);
+ if (pln)
+ pln->num_vports++;
+ spin_unlock_irq(&hw->lock);
+
+ hw->num_lns++;
+
+ return 0;
+err:
+ csio_lnode_to_hw(ln) = NULL;
+ return rv;
+}
+
+/**
+ * csio_lnode_exit - De-instantiate an lnode.
+ * @ln: lnode
+ *
+ */
+void
+csio_lnode_exit(struct csio_lnode *ln)
+{
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ csio_ln_exit(ln);
+
+ /* Remove this lnode from hw->sln_head */
+ spin_lock_irq(&hw->lock);
+
+ list_del_init(&ln->sm.sm_list);
+
+ /* If it is children lnode, decrement the
+ * counter in its parent lnode
+ */
+ if (ln->pln)
+ ln->pln->num_vports--;
+
+ /* Update root lnode pointer */
+ if (list_empty(&hw->sln_head))
+ hw->rln = NULL;
+ else
+ hw->rln = (struct csio_lnode *)csio_list_next(&hw->sln_head);
+
+ spin_unlock_irq(&hw->lock);
+
+ csio_lnode_to_hw(ln) = NULL;
+ hw->num_lns--;
+}
diff --git a/drivers/scsi/csiostor/csio_lnode.h b/drivers/scsi/csiostor/csio_lnode.h
new file mode 100644
index 00000000000..8d84988ab06
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_lnode.h
@@ -0,0 +1,255 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CSIO_LNODE_H__
+#define __CSIO_LNODE_H__
+
+#include <linux/kref.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <scsi/fc/fc_els.h>
+
+
+#include "csio_defs.h"
+#include "csio_hw.h"
+
+#define CSIO_FCOE_MAX_NPIV 128
+#define CSIO_FCOE_MAX_RNODES 2048
+
+/* FDMI port attribute unknown speed */
+#define CSIO_HBA_PORTSPEED_UNKNOWN 0x8000
+
+extern int csio_fcoe_rnodes;
+extern int csio_fdmi_enable;
+
+/* State machine evets */
+enum csio_ln_ev {
+ CSIO_LNE_NONE = (uint32_t)0,
+ CSIO_LNE_LINKUP,
+ CSIO_LNE_FAB_INIT_DONE,
+ CSIO_LNE_LINK_DOWN,
+ CSIO_LNE_DOWN_LINK,
+ CSIO_LNE_LOGO,
+ CSIO_LNE_CLOSE,
+ CSIO_LNE_MAX_EVENT,
+};
+
+
+struct csio_fcf_info {
+ struct list_head list;
+ uint8_t priority;
+ uint8_t mac[6];
+ uint8_t name_id[8];
+ uint8_t fabric[8];
+ uint16_t vf_id;
+ uint8_t vlan_id;
+ uint16_t max_fcoe_size;
+ uint8_t fc_map[3];
+ uint32_t fka_adv;
+ uint32_t fcfi;
+ uint8_t get_next:1;
+ uint8_t link_aff:1;
+ uint8_t fpma:1;
+ uint8_t spma:1;
+ uint8_t login:1;
+ uint8_t portid;
+ uint8_t spma_mac[6];
+ struct kref kref;
+};
+
+/* Defines for flags */
+#define CSIO_LNF_FIPSUPP 0x00000001 /* Fip Supported */
+#define CSIO_LNF_NPIVSUPP 0x00000002 /* NPIV supported */
+#define CSIO_LNF_LINK_ENABLE 0x00000004 /* Link enabled */
+#define CSIO_LNF_FDMI_ENABLE 0x00000008 /* FDMI support */
+
+/* Transport events */
+enum csio_ln_fc_evt {
+ CSIO_LN_FC_LINKUP = 1,
+ CSIO_LN_FC_LINKDOWN,
+ CSIO_LN_FC_RSCN,
+ CSIO_LN_FC_ATTRIB_UPDATE,
+};
+
+/* Lnode stats */
+struct csio_lnode_stats {
+ uint32_t n_link_up; /* Link down */
+ uint32_t n_link_down; /* Link up */
+ uint32_t n_err; /* error */
+ uint32_t n_err_nomem; /* memory not available */
+ uint32_t n_inval_parm; /* Invalid parameters */
+ uint32_t n_evt_unexp; /* unexpected event */
+ uint32_t n_evt_drop; /* dropped event */
+ uint32_t n_rnode_match; /* matched rnode */
+ uint32_t n_dev_loss_tmo; /* Device loss timeout */
+ uint32_t n_fdmi_err; /* fdmi err */
+ uint32_t n_evt_fw[RSCN_DEV_LOST]; /* fw events */
+ enum csio_ln_ev n_evt_sm[CSIO_LNE_MAX_EVENT]; /* State m/c events */
+ uint32_t n_rnode_alloc; /* rnode allocated */
+ uint32_t n_rnode_free; /* rnode freed */
+ uint32_t n_rnode_nomem; /* rnode alloc failure */
+ uint32_t n_input_requests; /* Input Requests */
+ uint32_t n_output_requests; /* Output Requests */
+ uint32_t n_control_requests; /* Control Requests */
+ uint32_t n_input_bytes; /* Input Bytes */
+ uint32_t n_output_bytes; /* Output Bytes */
+ uint32_t rsvd1;
+};
+
+/* Common Lnode params */
+struct csio_lnode_params {
+ uint32_t ra_tov;
+ uint32_t fcfi;
+ uint32_t log_level; /* Module level for debugging */
+};
+
+struct csio_service_parms {
+ struct fc_els_csp csp; /* Common service parms */
+ uint8_t wwpn[8]; /* WWPN */
+ uint8_t wwnn[8]; /* WWNN */
+ struct fc_els_cssp clsp[4]; /* Class service params */
+ uint8_t vvl[16]; /* Vendor version level */
+};
+
+/* Lnode */
+struct csio_lnode {
+ struct csio_sm sm; /* State machine + sibling
+ * lnode list.
+ */
+ struct csio_hw *hwp; /* Pointer to the HW module */
+ uint8_t portid; /* Port ID */
+ uint8_t rsvd1;
+ uint16_t rsvd2;
+ uint32_t dev_num; /* Device number */
+ uint32_t flags; /* Flags */
+ struct list_head fcf_lsthead; /* FCF entries */
+ struct csio_fcf_info *fcfinfo; /* FCF in use */
+ struct csio_ioreq *mgmt_req; /* MGMT request */
+
+ /* FCoE identifiers */
+ uint8_t mac[6];
+ uint32_t nport_id;
+ struct csio_service_parms ln_sparm; /* Service parms */
+
+ /* Firmware identifiers */
+ uint32_t fcf_flowid; /*fcf flowid */
+ uint32_t vnp_flowid;
+ uint16_t ssn_cnt; /* Registered Session */
+ uint8_t cur_evt; /* Current event */
+ uint8_t prev_evt; /* Previous event */
+
+ /* Children */
+ struct list_head cln_head; /* Head of the children lnode
+ * list.
+ */
+ uint32_t num_vports; /* Total NPIV/children LNodes*/
+ struct csio_lnode *pln; /* Parent lnode of child
+ * lnodes.
+ */
+ struct list_head cmpl_q; /* Pending I/Os on this lnode */
+
+ /* Remote node information */
+ struct list_head rnhead; /* Head of rnode list */
+ uint32_t num_reg_rnodes; /* Number of rnodes registered
+ * with the host.
+ */
+ uint32_t n_scsi_tgts; /* Number of scsi targets
+ * found
+ */
+ uint32_t last_scan_ntgts;/* Number of scsi targets
+ * found per last scan.
+ */
+ uint32_t tgt_scan_tick; /* timer started after
+ * new tgt found
+ */
+ /* FC transport data */
+ struct fc_vport *fc_vport;
+ struct fc_host_statistics fch_stats;
+
+ struct csio_lnode_stats stats; /* Common lnode stats */
+ struct csio_lnode_params params; /* Common lnode params */
+};
+
+#define csio_lnode_to_hw(ln) ((ln)->hwp)
+#define csio_root_lnode(ln) (csio_lnode_to_hw((ln))->rln)
+#define csio_parent_lnode(ln) ((ln)->pln)
+#define csio_ln_flowid(ln) ((ln)->vnp_flowid)
+#define csio_ln_wwpn(ln) ((ln)->ln_sparm.wwpn)
+#define csio_ln_wwnn(ln) ((ln)->ln_sparm.wwnn)
+
+#define csio_is_root_ln(ln) (((ln) == csio_root_lnode((ln))) ? 1 : 0)
+#define csio_is_phys_ln(ln) (((ln)->pln == NULL) ? 1 : 0)
+#define csio_is_npiv_ln(ln) (((ln)->pln != NULL) ? 1 : 0)
+
+
+#define csio_ln_dbg(_ln, _fmt, ...) \
+ csio_dbg(_ln->hwp, "%x:%x "_fmt, CSIO_DEVID_HI(_ln), \
+ CSIO_DEVID_LO(_ln), ##__VA_ARGS__);
+
+#define csio_ln_err(_ln, _fmt, ...) \
+ csio_err(_ln->hwp, "%x:%x "_fmt, CSIO_DEVID_HI(_ln), \
+ CSIO_DEVID_LO(_ln), ##__VA_ARGS__);
+
+#define csio_ln_warn(_ln, _fmt, ...) \
+ csio_warn(_ln->hwp, "%x:%x "_fmt, CSIO_DEVID_HI(_ln), \
+ CSIO_DEVID_LO(_ln), ##__VA_ARGS__);
+
+/* HW->Lnode notifications */
+enum csio_ln_notify {
+ CSIO_LN_NOTIFY_HWREADY = 1,
+ CSIO_LN_NOTIFY_HWSTOP,
+ CSIO_LN_NOTIFY_HWREMOVE,
+ CSIO_LN_NOTIFY_HWRESET,
+};
+
+void csio_fcoe_fwevt_handler(struct csio_hw *, __u8 cpl_op, __be64 *);
+int csio_is_lnode_ready(struct csio_lnode *);
+void csio_lnode_state_to_str(struct csio_lnode *ln, int8_t *str);
+struct csio_lnode *csio_lnode_lookup_by_wwpn(struct csio_hw *, uint8_t *);
+int csio_get_phy_port_stats(struct csio_hw *, uint8_t ,
+ struct fw_fcoe_port_stats *);
+int csio_scan_done(struct csio_lnode *, unsigned long, unsigned long,
+ unsigned long, unsigned long);
+void csio_notify_lnodes(struct csio_hw *, enum csio_ln_notify);
+void csio_disable_lnodes(struct csio_hw *, uint8_t, bool);
+void csio_lnode_async_event(struct csio_lnode *, enum csio_ln_fc_evt);
+int csio_ln_fdmi_start(struct csio_lnode *, void *);
+int csio_lnode_start(struct csio_lnode *);
+void csio_lnode_stop(struct csio_lnode *);
+void csio_lnode_close(struct csio_lnode *);
+int csio_lnode_init(struct csio_lnode *, struct csio_hw *,
+ struct csio_lnode *);
+void csio_lnode_exit(struct csio_lnode *);
+
+#endif /* ifndef __CSIO_LNODE_H__ */
diff --git a/drivers/scsi/csiostor/csio_mb.c b/drivers/scsi/csiostor/csio_mb.c
new file mode 100644
index 00000000000..5b27c48f683
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_mb.c
@@ -0,0 +1,1750 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/string.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include "csio_hw.h"
+#include "csio_lnode.h"
+#include "csio_rnode.h"
+#include "csio_mb.h"
+#include "csio_wr.h"
+
+#define csio_mb_is_host_owner(__owner) ((__owner) == CSIO_MBOWNER_PL)
+
+/* MB Command/Response Helpers */
+/*
+ * csio_mb_fw_retval - FW return value from a mailbox response.
+ * @mbp: Mailbox structure
+ *
+ */
+enum fw_retval
+csio_mb_fw_retval(struct csio_mb *mbp)
+{
+ struct fw_cmd_hdr *hdr;
+
+ hdr = (struct fw_cmd_hdr *)(mbp->mb);
+
+ return FW_CMD_RETVAL_GET(ntohl(hdr->lo));
+}
+
+/*
+ * csio_mb_hello - FW HELLO command helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @m_mbox: Master mailbox number, if any.
+ * @a_mbox: Mailbox number for asycn notifications.
+ * @master: Device mastership.
+ * @cbfn: Callback, if any.
+ *
+ */
+void
+csio_mb_hello(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
+ uint32_t m_mbox, uint32_t a_mbox, enum csio_dev_master master,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_hello_cmd *cmdp = (struct fw_hello_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, tmo, hw, cbfn, 1);
+
+ cmdp->op_to_write = htonl(FW_CMD_OP(FW_HELLO_CMD) |
+ FW_CMD_REQUEST | FW_CMD_WRITE);
+ cmdp->retval_len16 = htonl(FW_CMD_LEN16(sizeof(*cmdp) / 16));
+ cmdp->err_to_clearinit = htonl(
+ FW_HELLO_CMD_MASTERDIS(master == CSIO_MASTER_CANT) |
+ FW_HELLO_CMD_MASTERFORCE(master == CSIO_MASTER_MUST) |
+ FW_HELLO_CMD_MBMASTER(master == CSIO_MASTER_MUST ?
+ m_mbox : FW_HELLO_CMD_MBMASTER_MASK) |
+ FW_HELLO_CMD_MBASYNCNOT(a_mbox) |
+ FW_HELLO_CMD_STAGE(fw_hello_cmd_stage_os) |
+ FW_HELLO_CMD_CLEARINIT);
+
+}
+
+/*
+ * csio_mb_process_hello_rsp - FW HELLO response processing helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @retval: Mailbox return value from Firmware
+ * @state: State that the function is in.
+ * @mpfn: Master pfn
+ *
+ */
+void
+csio_mb_process_hello_rsp(struct csio_hw *hw, struct csio_mb *mbp,
+ enum fw_retval *retval, enum csio_dev_state *state,
+ uint8_t *mpfn)
+{
+ struct fw_hello_cmd *rsp = (struct fw_hello_cmd *)(mbp->mb);
+ uint32_t value;
+
+ *retval = FW_CMD_RETVAL_GET(ntohl(rsp->retval_len16));
+
+ if (*retval == FW_SUCCESS) {
+ hw->fwrev = ntohl(rsp->fwrev);
+
+ value = ntohl(rsp->err_to_clearinit);
+ *mpfn = FW_HELLO_CMD_MBMASTER_GET(value);
+
+ if (value & FW_HELLO_CMD_INIT)
+ *state = CSIO_DEV_STATE_INIT;
+ else if (value & FW_HELLO_CMD_ERR)
+ *state = CSIO_DEV_STATE_ERR;
+ else
+ *state = CSIO_DEV_STATE_UNINIT;
+ }
+}
+
+/*
+ * csio_mb_bye - FW BYE command helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @cbfn: Callback, if any.
+ *
+ */
+void
+csio_mb_bye(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_bye_cmd *cmdp = (struct fw_bye_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, tmo, hw, cbfn, 1);
+
+ cmdp->op_to_write = htonl(FW_CMD_OP(FW_BYE_CMD) |
+ FW_CMD_REQUEST | FW_CMD_WRITE);
+ cmdp->retval_len16 = htonl(FW_CMD_LEN16(sizeof(*cmdp) / 16));
+
+}
+
+/*
+ * csio_mb_reset - FW RESET command helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @reset: Type of reset.
+ * @cbfn: Callback, if any.
+ *
+ */
+void
+csio_mb_reset(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
+ int reset, int halt,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_reset_cmd *cmdp = (struct fw_reset_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, tmo, hw, cbfn, 1);
+
+ cmdp->op_to_write = htonl(FW_CMD_OP(FW_RESET_CMD) |
+ FW_CMD_REQUEST | FW_CMD_WRITE);
+ cmdp->retval_len16 = htonl(FW_CMD_LEN16(sizeof(*cmdp) / 16));
+ cmdp->val = htonl(reset);
+ cmdp->halt_pkd = htonl(halt);
+
+}
+
+/*
+ * csio_mb_params - FW PARAMS command helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @tmo: Command timeout.
+ * @pf: PF number.
+ * @vf: VF number.
+ * @nparams: Number of paramters
+ * @params: Parameter mnemonic array.
+ * @val: Parameter value array.
+ * @wr: Write/Read PARAMS.
+ * @cbfn: Callback, if any.
+ *
+ */
+void
+csio_mb_params(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
+ unsigned int pf, unsigned int vf, unsigned int nparams,
+ const u32 *params, u32 *val, bool wr,
+ void (*cbfn)(struct csio_hw *, struct csio_mb *))
+{
+ uint32_t i;
+ uint32_t temp_params = 0, temp_val = 0;
+ struct fw_params_cmd *cmdp = (struct fw_params_cmd *)(mbp->mb);
+ __be32 *p = &cmdp->param[0].mnem;
+
+ CSIO_INIT_MBP(mbp, cmdp, tmo, hw, cbfn, 1);
+
+ cmdp->op_to_vfn = htonl(FW_CMD_OP(FW_PARAMS_CMD) |
+ FW_CMD_REQUEST |
+ (wr ? FW_CMD_WRITE : FW_CMD_READ) |
+ FW_PARAMS_CMD_PFN(pf) |
+ FW_PARAMS_CMD_VFN(vf));
+ cmdp->retval_len16 = htonl(FW_CMD_LEN16(sizeof(*cmdp) / 16));
+
+ /* Write Params */
+ if (wr) {
+ while (nparams--) {
+ temp_params = *params++;
+ temp_val = *val++;
+
+ *p++ = htonl(temp_params);
+ *p++ = htonl(temp_val);
+ }
+ } else {
+ for (i = 0; i < nparams; i++, p += 2) {
+ temp_params = *params++;
+ *p = htonl(temp_params);
+ }
+ }
+
+}
+
+/*
+ * csio_mb_process_read_params_rsp - FW PARAMS response processing helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @retval: Mailbox return value from Firmware
+ * @nparams: Number of parameters
+ * @val: Parameter value array.
+ *
+ */
+void
+csio_mb_process_read_params_rsp(struct csio_hw *hw, struct csio_mb *mbp,
+ enum fw_retval *retval, unsigned int nparams,
+ u32 *val)
+{
+ struct fw_params_cmd *rsp = (struct fw_params_cmd *)(mbp->mb);
+ uint32_t i;
+ __be32 *p = &rsp->param[0].val;
+
+ *retval = FW_CMD_RETVAL_GET(ntohl(rsp->retval_len16));
+
+ if (*retval == FW_SUCCESS)
+ for (i = 0; i < nparams; i++, p += 2)
+ *val++ = ntohl(*p);
+}
+
+/*
+ * csio_mb_ldst - FW LDST command
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @tmo: timeout
+ * @reg: register
+ *
+ */
+void
+csio_mb_ldst(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo, int reg)
+{
+ struct fw_ldst_cmd *ldst_cmd = (struct fw_ldst_cmd *)(mbp->mb);
+ CSIO_INIT_MBP(mbp, ldst_cmd, tmo, hw, NULL, 1);
+
+ /*
+ * Construct and send the Firmware LDST Command to retrieve the
+ * specified PCI-E Configuration Space register.
+ */
+ ldst_cmd->op_to_addrspace =
+ htonl(FW_CMD_OP(FW_LDST_CMD) |
+ FW_CMD_REQUEST |
+ FW_CMD_READ |
+ FW_LDST_CMD_ADDRSPACE(FW_LDST_ADDRSPC_FUNC_PCIE));
+ ldst_cmd->cycles_to_len16 = htonl(FW_LEN16(struct fw_ldst_cmd));
+ ldst_cmd->u.pcie.select_naccess = FW_LDST_CMD_NACCESS(1);
+ ldst_cmd->u.pcie.ctrl_to_fn =
+ (FW_LDST_CMD_LC | FW_LDST_CMD_FN(hw->pfn));
+ ldst_cmd->u.pcie.r = (uint8_t)reg;
+}
+
+/*
+ *
+ * csio_mb_caps_config - FW Read/Write Capabilities command helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @wr: Write if 1, Read if 0
+ * @init: Turn on initiator mode.
+ * @tgt: Turn on target mode.
+ * @cofld: If 1, Control Offload for FCoE
+ * @cbfn: Callback, if any.
+ *
+ * This helper assumes that cmdp has MB payload from a previous CAPS
+ * read command.
+ */
+void
+csio_mb_caps_config(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
+ bool wr, bool init, bool tgt, bool cofld,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_caps_config_cmd *cmdp =
+ (struct fw_caps_config_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, tmo, hw, cbfn, wr ? 0 : 1);
+
+ cmdp->op_to_write = htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) |
+ FW_CMD_REQUEST |
+ (wr ? FW_CMD_WRITE : FW_CMD_READ));
+ cmdp->cfvalid_to_len16 = htonl(FW_CMD_LEN16(sizeof(*cmdp) / 16));
+
+ /* Read config */
+ if (!wr)
+ return;
+
+ /* Write config */
+ cmdp->fcoecaps = 0;
+
+ if (cofld)
+ cmdp->fcoecaps |= htons(FW_CAPS_CONFIG_FCOE_CTRL_OFLD);
+ if (init)
+ cmdp->fcoecaps |= htons(FW_CAPS_CONFIG_FCOE_INITIATOR);
+ if (tgt)
+ cmdp->fcoecaps |= htons(FW_CAPS_CONFIG_FCOE_TARGET);
+}
+
+void
+csio_rss_glb_config(struct csio_hw *hw, struct csio_mb *mbp,
+ uint32_t tmo, uint8_t mode, unsigned int flags,
+ void (*cbfn)(struct csio_hw *, struct csio_mb *))
+{
+ struct fw_rss_glb_config_cmd *cmdp =
+ (struct fw_rss_glb_config_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, tmo, hw, cbfn, 1);
+
+ cmdp->op_to_write = htonl(FW_CMD_OP(FW_RSS_GLB_CONFIG_CMD) |
+ FW_CMD_REQUEST | FW_CMD_WRITE);
+ cmdp->retval_len16 = htonl(FW_CMD_LEN16(sizeof(*cmdp) / 16));
+
+ if (mode == FW_RSS_GLB_CONFIG_CMD_MODE_MANUAL) {
+ cmdp->u.manual.mode_pkd =
+ htonl(FW_RSS_GLB_CONFIG_CMD_MODE(mode));
+ } else if (mode == FW_RSS_GLB_CONFIG_CMD_MODE_BASICVIRTUAL) {
+ cmdp->u.basicvirtual.mode_pkd =
+ htonl(FW_RSS_GLB_CONFIG_CMD_MODE(mode));
+ cmdp->u.basicvirtual.synmapen_to_hashtoeplitz = htonl(flags);
+ }
+}
+
+
+/*
+ * csio_mb_pfvf - FW Write PF/VF capabilities command helper.
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @pf:
+ * @vf:
+ * @txq:
+ * @txq_eht_ctrl:
+ * @rxqi:
+ * @rxq:
+ * @tc:
+ * @vi:
+ * @pmask:
+ * @rcaps:
+ * @wxcaps:
+ * @cbfn: Callback, if any.
+ *
+ */
+void
+csio_mb_pfvf(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
+ unsigned int pf, unsigned int vf, unsigned int txq,
+ unsigned int txq_eth_ctrl, unsigned int rxqi,
+ unsigned int rxq, unsigned int tc, unsigned int vi,
+ unsigned int cmask, unsigned int pmask, unsigned int nexactf,
+ unsigned int rcaps, unsigned int wxcaps,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_pfvf_cmd *cmdp = (struct fw_pfvf_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, tmo, hw, cbfn, 1);
+
+ cmdp->op_to_vfn = htonl(FW_CMD_OP(FW_PFVF_CMD) |
+ FW_CMD_REQUEST |
+ FW_CMD_WRITE |
+ FW_PFVF_CMD_PFN(pf) |
+ FW_PFVF_CMD_VFN(vf));
+ cmdp->retval_len16 = htonl(FW_CMD_LEN16(sizeof(*cmdp) / 16));
+ cmdp->niqflint_niq = htonl(FW_PFVF_CMD_NIQFLINT(rxqi) |
+ FW_PFVF_CMD_NIQ(rxq));
+
+ cmdp->type_to_neq = htonl(FW_PFVF_CMD_TYPE |
+ FW_PFVF_CMD_CMASK(cmask) |
+ FW_PFVF_CMD_PMASK(pmask) |
+ FW_PFVF_CMD_NEQ(txq));
+ cmdp->tc_to_nexactf = htonl(FW_PFVF_CMD_TC(tc) |
+ FW_PFVF_CMD_NVI(vi) |
+ FW_PFVF_CMD_NEXACTF(nexactf));
+ cmdp->r_caps_to_nethctrl = htonl(FW_PFVF_CMD_R_CAPS(rcaps) |
+ FW_PFVF_CMD_WX_CAPS(wxcaps) |
+ FW_PFVF_CMD_NETHCTRL(txq_eth_ctrl));
+}
+
+#define CSIO_ADVERT_MASK (FW_PORT_CAP_SPEED_100M | FW_PORT_CAP_SPEED_1G |\
+ FW_PORT_CAP_SPEED_10G | FW_PORT_CAP_ANEG)
+
+/*
+ * csio_mb_port- FW PORT command helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @tmo: COmmand timeout
+ * @portid: Port ID to get/set info
+ * @wr: Write/Read PORT information.
+ * @fc: Flow control
+ * @caps: Port capabilites to set.
+ * @cbfn: Callback, if any.
+ *
+ */
+void
+csio_mb_port(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
+ uint8_t portid, bool wr, uint32_t fc, uint16_t caps,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_port_cmd *cmdp = (struct fw_port_cmd *)(mbp->mb);
+ unsigned int lfc = 0, mdi = FW_PORT_MDI(FW_PORT_MDI_AUTO);
+
+ CSIO_INIT_MBP(mbp, cmdp, tmo, hw, cbfn, 1);
+
+ cmdp->op_to_portid = htonl(FW_CMD_OP(FW_PORT_CMD) |
+ FW_CMD_REQUEST |
+ (wr ? FW_CMD_EXEC : FW_CMD_READ) |
+ FW_PORT_CMD_PORTID(portid));
+ if (!wr) {
+ cmdp->action_to_len16 = htonl(
+ FW_PORT_CMD_ACTION(FW_PORT_ACTION_GET_PORT_INFO) |
+ FW_CMD_LEN16(sizeof(*cmdp) / 16));
+ return;
+ }
+
+ /* Set port */
+ cmdp->action_to_len16 = htonl(
+ FW_PORT_CMD_ACTION(FW_PORT_ACTION_L1_CFG) |
+ FW_CMD_LEN16(sizeof(*cmdp) / 16));
+
+ if (fc & PAUSE_RX)
+ lfc |= FW_PORT_CAP_FC_RX;
+ if (fc & PAUSE_TX)
+ lfc |= FW_PORT_CAP_FC_TX;
+
+ if (!(caps & FW_PORT_CAP_ANEG))
+ cmdp->u.l1cfg.rcap = htonl((caps & CSIO_ADVERT_MASK) | lfc);
+ else
+ cmdp->u.l1cfg.rcap = htonl((caps & CSIO_ADVERT_MASK) |
+ lfc | mdi);
+}
+
+/*
+ * csio_mb_process_read_port_rsp - FW PORT command response processing helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @retval: Mailbox return value from Firmware
+ * @caps: port capabilities
+ *
+ */
+void
+csio_mb_process_read_port_rsp(struct csio_hw *hw, struct csio_mb *mbp,
+ enum fw_retval *retval, uint16_t *caps)
+{
+ struct fw_port_cmd *rsp = (struct fw_port_cmd *)(mbp->mb);
+
+ *retval = FW_CMD_RETVAL_GET(ntohl(rsp->action_to_len16));
+
+ if (*retval == FW_SUCCESS)
+ *caps = ntohs(rsp->u.info.pcap);
+}
+
+/*
+ * csio_mb_initialize - FW INITIALIZE command helper
+ * @hw: The HW structure
+ * @mbp: Mailbox structure
+ * @tmo: COmmand timeout
+ * @cbfn: Callback, if any.
+ *
+ */
+void
+csio_mb_initialize(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_initialize_cmd *cmdp = (struct fw_initialize_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, tmo, hw, cbfn, 1);
+
+ cmdp->op_to_write = htonl(FW_CMD_OP(FW_INITIALIZE_CMD) |
+ FW_CMD_REQUEST | FW_CMD_WRITE);
+ cmdp->retval_len16 = htonl(FW_CMD_LEN16(sizeof(*cmdp) / 16));
+
+}
+
+/*
+ * csio_mb_iq_alloc - Initializes the mailbox to allocate an
+ * Ingress DMA queue in the firmware.
+ *
+ * @hw: The hw structure
+ * @mbp: Mailbox structure to initialize
+ * @priv: Private object
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @iq_params: Ingress queue params needed for allocation.
+ * @cbfn: The call-back function
+ *
+ *
+ */
+static void
+csio_mb_iq_alloc(struct csio_hw *hw, struct csio_mb *mbp, void *priv,
+ uint32_t mb_tmo, struct csio_iq_params *iq_params,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_iq_cmd *cmdp = (struct fw_iq_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, priv, cbfn, 1);
+
+ cmdp->op_to_vfn = htonl(FW_CMD_OP(FW_IQ_CMD) |
+ FW_CMD_REQUEST | FW_CMD_EXEC |
+ FW_IQ_CMD_PFN(iq_params->pfn) |
+ FW_IQ_CMD_VFN(iq_params->vfn));
+
+ cmdp->alloc_to_len16 = htonl(FW_IQ_CMD_ALLOC |
+ FW_CMD_LEN16(sizeof(*cmdp) / 16));
+
+ cmdp->type_to_iqandstindex = htonl(
+ FW_IQ_CMD_VIID(iq_params->viid) |
+ FW_IQ_CMD_TYPE(iq_params->type) |
+ FW_IQ_CMD_IQASYNCH(iq_params->iqasynch));
+
+ cmdp->fl0size = htons(iq_params->fl0size);
+ cmdp->fl0size = htons(iq_params->fl1size);
+
+} /* csio_mb_iq_alloc */
+
+/*
+ * csio_mb_iq_write - Initializes the mailbox for writing into an
+ * Ingress DMA Queue.
+ *
+ * @hw: The HW structure
+ * @mbp: Mailbox structure to initialize
+ * @priv: Private object
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @cascaded_req: TRUE - if this request is cascased with iq-alloc request.
+ * @iq_params: Ingress queue params needed for writing.
+ * @cbfn: The call-back function
+ *
+ * NOTE: We OR relevant bits with cmdp->XXX, instead of just equating,
+ * because this IQ write request can be cascaded with a previous
+ * IQ alloc request, and we dont want to over-write the bits set by
+ * that request. This logic will work even in a non-cascaded case, since the
+ * cmdp structure is zeroed out by CSIO_INIT_MBP.
+ */
+static void
+csio_mb_iq_write(struct csio_hw *hw, struct csio_mb *mbp, void *priv,
+ uint32_t mb_tmo, bool cascaded_req,
+ struct csio_iq_params *iq_params,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_iq_cmd *cmdp = (struct fw_iq_cmd *)(mbp->mb);
+
+ uint32_t iq_start_stop = (iq_params->iq_start) ?
+ FW_IQ_CMD_IQSTART(1) :
+ FW_IQ_CMD_IQSTOP(1);
+
+ /*
+ * If this IQ write is cascaded with IQ alloc request, do not
+ * re-initialize with 0's.
+ *
+ */
+ if (!cascaded_req)
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, priv, cbfn, 1);
+
+ cmdp->op_to_vfn |= htonl(FW_CMD_OP(FW_IQ_CMD) |
+ FW_CMD_REQUEST | FW_CMD_WRITE |
+ FW_IQ_CMD_PFN(iq_params->pfn) |
+ FW_IQ_CMD_VFN(iq_params->vfn));
+ cmdp->alloc_to_len16 |= htonl(iq_start_stop |
+ FW_CMD_LEN16(sizeof(*cmdp) / 16));
+ cmdp->iqid |= htons(iq_params->iqid);
+ cmdp->fl0id |= htons(iq_params->fl0id);
+ cmdp->fl1id |= htons(iq_params->fl1id);
+ cmdp->type_to_iqandstindex |= htonl(
+ FW_IQ_CMD_IQANDST(iq_params->iqandst) |
+ FW_IQ_CMD_IQANUS(iq_params->iqanus) |
+ FW_IQ_CMD_IQANUD(iq_params->iqanud) |
+ FW_IQ_CMD_IQANDSTINDEX(iq_params->iqandstindex));
+ cmdp->iqdroprss_to_iqesize |= htons(
+ FW_IQ_CMD_IQPCIECH(iq_params->iqpciech) |
+ FW_IQ_CMD_IQDCAEN(iq_params->iqdcaen) |
+ FW_IQ_CMD_IQDCACPU(iq_params->iqdcacpu) |
+ FW_IQ_CMD_IQINTCNTTHRESH(iq_params->iqintcntthresh) |
+ FW_IQ_CMD_IQCPRIO(iq_params->iqcprio) |
+ FW_IQ_CMD_IQESIZE(iq_params->iqesize));
+
+ cmdp->iqsize |= htons(iq_params->iqsize);
+ cmdp->iqaddr |= cpu_to_be64(iq_params->iqaddr);
+
+ if (iq_params->type == 0) {
+ cmdp->iqns_to_fl0congen |= htonl(
+ FW_IQ_CMD_IQFLINTIQHSEN(iq_params->iqflintiqhsen)|
+ FW_IQ_CMD_IQFLINTCONGEN(iq_params->iqflintcongen));
+ }
+
+ if (iq_params->fl0size && iq_params->fl0addr &&
+ (iq_params->fl0id != 0xFFFF)) {
+
+ cmdp->iqns_to_fl0congen |= htonl(
+ FW_IQ_CMD_FL0HOSTFCMODE(iq_params->fl0hostfcmode)|
+ FW_IQ_CMD_FL0CPRIO(iq_params->fl0cprio) |
+ FW_IQ_CMD_FL0PADEN(iq_params->fl0paden) |
+ FW_IQ_CMD_FL0PACKEN(iq_params->fl0packen));
+ cmdp->fl0dcaen_to_fl0cidxfthresh |= htons(
+ FW_IQ_CMD_FL0DCAEN(iq_params->fl0dcaen) |
+ FW_IQ_CMD_FL0DCACPU(iq_params->fl0dcacpu) |
+ FW_IQ_CMD_FL0FBMIN(iq_params->fl0fbmin) |
+ FW_IQ_CMD_FL0FBMAX(iq_params->fl0fbmax) |
+ FW_IQ_CMD_FL0CIDXFTHRESH(iq_params->fl0cidxfthresh));
+ cmdp->fl0size |= htons(iq_params->fl0size);
+ cmdp->fl0addr |= cpu_to_be64(iq_params->fl0addr);
+ }
+} /* csio_mb_iq_write */
+
+/*
+ * csio_mb_iq_alloc_write - Initializes the mailbox for allocating an
+ * Ingress DMA Queue.
+ *
+ * @hw: The HW structure
+ * @mbp: Mailbox structure to initialize
+ * @priv: Private data.
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @iq_params: Ingress queue params needed for allocation & writing.
+ * @cbfn: The call-back function
+ *
+ *
+ */
+void
+csio_mb_iq_alloc_write(struct csio_hw *hw, struct csio_mb *mbp, void *priv,
+ uint32_t mb_tmo, struct csio_iq_params *iq_params,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ csio_mb_iq_alloc(hw, mbp, priv, mb_tmo, iq_params, cbfn);
+ csio_mb_iq_write(hw, mbp, priv, mb_tmo, true, iq_params, cbfn);
+} /* csio_mb_iq_alloc_write */
+
+/*
+ * csio_mb_iq_alloc_write_rsp - Process the allocation & writing
+ * of ingress DMA queue mailbox's response.
+ *
+ * @hw: The HW structure.
+ * @mbp: Mailbox structure to initialize.
+ * @retval: Firmware return value.
+ * @iq_params: Ingress queue parameters, after allocation and write.
+ *
+ */
+void
+csio_mb_iq_alloc_write_rsp(struct csio_hw *hw, struct csio_mb *mbp,
+ enum fw_retval *ret_val,
+ struct csio_iq_params *iq_params)
+{
+ struct fw_iq_cmd *rsp = (struct fw_iq_cmd *)(mbp->mb);
+
+ *ret_val = FW_CMD_RETVAL_GET(ntohl(rsp->alloc_to_len16));
+ if (*ret_val == FW_SUCCESS) {
+ iq_params->physiqid = ntohs(rsp->physiqid);
+ iq_params->iqid = ntohs(rsp->iqid);
+ iq_params->fl0id = ntohs(rsp->fl0id);
+ iq_params->fl1id = ntohs(rsp->fl1id);
+ } else {
+ iq_params->physiqid = iq_params->iqid =
+ iq_params->fl0id = iq_params->fl1id = 0;
+ }
+} /* csio_mb_iq_alloc_write_rsp */
+
+/*
+ * csio_mb_iq_free - Initializes the mailbox for freeing a
+ * specified Ingress DMA Queue.
+ *
+ * @hw: The HW structure
+ * @mbp: Mailbox structure to initialize
+ * @priv: Private data
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @iq_params: Parameters of ingress queue, that is to be freed.
+ * @cbfn: The call-back function
+ *
+ *
+ */
+void
+csio_mb_iq_free(struct csio_hw *hw, struct csio_mb *mbp, void *priv,
+ uint32_t mb_tmo, struct csio_iq_params *iq_params,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_iq_cmd *cmdp = (struct fw_iq_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, priv, cbfn, 1);
+
+ cmdp->op_to_vfn = htonl(FW_CMD_OP(FW_IQ_CMD) |
+ FW_CMD_REQUEST | FW_CMD_EXEC |
+ FW_IQ_CMD_PFN(iq_params->pfn) |
+ FW_IQ_CMD_VFN(iq_params->vfn));
+ cmdp->alloc_to_len16 = htonl(FW_IQ_CMD_FREE |
+ FW_CMD_LEN16(sizeof(*cmdp) / 16));
+ cmdp->type_to_iqandstindex = htonl(FW_IQ_CMD_TYPE(iq_params->type));
+
+ cmdp->iqid = htons(iq_params->iqid);
+ cmdp->fl0id = htons(iq_params->fl0id);
+ cmdp->fl1id = htons(iq_params->fl1id);
+
+} /* csio_mb_iq_free */
+
+/*
+ * csio_mb_eq_ofld_alloc - Initializes the mailbox for allocating
+ * an offload-egress queue.
+ *
+ * @hw: The HW structure
+ * @mbp: Mailbox structure to initialize
+ * @priv: Private data
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @eq_ofld_params: (Offload) Egress queue paramters.
+ * @cbfn: The call-back function
+ *
+ *
+ */
+static void
+csio_mb_eq_ofld_alloc(struct csio_hw *hw, struct csio_mb *mbp, void *priv,
+ uint32_t mb_tmo, struct csio_eq_params *eq_ofld_params,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_eq_ofld_cmd *cmdp = (struct fw_eq_ofld_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, priv, cbfn, 1);
+ cmdp->op_to_vfn = htonl(FW_CMD_OP(FW_EQ_OFLD_CMD) |
+ FW_CMD_REQUEST | FW_CMD_EXEC |
+ FW_EQ_OFLD_CMD_PFN(eq_ofld_params->pfn) |
+ FW_EQ_OFLD_CMD_VFN(eq_ofld_params->vfn));
+ cmdp->alloc_to_len16 = htonl(FW_EQ_OFLD_CMD_ALLOC |
+ FW_CMD_LEN16(sizeof(*cmdp) / 16));
+
+} /* csio_mb_eq_ofld_alloc */
+
+/*
+ * csio_mb_eq_ofld_write - Initializes the mailbox for writing
+ * an alloacted offload-egress queue.
+ *
+ * @hw: The HW structure
+ * @mbp: Mailbox structure to initialize
+ * @priv: Private data
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @cascaded_req: TRUE - if this request is cascased with Eq-alloc request.
+ * @eq_ofld_params: (Offload) Egress queue paramters.
+ * @cbfn: The call-back function
+ *
+ *
+ * NOTE: We OR relevant bits with cmdp->XXX, instead of just equating,
+ * because this EQ write request can be cascaded with a previous
+ * EQ alloc request, and we dont want to over-write the bits set by
+ * that request. This logic will work even in a non-cascaded case, since the
+ * cmdp structure is zeroed out by CSIO_INIT_MBP.
+ */
+static void
+csio_mb_eq_ofld_write(struct csio_hw *hw, struct csio_mb *mbp, void *priv,
+ uint32_t mb_tmo, bool cascaded_req,
+ struct csio_eq_params *eq_ofld_params,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_eq_ofld_cmd *cmdp = (struct fw_eq_ofld_cmd *)(mbp->mb);
+
+ uint32_t eq_start_stop = (eq_ofld_params->eqstart) ?
+ FW_EQ_OFLD_CMD_EQSTART : FW_EQ_OFLD_CMD_EQSTOP;
+
+ /*
+ * If this EQ write is cascaded with EQ alloc request, do not
+ * re-initialize with 0's.
+ *
+ */
+ if (!cascaded_req)
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, priv, cbfn, 1);
+
+ cmdp->op_to_vfn |= htonl(FW_CMD_OP(FW_EQ_OFLD_CMD) |
+ FW_CMD_REQUEST | FW_CMD_WRITE |
+ FW_EQ_OFLD_CMD_PFN(eq_ofld_params->pfn) |
+ FW_EQ_OFLD_CMD_VFN(eq_ofld_params->vfn));
+ cmdp->alloc_to_len16 |= htonl(eq_start_stop |
+ FW_CMD_LEN16(sizeof(*cmdp) / 16));
+
+ cmdp->eqid_pkd |= htonl(FW_EQ_OFLD_CMD_EQID(eq_ofld_params->eqid));
+
+ cmdp->fetchszm_to_iqid |= htonl(
+ FW_EQ_OFLD_CMD_HOSTFCMODE(eq_ofld_params->hostfcmode) |
+ FW_EQ_OFLD_CMD_CPRIO(eq_ofld_params->cprio) |
+ FW_EQ_OFLD_CMD_PCIECHN(eq_ofld_params->pciechn) |
+ FW_EQ_OFLD_CMD_IQID(eq_ofld_params->iqid));
+
+ cmdp->dcaen_to_eqsize |= htonl(
+ FW_EQ_OFLD_CMD_DCAEN(eq_ofld_params->dcaen) |
+ FW_EQ_OFLD_CMD_DCACPU(eq_ofld_params->dcacpu) |
+ FW_EQ_OFLD_CMD_FBMIN(eq_ofld_params->fbmin) |
+ FW_EQ_OFLD_CMD_FBMAX(eq_ofld_params->fbmax) |
+ FW_EQ_OFLD_CMD_CIDXFTHRESHO(eq_ofld_params->cidxfthresho) |
+ FW_EQ_OFLD_CMD_CIDXFTHRESH(eq_ofld_params->cidxfthresh) |
+ FW_EQ_OFLD_CMD_EQSIZE(eq_ofld_params->eqsize));
+
+ cmdp->eqaddr |= cpu_to_be64(eq_ofld_params->eqaddr);
+
+} /* csio_mb_eq_ofld_write */
+
+/*
+ * csio_mb_eq_ofld_alloc_write - Initializes the mailbox for allocation
+ * writing into an Engress DMA Queue.
+ *
+ * @hw: The HW structure
+ * @mbp: Mailbox structure to initialize
+ * @priv: Private data.
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @eq_ofld_params: (Offload) Egress queue paramters.
+ * @cbfn: The call-back function
+ *
+ *
+ */
+void
+csio_mb_eq_ofld_alloc_write(struct csio_hw *hw, struct csio_mb *mbp,
+ void *priv, uint32_t mb_tmo,
+ struct csio_eq_params *eq_ofld_params,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ csio_mb_eq_ofld_alloc(hw, mbp, priv, mb_tmo, eq_ofld_params, cbfn);
+ csio_mb_eq_ofld_write(hw, mbp, priv, mb_tmo, true,
+ eq_ofld_params, cbfn);
+} /* csio_mb_eq_ofld_alloc_write */
+
+/*
+ * csio_mb_eq_ofld_alloc_write_rsp - Process the allocation
+ * & write egress DMA queue mailbox's response.
+ *
+ * @hw: The HW structure.
+ * @mbp: Mailbox structure to initialize.
+ * @retval: Firmware return value.
+ * @eq_ofld_params: (Offload) Egress queue paramters.
+ *
+ */
+void
+csio_mb_eq_ofld_alloc_write_rsp(struct csio_hw *hw,
+ struct csio_mb *mbp, enum fw_retval *ret_val,
+ struct csio_eq_params *eq_ofld_params)
+{
+ struct fw_eq_ofld_cmd *rsp = (struct fw_eq_ofld_cmd *)(mbp->mb);
+
+ *ret_val = FW_CMD_RETVAL_GET(ntohl(rsp->alloc_to_len16));
+
+ if (*ret_val == FW_SUCCESS) {
+ eq_ofld_params->eqid = FW_EQ_OFLD_CMD_EQID_GET(
+ ntohl(rsp->eqid_pkd));
+ eq_ofld_params->physeqid = FW_EQ_OFLD_CMD_PHYSEQID_GET(
+ ntohl(rsp->physeqid_pkd));
+ } else
+ eq_ofld_params->eqid = 0;
+
+} /* csio_mb_eq_ofld_alloc_write_rsp */
+
+/*
+ * csio_mb_eq_ofld_free - Initializes the mailbox for freeing a
+ * specified Engress DMA Queue.
+ *
+ * @hw: The HW structure
+ * @mbp: Mailbox structure to initialize
+ * @priv: Private data area.
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @eq_ofld_params: (Offload) Egress queue paramters, that is to be freed.
+ * @cbfn: The call-back function
+ *
+ *
+ */
+void
+csio_mb_eq_ofld_free(struct csio_hw *hw, struct csio_mb *mbp, void *priv,
+ uint32_t mb_tmo, struct csio_eq_params *eq_ofld_params,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_eq_ofld_cmd *cmdp = (struct fw_eq_ofld_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, priv, cbfn, 1);
+
+ cmdp->op_to_vfn = htonl(FW_CMD_OP(FW_EQ_OFLD_CMD) |
+ FW_CMD_REQUEST | FW_CMD_EXEC |
+ FW_EQ_OFLD_CMD_PFN(eq_ofld_params->pfn) |
+ FW_EQ_OFLD_CMD_VFN(eq_ofld_params->vfn));
+ cmdp->alloc_to_len16 = htonl(FW_EQ_OFLD_CMD_FREE |
+ FW_CMD_LEN16(sizeof(*cmdp) / 16));
+ cmdp->eqid_pkd = htonl(FW_EQ_OFLD_CMD_EQID(eq_ofld_params->eqid));
+
+} /* csio_mb_eq_ofld_free */
+
+/*
+ * csio_write_fcoe_link_cond_init_mb - Initialize Mailbox to write FCoE link
+ * condition.
+ *
+ * @ln: The Lnode structure
+ * @mbp: Mailbox structure to initialize
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @cbfn: The call back function.
+ *
+ *
+ */
+void
+csio_write_fcoe_link_cond_init_mb(struct csio_lnode *ln, struct csio_mb *mbp,
+ uint32_t mb_tmo, uint8_t port_id, uint32_t sub_opcode,
+ uint8_t cos, bool link_status, uint32_t fcfi,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_fcoe_link_cmd *cmdp =
+ (struct fw_fcoe_link_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, ln, cbfn, 1);
+
+ cmdp->op_to_portid = htonl((
+ FW_CMD_OP(FW_FCOE_LINK_CMD) |
+ FW_CMD_REQUEST |
+ FW_CMD_WRITE |
+ FW_FCOE_LINK_CMD_PORTID(port_id)));
+ cmdp->sub_opcode_fcfi = htonl(
+ FW_FCOE_LINK_CMD_SUB_OPCODE(sub_opcode) |
+ FW_FCOE_LINK_CMD_FCFI(fcfi));
+ cmdp->lstatus = link_status;
+ cmdp->retval_len16 = htonl(FW_CMD_LEN16(sizeof(*cmdp) / 16));
+
+} /* csio_write_fcoe_link_cond_init_mb */
+
+/*
+ * csio_fcoe_read_res_info_init_mb - Initializes the mailbox for reading FCoE
+ * resource information(FW_GET_RES_INFO_CMD).
+ *
+ * @hw: The HW structure
+ * @mbp: Mailbox structure to initialize
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @cbfn: The call-back function
+ *
+ *
+ */
+void
+csio_fcoe_read_res_info_init_mb(struct csio_hw *hw, struct csio_mb *mbp,
+ uint32_t mb_tmo,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_fcoe_res_info_cmd *cmdp =
+ (struct fw_fcoe_res_info_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, hw, cbfn, 1);
+
+ cmdp->op_to_read = htonl((FW_CMD_OP(FW_FCOE_RES_INFO_CMD) |
+ FW_CMD_REQUEST |
+ FW_CMD_READ));
+
+ cmdp->retval_len16 = htonl(FW_CMD_LEN16(sizeof(*cmdp) / 16));
+
+} /* csio_fcoe_read_res_info_init_mb */
+
+/*
+ * csio_fcoe_vnp_alloc_init_mb - Initializes the mailbox for allocating VNP
+ * in the firmware (FW_FCOE_VNP_CMD).
+ *
+ * @ln: The Lnode structure.
+ * @mbp: Mailbox structure to initialize.
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @fcfi: FCF Index.
+ * @vnpi: vnpi
+ * @iqid: iqid
+ * @vnport_wwnn: vnport WWNN
+ * @vnport_wwpn: vnport WWPN
+ * @cbfn: The call-back function.
+ *
+ *
+ */
+void
+csio_fcoe_vnp_alloc_init_mb(struct csio_lnode *ln, struct csio_mb *mbp,
+ uint32_t mb_tmo, uint32_t fcfi, uint32_t vnpi, uint16_t iqid,
+ uint8_t vnport_wwnn[8], uint8_t vnport_wwpn[8],
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_fcoe_vnp_cmd *cmdp =
+ (struct fw_fcoe_vnp_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, ln, cbfn, 1);
+
+ cmdp->op_to_fcfi = htonl((FW_CMD_OP(FW_FCOE_VNP_CMD) |
+ FW_CMD_REQUEST |
+ FW_CMD_EXEC |
+ FW_FCOE_VNP_CMD_FCFI(fcfi)));
+
+ cmdp->alloc_to_len16 = htonl(FW_FCOE_VNP_CMD_ALLOC |
+ FW_CMD_LEN16(sizeof(*cmdp) / 16));
+
+ cmdp->gen_wwn_to_vnpi = htonl(FW_FCOE_VNP_CMD_VNPI(vnpi));
+
+ cmdp->iqid = htons(iqid);
+
+ if (!wwn_to_u64(vnport_wwnn) && !wwn_to_u64(vnport_wwpn))
+ cmdp->gen_wwn_to_vnpi |= htonl(FW_FCOE_VNP_CMD_GEN_WWN);
+
+ if (vnport_wwnn)
+ memcpy(cmdp->vnport_wwnn, vnport_wwnn, 8);
+ if (vnport_wwpn)
+ memcpy(cmdp->vnport_wwpn, vnport_wwpn, 8);
+
+} /* csio_fcoe_vnp_alloc_init_mb */
+
+/*
+ * csio_fcoe_vnp_read_init_mb - Prepares VNP read cmd.
+ * @ln: The Lnode structure.
+ * @mbp: Mailbox structure to initialize.
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @fcfi: FCF Index.
+ * @vnpi: vnpi
+ * @cbfn: The call-back handler.
+ */
+void
+csio_fcoe_vnp_read_init_mb(struct csio_lnode *ln, struct csio_mb *mbp,
+ uint32_t mb_tmo, uint32_t fcfi, uint32_t vnpi,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_fcoe_vnp_cmd *cmdp =
+ (struct fw_fcoe_vnp_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, ln, cbfn, 1);
+ cmdp->op_to_fcfi = htonl(FW_CMD_OP(FW_FCOE_VNP_CMD) |
+ FW_CMD_REQUEST |
+ FW_CMD_READ |
+ FW_FCOE_VNP_CMD_FCFI(fcfi));
+ cmdp->alloc_to_len16 = htonl(FW_CMD_LEN16(sizeof(*cmdp) / 16));
+ cmdp->gen_wwn_to_vnpi = htonl(FW_FCOE_VNP_CMD_VNPI(vnpi));
+}
+
+/*
+ * csio_fcoe_vnp_free_init_mb - Initializes the mailbox for freeing an
+ * alloacted VNP in the firmware (FW_FCOE_VNP_CMD).
+ *
+ * @ln: The Lnode structure.
+ * @mbp: Mailbox structure to initialize.
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @fcfi: FCF flow id
+ * @vnpi: VNP flow id
+ * @cbfn: The call-back function.
+ * Return: None
+ */
+void
+csio_fcoe_vnp_free_init_mb(struct csio_lnode *ln, struct csio_mb *mbp,
+ uint32_t mb_tmo, uint32_t fcfi, uint32_t vnpi,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_fcoe_vnp_cmd *cmdp =
+ (struct fw_fcoe_vnp_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, ln, cbfn, 1);
+
+ cmdp->op_to_fcfi = htonl(FW_CMD_OP(FW_FCOE_VNP_CMD) |
+ FW_CMD_REQUEST |
+ FW_CMD_EXEC |
+ FW_FCOE_VNP_CMD_FCFI(fcfi));
+ cmdp->alloc_to_len16 = htonl(FW_FCOE_VNP_CMD_FREE |
+ FW_CMD_LEN16(sizeof(*cmdp) / 16));
+ cmdp->gen_wwn_to_vnpi = htonl(FW_FCOE_VNP_CMD_VNPI(vnpi));
+}
+
+/*
+ * csio_fcoe_read_fcf_init_mb - Initializes the mailbox to read the
+ * FCF records.
+ *
+ * @ln: The Lnode structure
+ * @mbp: Mailbox structure to initialize
+ * @mb_tmo: Mailbox time-out period (in ms).
+ * @fcf_params: FC-Forwarder parameters.
+ * @cbfn: The call-back function
+ *
+ *
+ */
+void
+csio_fcoe_read_fcf_init_mb(struct csio_lnode *ln, struct csio_mb *mbp,
+ uint32_t mb_tmo, uint32_t portid, uint32_t fcfi,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct fw_fcoe_fcf_cmd *cmdp =
+ (struct fw_fcoe_fcf_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, ln, cbfn, 1);
+
+ cmdp->op_to_fcfi = htonl(FW_CMD_OP(FW_FCOE_FCF_CMD) |
+ FW_CMD_REQUEST |
+ FW_CMD_READ |
+ FW_FCOE_FCF_CMD_FCFI(fcfi));
+ cmdp->retval_len16 = htonl(FW_CMD_LEN16(sizeof(*cmdp) / 16));
+
+} /* csio_fcoe_read_fcf_init_mb */
+
+void
+csio_fcoe_read_portparams_init_mb(struct csio_hw *hw, struct csio_mb *mbp,
+ uint32_t mb_tmo,
+ struct fw_fcoe_port_cmd_params *portparams,
+ void (*cbfn)(struct csio_hw *,
+ struct csio_mb *))
+{
+ struct fw_fcoe_stats_cmd *cmdp = (struct fw_fcoe_stats_cmd *)(mbp->mb);
+
+ CSIO_INIT_MBP(mbp, cmdp, mb_tmo, hw, cbfn, 1);
+ mbp->mb_size = 64;
+
+ cmdp->op_to_flowid = htonl(FW_CMD_OP(FW_FCOE_STATS_CMD) |
+ FW_CMD_REQUEST | FW_CMD_READ);
+ cmdp->free_to_len16 = htonl(FW_CMD_LEN16(CSIO_MAX_MB_SIZE/16));
+
+ cmdp->u.ctl.nstats_port = FW_FCOE_STATS_CMD_NSTATS(portparams->nstats) |
+ FW_FCOE_STATS_CMD_PORT(portparams->portid);
+
+ cmdp->u.ctl.port_valid_ix = FW_FCOE_STATS_CMD_IX(portparams->idx) |
+ FW_FCOE_STATS_CMD_PORT_VALID;
+
+} /* csio_fcoe_read_portparams_init_mb */
+
+void
+csio_mb_process_portparams_rsp(struct csio_hw *hw,
+ struct csio_mb *mbp,
+ enum fw_retval *retval,
+ struct fw_fcoe_port_cmd_params *portparams,
+ struct fw_fcoe_port_stats *portstats)
+{
+ struct fw_fcoe_stats_cmd *rsp = (struct fw_fcoe_stats_cmd *)(mbp->mb);
+ struct fw_fcoe_port_stats stats;
+ uint8_t *src;
+ uint8_t *dst;
+
+ *retval = FW_CMD_RETVAL_GET(ntohl(rsp->free_to_len16));
+
+ memset(&stats, 0, sizeof(struct fw_fcoe_port_stats));
+
+ if (*retval == FW_SUCCESS) {
+ dst = (uint8_t *)(&stats) + ((portparams->idx - 1) * 8);
+ src = (uint8_t *)rsp + (CSIO_STATS_OFFSET * 8);
+ memcpy(dst, src, (portparams->nstats * 8));
+ if (portparams->idx == 1) {
+ /* Get the first 6 flits from the Mailbox */
+ portstats->tx_bcast_bytes = stats.tx_bcast_bytes;
+ portstats->tx_bcast_frames = stats.tx_bcast_frames;
+ portstats->tx_mcast_bytes = stats.tx_mcast_bytes;
+ portstats->tx_mcast_frames = stats.tx_mcast_frames;
+ portstats->tx_ucast_bytes = stats.tx_ucast_bytes;
+ portstats->tx_ucast_frames = stats.tx_ucast_frames;
+ }
+ if (portparams->idx == 7) {
+ /* Get the second 6 flits from the Mailbox */
+ portstats->tx_drop_frames = stats.tx_drop_frames;
+ portstats->tx_offload_bytes = stats.tx_offload_bytes;
+ portstats->tx_offload_frames = stats.tx_offload_frames;
+#if 0
+ portstats->rx_pf_bytes = stats.rx_pf_bytes;
+ portstats->rx_pf_frames = stats.rx_pf_frames;
+#endif
+ portstats->rx_bcast_bytes = stats.rx_bcast_bytes;
+ portstats->rx_bcast_frames = stats.rx_bcast_frames;
+ portstats->rx_mcast_bytes = stats.rx_mcast_bytes;
+ }
+ if (portparams->idx == 13) {
+ /* Get the last 4 flits from the Mailbox */
+ portstats->rx_mcast_frames = stats.rx_mcast_frames;
+ portstats->rx_ucast_bytes = stats.rx_ucast_bytes;
+ portstats->rx_ucast_frames = stats.rx_ucast_frames;
+ portstats->rx_err_frames = stats.rx_err_frames;
+ }
+ }
+}
+
+/* Entry points/APIs for MB module */
+/*
+ * csio_mb_intr_enable - Enable Interrupts from mailboxes.
+ * @hw: The HW structure
+ *
+ * Enables CIM interrupt bit in appropriate INT_ENABLE registers.
+ */
+void
+csio_mb_intr_enable(struct csio_hw *hw)
+{
+ csio_wr_reg32(hw, MBMSGRDYINTEN(1), MYPF_REG(CIM_PF_HOST_INT_ENABLE));
+ csio_rd_reg32(hw, MYPF_REG(CIM_PF_HOST_INT_ENABLE));
+}
+
+/*
+ * csio_mb_intr_disable - Disable Interrupts from mailboxes.
+ * @hw: The HW structure
+ *
+ * Disable bit in HostInterruptEnable CIM register.
+ */
+void
+csio_mb_intr_disable(struct csio_hw *hw)
+{
+ csio_wr_reg32(hw, MBMSGRDYINTEN(0), MYPF_REG(CIM_PF_HOST_INT_ENABLE));
+ csio_rd_reg32(hw, MYPF_REG(CIM_PF_HOST_INT_ENABLE));
+}
+
+static void
+csio_mb_dump_fw_dbg(struct csio_hw *hw, __be64 *cmd)
+{
+ struct fw_debug_cmd *dbg = (struct fw_debug_cmd *)cmd;
+
+ if ((FW_DEBUG_CMD_TYPE_GET(ntohl(dbg->op_type))) == 1) {
+ csio_info(hw, "FW print message:\n");
+ csio_info(hw, "\tdebug->dprtstridx = %d\n",
+ ntohs(dbg->u.prt.dprtstridx));
+ csio_info(hw, "\tdebug->dprtstrparam0 = 0x%x\n",
+ ntohl(dbg->u.prt.dprtstrparam0));
+ csio_info(hw, "\tdebug->dprtstrparam1 = 0x%x\n",
+ ntohl(dbg->u.prt.dprtstrparam1));
+ csio_info(hw, "\tdebug->dprtstrparam2 = 0x%x\n",
+ ntohl(dbg->u.prt.dprtstrparam2));
+ csio_info(hw, "\tdebug->dprtstrparam3 = 0x%x\n",
+ ntohl(dbg->u.prt.dprtstrparam3));
+ } else {
+ /* This is a FW assertion */
+ csio_fatal(hw, "FW assertion at %.16s:%u, val0 %#x, val1 %#x\n",
+ dbg->u.assert.filename_0_7,
+ ntohl(dbg->u.assert.line),
+ ntohl(dbg->u.assert.x),
+ ntohl(dbg->u.assert.y));
+ }
+}
+
+static void
+csio_mb_debug_cmd_handler(struct csio_hw *hw)
+{
+ int i;
+ __be64 cmd[CSIO_MB_MAX_REGS];
+ uint32_t ctl_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_CTRL);
+ uint32_t data_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_DATA);
+ int size = sizeof(struct fw_debug_cmd);
+
+ /* Copy mailbox data */
+ for (i = 0; i < size; i += 8)
+ cmd[i / 8] = cpu_to_be64(csio_rd_reg64(hw, data_reg + i));
+
+ csio_mb_dump_fw_dbg(hw, cmd);
+
+ /* Notify FW of mailbox by setting owner as UP */
+ csio_wr_reg32(hw, MBMSGVALID | MBINTREQ | MBOWNER(CSIO_MBOWNER_FW),
+ ctl_reg);
+
+ csio_rd_reg32(hw, ctl_reg);
+ wmb();
+}
+
+/*
+ * csio_mb_issue - generic routine for issuing Mailbox commands.
+ * @hw: The HW structure
+ * @mbp: Mailbox command to issue
+ *
+ * Caller should hold hw lock across this call.
+ */
+int
+csio_mb_issue(struct csio_hw *hw, struct csio_mb *mbp)
+{
+ uint32_t owner, ctl;
+ int i;
+ uint32_t ii;
+ __be64 *cmd = mbp->mb;
+ __be64 hdr;
+ struct csio_mbm *mbm = &hw->mbm;
+ uint32_t ctl_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_CTRL);
+ uint32_t data_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_DATA);
+ int size = mbp->mb_size;
+ int rv = -EINVAL;
+ struct fw_cmd_hdr *fw_hdr;
+
+ /* Determine mode */
+ if (mbp->mb_cbfn == NULL) {
+ /* Need to issue/get results in the same context */
+ if (mbp->tmo < CSIO_MB_POLL_FREQ) {
+ csio_err(hw, "Invalid tmo: 0x%x\n", mbp->tmo);
+ goto error_out;
+ }
+ } else if (!csio_is_host_intr_enabled(hw) ||
+ !csio_is_hw_intr_enabled(hw)) {
+ csio_err(hw, "Cannot issue mailbox in interrupt mode 0x%x\n",
+ *((uint8_t *)mbp->mb));
+ goto error_out;
+ }
+
+ if (mbm->mcurrent != NULL) {
+ /* Queue mbox cmd, if another mbox cmd is active */
+ if (mbp->mb_cbfn == NULL) {
+ rv = -EBUSY;
+ csio_dbg(hw, "Couldnt own Mailbox %x op:0x%x\n",
+ hw->pfn, *((uint8_t *)mbp->mb));
+
+ goto error_out;
+ } else {
+ list_add_tail(&mbp->list, &mbm->req_q);
+ CSIO_INC_STATS(mbm, n_activeq);
+
+ return 0;
+ }
+ }
+
+ /* Now get ownership of mailbox */
+ owner = MBOWNER_GET(csio_rd_reg32(hw, ctl_reg));
+
+ if (!csio_mb_is_host_owner(owner)) {
+
+ for (i = 0; (owner == CSIO_MBOWNER_NONE) && (i < 3); i++)
+ owner = MBOWNER_GET(csio_rd_reg32(hw, ctl_reg));
+ /*
+ * Mailbox unavailable. In immediate mode, fail the command.
+ * In other modes, enqueue the request.
+ */
+ if (!csio_mb_is_host_owner(owner)) {
+ if (mbp->mb_cbfn == NULL) {
+ rv = owner ? -EBUSY : -ETIMEDOUT;
+
+ csio_dbg(hw,
+ "Couldnt own Mailbox %x op:0x%x "
+ "owner:%x\n",
+ hw->pfn, *((uint8_t *)mbp->mb), owner);
+ goto error_out;
+ } else {
+ if (mbm->mcurrent == NULL) {
+ csio_err(hw,
+ "Couldnt own Mailbox %x "
+ "op:0x%x owner:%x\n",
+ hw->pfn, *((uint8_t *)mbp->mb),
+ owner);
+ csio_err(hw,
+ "No outstanding driver"
+ " mailbox as well\n");
+ goto error_out;
+ }
+ }
+ }
+ }
+
+ /* Mailbox is available, copy mailbox data into it */
+ for (i = 0; i < size; i += 8) {
+ csio_wr_reg64(hw, be64_to_cpu(*cmd), data_reg + i);
+ cmd++;
+ }
+
+ CSIO_DUMP_MB(hw, hw->pfn, data_reg);
+
+ /* Start completion timers in non-immediate modes and notify FW */
+ if (mbp->mb_cbfn != NULL) {
+ mbm->mcurrent = mbp;
+ mod_timer(&mbm->timer, jiffies + msecs_to_jiffies(mbp->tmo));
+ csio_wr_reg32(hw, MBMSGVALID | MBINTREQ |
+ MBOWNER(CSIO_MBOWNER_FW), ctl_reg);
+ } else
+ csio_wr_reg32(hw, MBMSGVALID | MBOWNER(CSIO_MBOWNER_FW),
+ ctl_reg);
+
+ /* Flush posted writes */
+ csio_rd_reg32(hw, ctl_reg);
+ wmb();
+
+ CSIO_INC_STATS(mbm, n_req);
+
+ if (mbp->mb_cbfn)
+ return 0;
+
+ /* Poll for completion in immediate mode */
+ cmd = mbp->mb;
+
+ for (ii = 0; ii < mbp->tmo; ii += CSIO_MB_POLL_FREQ) {
+ mdelay(CSIO_MB_POLL_FREQ);
+
+ /* Check for response */
+ ctl = csio_rd_reg32(hw, ctl_reg);
+ if (csio_mb_is_host_owner(MBOWNER_GET(ctl))) {
+
+ if (!(ctl & MBMSGVALID)) {
+ csio_wr_reg32(hw, 0, ctl_reg);
+ continue;
+ }
+
+ CSIO_DUMP_MB(hw, hw->pfn, data_reg);
+
+ hdr = cpu_to_be64(csio_rd_reg64(hw, data_reg));
+ fw_hdr = (struct fw_cmd_hdr *)&hdr;
+
+ switch (FW_CMD_OP_GET(ntohl(fw_hdr->hi))) {
+ case FW_DEBUG_CMD:
+ csio_mb_debug_cmd_handler(hw);
+ continue;
+ }
+
+ /* Copy response */
+ for (i = 0; i < size; i += 8)
+ *cmd++ = cpu_to_be64(csio_rd_reg64
+ (hw, data_reg + i));
+ csio_wr_reg32(hw, 0, ctl_reg);
+
+ if (csio_mb_fw_retval(mbp) != FW_SUCCESS)
+ CSIO_INC_STATS(mbm, n_err);
+
+ CSIO_INC_STATS(mbm, n_rsp);
+ return 0;
+ }
+ }
+
+ CSIO_INC_STATS(mbm, n_tmo);
+
+ csio_err(hw, "Mailbox %x op:0x%x timed out!\n",
+ hw->pfn, *((uint8_t *)cmd));
+
+ return -ETIMEDOUT;
+
+error_out:
+ CSIO_INC_STATS(mbm, n_err);
+ return rv;
+}
+
+/*
+ * csio_mb_completions - Completion handler for Mailbox commands
+ * @hw: The HW structure
+ * @cbfn_q: Completion queue.
+ *
+ */
+void
+csio_mb_completions(struct csio_hw *hw, struct list_head *cbfn_q)
+{
+ struct csio_mb *mbp;
+ struct csio_mbm *mbm = &hw->mbm;
+ enum fw_retval rv;
+
+ while (!list_empty(cbfn_q)) {
+ mbp = list_first_entry(cbfn_q, struct csio_mb, list);
+ list_del_init(&mbp->list);
+
+ rv = csio_mb_fw_retval(mbp);
+ if ((rv != FW_SUCCESS) && (rv != FW_HOSTERROR))
+ CSIO_INC_STATS(mbm, n_err);
+ else if (rv != FW_HOSTERROR)
+ CSIO_INC_STATS(mbm, n_rsp);
+
+ if (mbp->mb_cbfn)
+ mbp->mb_cbfn(hw, mbp);
+ }
+}
+
+static void
+csio_mb_portmod_changed(struct csio_hw *hw, uint8_t port_id)
+{
+ static char *mod_str[] = {
+ NULL, "LR", "SR", "ER", "TWINAX", "active TWINAX", "LRM"
+ };
+
+ struct csio_pport *port = &hw->pport[port_id];
+
+ if (port->mod_type == FW_PORT_MOD_TYPE_NONE)
+ csio_info(hw, "Port:%d - port module unplugged\n", port_id);
+ else if (port->mod_type < ARRAY_SIZE(mod_str))
+ csio_info(hw, "Port:%d - %s port module inserted\n", port_id,
+ mod_str[port->mod_type]);
+ else if (port->mod_type == FW_PORT_MOD_TYPE_NOTSUPPORTED)
+ csio_info(hw,
+ "Port:%d - unsupported optical port module "
+ "inserted\n", port_id);
+ else if (port->mod_type == FW_PORT_MOD_TYPE_UNKNOWN)
+ csio_info(hw,
+ "Port:%d - unknown port module inserted, forcing "
+ "TWINAX\n", port_id);
+ else if (port->mod_type == FW_PORT_MOD_TYPE_ERROR)
+ csio_info(hw, "Port:%d - transceiver module error\n", port_id);
+ else
+ csio_info(hw, "Port:%d - unknown module type %d inserted\n",
+ port_id, port->mod_type);
+}
+
+int
+csio_mb_fwevt_handler(struct csio_hw *hw, __be64 *cmd)
+{
+ uint8_t opcode = *(uint8_t *)cmd;
+ struct fw_port_cmd *pcmd;
+ uint8_t port_id;
+ uint32_t link_status;
+ uint16_t action;
+ uint8_t mod_type;
+
+ if (opcode == FW_PORT_CMD) {
+ pcmd = (struct fw_port_cmd *)cmd;
+ port_id = FW_PORT_CMD_PORTID_GET(
+ ntohl(pcmd->op_to_portid));
+ action = FW_PORT_CMD_ACTION_GET(
+ ntohl(pcmd->action_to_len16));
+ if (action != FW_PORT_ACTION_GET_PORT_INFO) {
+ csio_err(hw, "Unhandled FW_PORT_CMD action: %u\n",
+ action);
+ return -EINVAL;
+ }
+
+ link_status = ntohl(pcmd->u.info.lstatus_to_modtype);
+ mod_type = FW_PORT_CMD_MODTYPE_GET(link_status);
+
+ hw->pport[port_id].link_status =
+ FW_PORT_CMD_LSTATUS_GET(link_status);
+ hw->pport[port_id].link_speed =
+ FW_PORT_CMD_LSPEED_GET(link_status);
+
+ csio_info(hw, "Port:%x - LINK %s\n", port_id,
+ FW_PORT_CMD_LSTATUS_GET(link_status) ? "UP" : "DOWN");
+
+ if (mod_type != hw->pport[port_id].mod_type) {
+ hw->pport[port_id].mod_type = mod_type;
+ csio_mb_portmod_changed(hw, port_id);
+ }
+ } else if (opcode == FW_DEBUG_CMD) {
+ csio_mb_dump_fw_dbg(hw, cmd);
+ } else {
+ csio_dbg(hw, "Gen MB can't handle op:0x%x on evtq.\n", opcode);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * csio_mb_isr_handler - Handle mailboxes related interrupts.
+ * @hw: The HW structure
+ *
+ * Called from the ISR to handle Mailbox related interrupts.
+ * HW Lock should be held across this call.
+ */
+int
+csio_mb_isr_handler(struct csio_hw *hw)
+{
+ struct csio_mbm *mbm = &hw->mbm;
+ struct csio_mb *mbp = mbm->mcurrent;
+ __be64 *cmd;
+ uint32_t ctl, cim_cause, pl_cause;
+ int i;
+ uint32_t ctl_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_CTRL);
+ uint32_t data_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_DATA);
+ int size;
+ __be64 hdr;
+ struct fw_cmd_hdr *fw_hdr;
+
+ pl_cause = csio_rd_reg32(hw, MYPF_REG(PL_PF_INT_CAUSE));
+ cim_cause = csio_rd_reg32(hw, MYPF_REG(CIM_PF_HOST_INT_CAUSE));
+
+ if (!(pl_cause & PFCIM) || !(cim_cause & MBMSGRDYINT)) {
+ CSIO_INC_STATS(hw, n_mbint_unexp);
+ return -EINVAL;
+ }
+
+ /*
+ * The cause registers below HAVE to be cleared in the SAME
+ * order as below: The low level cause register followed by
+ * the upper level cause register. In other words, CIM-cause
+ * first followed by PL-Cause next.
+ */
+ csio_wr_reg32(hw, MBMSGRDYINT, MYPF_REG(CIM_PF_HOST_INT_CAUSE));
+ csio_wr_reg32(hw, PFCIM, MYPF_REG(PL_PF_INT_CAUSE));
+
+ ctl = csio_rd_reg32(hw, ctl_reg);
+
+ if (csio_mb_is_host_owner(MBOWNER_GET(ctl))) {
+
+ CSIO_DUMP_MB(hw, hw->pfn, data_reg);
+
+ if (!(ctl & MBMSGVALID)) {
+ csio_warn(hw,
+ "Stray mailbox interrupt recvd,"
+ " mailbox data not valid\n");
+ csio_wr_reg32(hw, 0, ctl_reg);
+ /* Flush */
+ csio_rd_reg32(hw, ctl_reg);
+ return -EINVAL;
+ }
+
+ hdr = cpu_to_be64(csio_rd_reg64(hw, data_reg));
+ fw_hdr = (struct fw_cmd_hdr *)&hdr;
+
+ switch (FW_CMD_OP_GET(ntohl(fw_hdr->hi))) {
+ case FW_DEBUG_CMD:
+ csio_mb_debug_cmd_handler(hw);
+ return -EINVAL;
+#if 0
+ case FW_ERROR_CMD:
+ case FW_INITIALIZE_CMD: /* When we are not master */
+#endif
+ }
+
+ CSIO_ASSERT(mbp != NULL);
+
+ cmd = mbp->mb;
+ size = mbp->mb_size;
+ /* Get response */
+ for (i = 0; i < size; i += 8)
+ *cmd++ = cpu_to_be64(csio_rd_reg64
+ (hw, data_reg + i));
+
+ csio_wr_reg32(hw, 0, ctl_reg);
+ /* Flush */
+ csio_rd_reg32(hw, ctl_reg);
+
+ mbm->mcurrent = NULL;
+
+ /* Add completion to tail of cbfn queue */
+ list_add_tail(&mbp->list, &mbm->cbfn_q);
+ CSIO_INC_STATS(mbm, n_cbfnq);
+
+ /*
+ * Enqueue event to EventQ. Events processing happens
+ * in Event worker thread context
+ */
+ if (csio_enqueue_evt(hw, CSIO_EVT_MBX, mbp, sizeof(mbp)))
+ CSIO_INC_STATS(hw, n_evt_drop);
+
+ return 0;
+
+ } else {
+ /*
+ * We can get here if mailbox MSIX vector is shared,
+ * or in INTx case. Or a stray interrupt.
+ */
+ csio_dbg(hw, "Host not owner, no mailbox interrupt\n");
+ CSIO_INC_STATS(hw, n_int_stray);
+ return -EINVAL;
+ }
+}
+
+/*
+ * csio_mb_tmo_handler - Timeout handler
+ * @hw: The HW structure
+ *
+ */
+struct csio_mb *
+csio_mb_tmo_handler(struct csio_hw *hw)
+{
+ struct csio_mbm *mbm = &hw->mbm;
+ struct csio_mb *mbp = mbm->mcurrent;
+ struct fw_cmd_hdr *fw_hdr;
+
+ /*
+ * Could be a race b/w the completion handler and the timer
+ * and the completion handler won that race.
+ */
+ if (mbp == NULL) {
+ CSIO_DB_ASSERT(0);
+ return NULL;
+ }
+
+ fw_hdr = (struct fw_cmd_hdr *)(mbp->mb);
+
+ csio_dbg(hw, "Mailbox num:%x op:0x%x timed out\n", hw->pfn,
+ FW_CMD_OP_GET(ntohl(fw_hdr->hi)));
+
+ mbm->mcurrent = NULL;
+ CSIO_INC_STATS(mbm, n_tmo);
+ fw_hdr->lo = htonl(FW_CMD_RETVAL(FW_ETIMEDOUT));
+
+ return mbp;
+}
+
+/*
+ * csio_mb_cancel_all - Cancel all waiting commands.
+ * @hw: The HW structure
+ * @cbfn_q: The callback queue.
+ *
+ * Caller should hold hw lock across this call.
+ */
+void
+csio_mb_cancel_all(struct csio_hw *hw, struct list_head *cbfn_q)
+{
+ struct csio_mb *mbp;
+ struct csio_mbm *mbm = &hw->mbm;
+ struct fw_cmd_hdr *hdr;
+ struct list_head *tmp;
+
+ if (mbm->mcurrent) {
+ mbp = mbm->mcurrent;
+
+ /* Stop mailbox completion timer */
+ del_timer_sync(&mbm->timer);
+
+ /* Add completion to tail of cbfn queue */
+ list_add_tail(&mbp->list, cbfn_q);
+ mbm->mcurrent = NULL;
+ }
+
+ if (!list_empty(&mbm->req_q)) {
+ list_splice_tail_init(&mbm->req_q, cbfn_q);
+ mbm->stats.n_activeq = 0;
+ }
+
+ if (!list_empty(&mbm->cbfn_q)) {
+ list_splice_tail_init(&mbm->cbfn_q, cbfn_q);
+ mbm->stats.n_cbfnq = 0;
+ }
+
+ if (list_empty(cbfn_q))
+ return;
+
+ list_for_each(tmp, cbfn_q) {
+ mbp = (struct csio_mb *)tmp;
+ hdr = (struct fw_cmd_hdr *)(mbp->mb);
+
+ csio_dbg(hw, "Cancelling pending mailbox num %x op:%x\n",
+ hw->pfn, FW_CMD_OP_GET(ntohl(hdr->hi)));
+
+ CSIO_INC_STATS(mbm, n_cancel);
+ hdr->lo = htonl(FW_CMD_RETVAL(FW_HOSTERROR));
+ }
+}
+
+/*
+ * csio_mbm_init - Initialize Mailbox module
+ * @mbm: Mailbox module
+ * @hw: The HW structure
+ * @timer: Timing function for interrupting mailboxes
+ *
+ * Initialize timer and the request/response queues.
+ */
+int
+csio_mbm_init(struct csio_mbm *mbm, struct csio_hw *hw,
+ void (*timer_fn)(uintptr_t))
+{
+ struct timer_list *timer = &mbm->timer;
+
+ init_timer(timer);
+ timer->function = timer_fn;
+ timer->data = (unsigned long)hw;
+
+ INIT_LIST_HEAD(&mbm->req_q);
+ INIT_LIST_HEAD(&mbm->cbfn_q);
+ csio_set_mb_intr_idx(mbm, -1);
+
+ return 0;
+}
+
+/*
+ * csio_mbm_exit - Uninitialize mailbox module
+ * @mbm: Mailbox module
+ *
+ * Stop timer.
+ */
+void
+csio_mbm_exit(struct csio_mbm *mbm)
+{
+ del_timer_sync(&mbm->timer);
+
+ CSIO_DB_ASSERT(mbm->mcurrent == NULL);
+ CSIO_DB_ASSERT(list_empty(&mbm->req_q));
+ CSIO_DB_ASSERT(list_empty(&mbm->cbfn_q));
+}
diff --git a/drivers/scsi/csiostor/csio_mb.h b/drivers/scsi/csiostor/csio_mb.h
new file mode 100644
index 00000000000..1788ea506f3
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_mb.h
@@ -0,0 +1,278 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CSIO_MB_H__
+#define __CSIO_MB_H__
+
+#include <linux/timer.h>
+#include <linux/completion.h>
+
+#include "t4fw_api.h"
+#include "t4fw_api_stor.h"
+#include "csio_defs.h"
+
+#define CSIO_STATS_OFFSET (2)
+#define CSIO_NUM_STATS_PER_MB (6)
+
+struct fw_fcoe_port_cmd_params {
+ uint8_t portid;
+ uint8_t idx;
+ uint8_t nstats;
+};
+
+#define CSIO_DUMP_MB(__hw, __num, __mb) \
+ csio_dbg(__hw, "\t%llx %llx %llx %llx %llx %llx %llx %llx\n", \
+ (unsigned long long)csio_rd_reg64(__hw, __mb), \
+ (unsigned long long)csio_rd_reg64(__hw, __mb + 8), \
+ (unsigned long long)csio_rd_reg64(__hw, __mb + 16), \
+ (unsigned long long)csio_rd_reg64(__hw, __mb + 24), \
+ (unsigned long long)csio_rd_reg64(__hw, __mb + 32), \
+ (unsigned long long)csio_rd_reg64(__hw, __mb + 40), \
+ (unsigned long long)csio_rd_reg64(__hw, __mb + 48), \
+ (unsigned long long)csio_rd_reg64(__hw, __mb + 56))
+
+#define CSIO_MB_MAX_REGS 8
+#define CSIO_MAX_MB_SIZE 64
+#define CSIO_MB_POLL_FREQ 5 /* 5 ms */
+#define CSIO_MB_DEFAULT_TMO FW_CMD_MAX_TIMEOUT
+
+/* Device master in HELLO command */
+enum csio_dev_master { CSIO_MASTER_CANT, CSIO_MASTER_MAY, CSIO_MASTER_MUST };
+
+enum csio_mb_owner { CSIO_MBOWNER_NONE, CSIO_MBOWNER_FW, CSIO_MBOWNER_PL };
+
+enum csio_dev_state {
+ CSIO_DEV_STATE_UNINIT,
+ CSIO_DEV_STATE_INIT,
+ CSIO_DEV_STATE_ERR
+};
+
+#define FW_PARAM_DEV(param) \
+ (FW_PARAMS_MNEM(FW_PARAMS_MNEM_DEV) | \
+ FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_DEV_##param))
+
+#define FW_PARAM_PFVF(param) \
+ (FW_PARAMS_MNEM(FW_PARAMS_MNEM_PFVF) | \
+ FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_PFVF_##param)| \
+ FW_PARAMS_PARAM_Y(0) | \
+ FW_PARAMS_PARAM_Z(0))
+
+enum {
+ PAUSE_RX = 1 << 0,
+ PAUSE_TX = 1 << 1,
+ PAUSE_AUTONEG = 1 << 2
+};
+
+#define CSIO_INIT_MBP(__mbp, __cp, __tmo, __priv, __fn, __clear) \
+do { \
+ if (__clear) \
+ memset((__cp), 0, \
+ CSIO_MB_MAX_REGS * sizeof(__be64)); \
+ INIT_LIST_HEAD(&(__mbp)->list); \
+ (__mbp)->tmo = (__tmo); \
+ (__mbp)->priv = (void *)(__priv); \
+ (__mbp)->mb_cbfn = (__fn); \
+ (__mbp)->mb_size = sizeof(*(__cp)); \
+} while (0)
+
+struct csio_mbm_stats {
+ uint32_t n_req; /* number of mbox req */
+ uint32_t n_rsp; /* number of mbox rsp */
+ uint32_t n_activeq; /* number of mbox req active Q */
+ uint32_t n_cbfnq; /* number of mbox req cbfn Q */
+ uint32_t n_tmo; /* number of mbox timeout */
+ uint32_t n_cancel; /* number of mbox cancel */
+ uint32_t n_err; /* number of mbox error */
+};
+
+/* Driver version of Mailbox */
+struct csio_mb {
+ struct list_head list; /* for req/resp */
+ /* queue in driver */
+ __be64 mb[CSIO_MB_MAX_REGS]; /* MB in HW format */
+ int mb_size; /* Size of this
+ * mailbox.
+ */
+ uint32_t tmo; /* Timeout */
+ struct completion cmplobj; /* MB Completion
+ * object
+ */
+ void (*mb_cbfn) (struct csio_hw *, struct csio_mb *);
+ /* Callback fn */
+ void *priv; /* Owner private ptr */
+};
+
+struct csio_mbm {
+ uint32_t a_mbox; /* Async mbox num */
+ uint32_t intr_idx; /* Interrupt index */
+ struct timer_list timer; /* Mbox timer */
+ struct list_head req_q; /* Mbox request queue */
+ struct list_head cbfn_q; /* Mbox completion q */
+ struct csio_mb *mcurrent; /* Current mailbox */
+ uint32_t req_q_cnt; /* Outstanding mbox
+ * cmds
+ */
+ struct csio_mbm_stats stats; /* Statistics */
+};
+
+#define csio_set_mb_intr_idx(_m, _i) ((_m)->intr_idx = (_i))
+#define csio_get_mb_intr_idx(_m) ((_m)->intr_idx)
+
+struct csio_iq_params;
+struct csio_eq_params;
+
+enum fw_retval csio_mb_fw_retval(struct csio_mb *);
+
+/* MB helpers */
+void csio_mb_hello(struct csio_hw *, struct csio_mb *, uint32_t,
+ uint32_t, uint32_t, enum csio_dev_master,
+ void (*)(struct csio_hw *, struct csio_mb *));
+
+void csio_mb_process_hello_rsp(struct csio_hw *, struct csio_mb *,
+ enum fw_retval *, enum csio_dev_state *,
+ uint8_t *);
+
+void csio_mb_bye(struct csio_hw *, struct csio_mb *, uint32_t,
+ void (*)(struct csio_hw *, struct csio_mb *));
+
+void csio_mb_reset(struct csio_hw *, struct csio_mb *, uint32_t, int, int,
+ void (*)(struct csio_hw *, struct csio_mb *));
+
+void csio_mb_params(struct csio_hw *, struct csio_mb *, uint32_t, unsigned int,
+ unsigned int, unsigned int, const u32 *, u32 *, bool,
+ void (*)(struct csio_hw *, struct csio_mb *));
+
+void csio_mb_process_read_params_rsp(struct csio_hw *, struct csio_mb *,
+ enum fw_retval *, unsigned int , u32 *);
+
+void csio_mb_ldst(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo,
+ int reg);
+
+void csio_mb_caps_config(struct csio_hw *, struct csio_mb *, uint32_t,
+ bool, bool, bool, bool,
+ void (*)(struct csio_hw *, struct csio_mb *));
+
+void csio_rss_glb_config(struct csio_hw *, struct csio_mb *,
+ uint32_t, uint8_t, unsigned int,
+ void (*)(struct csio_hw *, struct csio_mb *));
+
+void csio_mb_pfvf(struct csio_hw *, struct csio_mb *, uint32_t,
+ unsigned int, unsigned int, unsigned int,
+ unsigned int, unsigned int, unsigned int,
+ unsigned int, unsigned int, unsigned int,
+ unsigned int, unsigned int, unsigned int,
+ unsigned int, void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_mb_port(struct csio_hw *, struct csio_mb *, uint32_t,
+ uint8_t, bool, uint32_t, uint16_t,
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_mb_process_read_port_rsp(struct csio_hw *, struct csio_mb *,
+ enum fw_retval *, uint16_t *);
+
+void csio_mb_initialize(struct csio_hw *, struct csio_mb *, uint32_t,
+ void (*)(struct csio_hw *, struct csio_mb *));
+
+void csio_mb_iq_alloc_write(struct csio_hw *, struct csio_mb *, void *,
+ uint32_t, struct csio_iq_params *,
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_mb_iq_alloc_write_rsp(struct csio_hw *, struct csio_mb *,
+ enum fw_retval *, struct csio_iq_params *);
+
+void csio_mb_iq_free(struct csio_hw *, struct csio_mb *, void *,
+ uint32_t, struct csio_iq_params *,
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_mb_eq_ofld_alloc_write(struct csio_hw *, struct csio_mb *, void *,
+ uint32_t, struct csio_eq_params *,
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_mb_eq_ofld_alloc_write_rsp(struct csio_hw *, struct csio_mb *,
+ enum fw_retval *, struct csio_eq_params *);
+
+void csio_mb_eq_ofld_free(struct csio_hw *, struct csio_mb *, void *,
+ uint32_t , struct csio_eq_params *,
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_fcoe_read_res_info_init_mb(struct csio_hw *, struct csio_mb *,
+ uint32_t,
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_write_fcoe_link_cond_init_mb(struct csio_lnode *, struct csio_mb *,
+ uint32_t, uint8_t, uint32_t, uint8_t, bool, uint32_t,
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_fcoe_vnp_alloc_init_mb(struct csio_lnode *, struct csio_mb *,
+ uint32_t, uint32_t , uint32_t , uint16_t,
+ uint8_t [8], uint8_t [8],
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_fcoe_vnp_read_init_mb(struct csio_lnode *, struct csio_mb *,
+ uint32_t, uint32_t , uint32_t ,
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_fcoe_vnp_free_init_mb(struct csio_lnode *, struct csio_mb *,
+ uint32_t , uint32_t, uint32_t ,
+ void (*) (struct csio_hw *, struct csio_mb *));
+
+void csio_fcoe_read_fcf_init_mb(struct csio_lnode *, struct csio_mb *,
+ uint32_t, uint32_t, uint32_t,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *));
+
+void csio_fcoe_read_portparams_init_mb(struct csio_hw *hw,
+ struct csio_mb *mbp, uint32_t mb_tmo,
+ struct fw_fcoe_port_cmd_params *portparams,
+ void (*cbfn)(struct csio_hw *, struct csio_mb *));
+
+void csio_mb_process_portparams_rsp(struct csio_hw *hw, struct csio_mb *mbp,
+ enum fw_retval *retval,
+ struct fw_fcoe_port_cmd_params *portparams,
+ struct fw_fcoe_port_stats *portstats);
+
+/* MB module functions */
+int csio_mbm_init(struct csio_mbm *, struct csio_hw *,
+ void (*)(uintptr_t));
+void csio_mbm_exit(struct csio_mbm *);
+void csio_mb_intr_enable(struct csio_hw *);
+void csio_mb_intr_disable(struct csio_hw *);
+
+int csio_mb_issue(struct csio_hw *, struct csio_mb *);
+void csio_mb_completions(struct csio_hw *, struct list_head *);
+int csio_mb_fwevt_handler(struct csio_hw *, __be64 *);
+int csio_mb_isr_handler(struct csio_hw *);
+struct csio_mb *csio_mb_tmo_handler(struct csio_hw *);
+void csio_mb_cancel_all(struct csio_hw *, struct list_head *);
+
+#endif /* ifndef __CSIO_MB_H__ */
diff --git a/drivers/scsi/csiostor/csio_rnode.c b/drivers/scsi/csiostor/csio_rnode.c
new file mode 100644
index 00000000000..51c6a388de2
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_rnode.c
@@ -0,0 +1,913 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/string.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport_fc.h>
+#include <scsi/fc/fc_els.h>
+#include <scsi/fc/fc_fs.h>
+
+#include "csio_hw.h"
+#include "csio_lnode.h"
+#include "csio_rnode.h"
+
+static int csio_rnode_init(struct csio_rnode *, struct csio_lnode *);
+static void csio_rnode_exit(struct csio_rnode *);
+
+/* Static machine forward declarations */
+static void csio_rns_uninit(struct csio_rnode *, enum csio_rn_ev);
+static void csio_rns_ready(struct csio_rnode *, enum csio_rn_ev);
+static void csio_rns_offline(struct csio_rnode *, enum csio_rn_ev);
+static void csio_rns_disappeared(struct csio_rnode *, enum csio_rn_ev);
+
+/* RNF event mapping */
+static enum csio_rn_ev fwevt_to_rnevt[] = {
+ CSIO_RNFE_NONE, /* None */
+ CSIO_RNFE_LOGGED_IN, /* PLOGI_ACC_RCVD */
+ CSIO_RNFE_NONE, /* PLOGI_RJT_RCVD */
+ CSIO_RNFE_PLOGI_RECV, /* PLOGI_RCVD */
+ CSIO_RNFE_LOGO_RECV, /* PLOGO_RCVD */
+ CSIO_RNFE_PRLI_DONE, /* PRLI_ACC_RCVD */
+ CSIO_RNFE_NONE, /* PRLI_RJT_RCVD */
+ CSIO_RNFE_PRLI_RECV, /* PRLI_RCVD */
+ CSIO_RNFE_PRLO_RECV, /* PRLO_RCVD */
+ CSIO_RNFE_NONE, /* NPORT_ID_CHGD */
+ CSIO_RNFE_LOGO_RECV, /* FLOGO_RCVD */
+ CSIO_RNFE_NONE, /* CLR_VIRT_LNK_RCVD */
+ CSIO_RNFE_LOGGED_IN, /* FLOGI_ACC_RCVD */
+ CSIO_RNFE_NONE, /* FLOGI_RJT_RCVD */
+ CSIO_RNFE_LOGGED_IN, /* FDISC_ACC_RCVD */
+ CSIO_RNFE_NONE, /* FDISC_RJT_RCVD */
+ CSIO_RNFE_NONE, /* FLOGI_TMO_MAX_RETRY */
+ CSIO_RNFE_NONE, /* IMPL_LOGO_ADISC_ACC */
+ CSIO_RNFE_NONE, /* IMPL_LOGO_ADISC_RJT */
+ CSIO_RNFE_NONE, /* IMPL_LOGO_ADISC_CNFLT */
+ CSIO_RNFE_NONE, /* PRLI_TMO */
+ CSIO_RNFE_NONE, /* ADISC_TMO */
+ CSIO_RNFE_NAME_MISSING, /* RSCN_DEV_LOST */
+ CSIO_RNFE_NONE, /* SCR_ACC_RCVD */
+ CSIO_RNFE_NONE, /* ADISC_RJT_RCVD */
+ CSIO_RNFE_NONE, /* LOGO_SNT */
+ CSIO_RNFE_LOGO_RECV, /* PROTO_ERR_IMPL_LOGO */
+};
+
+#define CSIO_FWE_TO_RNFE(_evt) ((_evt > PROTO_ERR_IMPL_LOGO) ? \
+ CSIO_RNFE_NONE : \
+ fwevt_to_rnevt[_evt])
+int
+csio_is_rnode_ready(struct csio_rnode *rn)
+{
+ return csio_match_state(rn, csio_rns_ready);
+}
+
+static int
+csio_is_rnode_uninit(struct csio_rnode *rn)
+{
+ return csio_match_state(rn, csio_rns_uninit);
+}
+
+static int
+csio_is_rnode_wka(uint8_t rport_type)
+{
+ if ((rport_type == FLOGI_VFPORT) ||
+ (rport_type == FDISC_VFPORT) ||
+ (rport_type == NS_VNPORT) ||
+ (rport_type == FDMI_VNPORT))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * csio_rn_lookup - Finds the rnode with the given flowid
+ * @ln - lnode
+ * @flowid - flowid.
+ *
+ * Does the rnode lookup on the given lnode and flowid.If no matching entry
+ * found, NULL is returned.
+ */
+static struct csio_rnode *
+csio_rn_lookup(struct csio_lnode *ln, uint32_t flowid)
+{
+ struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead;
+ struct list_head *tmp;
+ struct csio_rnode *rn;
+
+ list_for_each(tmp, &rnhead->sm.sm_list) {
+ rn = (struct csio_rnode *) tmp;
+ if (rn->flowid == flowid)
+ return rn;
+ }
+
+ return NULL;
+}
+
+/*
+ * csio_rn_lookup_wwpn - Finds the rnode with the given wwpn
+ * @ln: lnode
+ * @wwpn: wwpn
+ *
+ * Does the rnode lookup on the given lnode and wwpn. If no matching entry
+ * found, NULL is returned.
+ */
+static struct csio_rnode *
+csio_rn_lookup_wwpn(struct csio_lnode *ln, uint8_t *wwpn)
+{
+ struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead;
+ struct list_head *tmp;
+ struct csio_rnode *rn;
+
+ list_for_each(tmp, &rnhead->sm.sm_list) {
+ rn = (struct csio_rnode *) tmp;
+ if (!memcmp(csio_rn_wwpn(rn), wwpn, 8))
+ return rn;
+ }
+
+ return NULL;
+}
+
+/**
+ * csio_rnode_lookup_portid - Finds the rnode with the given portid
+ * @ln: lnode
+ * @portid: port id
+ *
+ * Lookup the rnode list for a given portid. If no matching entry
+ * found, NULL is returned.
+ */
+struct csio_rnode *
+csio_rnode_lookup_portid(struct csio_lnode *ln, uint32_t portid)
+{
+ struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead;
+ struct list_head *tmp;
+ struct csio_rnode *rn;
+
+ list_for_each(tmp, &rnhead->sm.sm_list) {
+ rn = (struct csio_rnode *) tmp;
+ if (rn->nport_id == portid)
+ return rn;
+ }
+
+ return NULL;
+}
+
+static int
+csio_rn_dup_flowid(struct csio_lnode *ln, uint32_t rdev_flowid,
+ uint32_t *vnp_flowid)
+{
+ struct csio_rnode *rnhead;
+ struct list_head *tmp, *tmp1;
+ struct csio_rnode *rn;
+ struct csio_lnode *ln_tmp;
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ list_for_each(tmp1, &hw->sln_head) {
+ ln_tmp = (struct csio_lnode *) tmp1;
+ if (ln_tmp == ln)
+ continue;
+
+ rnhead = (struct csio_rnode *)&ln_tmp->rnhead;
+ list_for_each(tmp, &rnhead->sm.sm_list) {
+
+ rn = (struct csio_rnode *) tmp;
+ if (csio_is_rnode_ready(rn)) {
+ if (rn->flowid == rdev_flowid) {
+ *vnp_flowid = csio_ln_flowid(ln_tmp);
+ return 1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static struct csio_rnode *
+csio_alloc_rnode(struct csio_lnode *ln)
+{
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ struct csio_rnode *rn = mempool_alloc(hw->rnode_mempool, GFP_ATOMIC);
+ if (!rn)
+ goto err;
+
+ memset(rn, 0, sizeof(struct csio_rnode));
+ if (csio_rnode_init(rn, ln))
+ goto err_free;
+
+ CSIO_INC_STATS(ln, n_rnode_alloc);
+
+ return rn;
+
+err_free:
+ mempool_free(rn, hw->rnode_mempool);
+err:
+ CSIO_INC_STATS(ln, n_rnode_nomem);
+ return NULL;
+}
+
+static void
+csio_free_rnode(struct csio_rnode *rn)
+{
+ struct csio_hw *hw = csio_lnode_to_hw(csio_rnode_to_lnode(rn));
+
+ csio_rnode_exit(rn);
+ CSIO_INC_STATS(rn->lnp, n_rnode_free);
+ mempool_free(rn, hw->rnode_mempool);
+}
+
+/*
+ * csio_get_rnode - Gets rnode with the given flowid
+ * @ln - lnode
+ * @flowid - flow id.
+ *
+ * Does the rnode lookup on the given lnode and flowid. If no matching
+ * rnode found, then new rnode with given npid is allocated and returned.
+ */
+static struct csio_rnode *
+csio_get_rnode(struct csio_lnode *ln, uint32_t flowid)
+{
+ struct csio_rnode *rn;
+
+ rn = csio_rn_lookup(ln, flowid);
+ if (!rn) {
+ rn = csio_alloc_rnode(ln);
+ if (!rn)
+ return NULL;
+
+ rn->flowid = flowid;
+ }
+
+ return rn;
+}
+
+/*
+ * csio_put_rnode - Frees the given rnode
+ * @ln - lnode
+ * @flowid - flow id.
+ *
+ * Does the rnode lookup on the given lnode and flowid. If no matching
+ * rnode found, then new rnode with given npid is allocated and returned.
+ */
+void
+csio_put_rnode(struct csio_lnode *ln, struct csio_rnode *rn)
+{
+ CSIO_DB_ASSERT(csio_is_rnode_uninit(rn) != 0);
+ csio_free_rnode(rn);
+}
+
+/*
+ * csio_confirm_rnode - confirms rnode based on wwpn.
+ * @ln: lnode
+ * @rdev_flowid: remote device flowid
+ * @rdevp: remote device params
+ * This routines searches other rnode in list having same wwpn of new rnode.
+ * If there is a match, then matched rnode is returned and otherwise new rnode
+ * is returned.
+ * returns rnode.
+ */
+struct csio_rnode *
+csio_confirm_rnode(struct csio_lnode *ln, uint32_t rdev_flowid,
+ struct fcoe_rdev_entry *rdevp)
+{
+ uint8_t rport_type;
+ struct csio_rnode *rn, *match_rn;
+ uint32_t vnp_flowid;
+ __be32 *port_id;
+
+ port_id = (__be32 *)&rdevp->r_id[0];
+ rport_type =
+ FW_RDEV_WR_RPORT_TYPE_GET(rdevp->rd_xfer_rdy_to_rport_type);
+
+ /* Drop rdev event for cntrl port */
+ if (rport_type == FAB_CTLR_VNPORT) {
+ csio_ln_dbg(ln,
+ "Unhandled rport_type:%d recv in rdev evt "
+ "ssni:x%x\n", rport_type, rdev_flowid);
+ return NULL;
+ }
+
+ /* Lookup on flowid */
+ rn = csio_rn_lookup(ln, rdev_flowid);
+ if (!rn) {
+
+ /* Drop events with duplicate flowid */
+ if (csio_rn_dup_flowid(ln, rdev_flowid, &vnp_flowid)) {
+ csio_ln_warn(ln,
+ "ssni:%x already active on vnpi:%x",
+ rdev_flowid, vnp_flowid);
+ return NULL;
+ }
+
+ /* Lookup on wwpn for NPORTs */
+ rn = csio_rn_lookup_wwpn(ln, rdevp->wwpn);
+ if (!rn)
+ goto alloc_rnode;
+
+ } else {
+ /* Lookup well-known ports with nport id */
+ if (csio_is_rnode_wka(rport_type)) {
+ match_rn = csio_rnode_lookup_portid(ln,
+ ((ntohl(*port_id) >> 8) & CSIO_DID_MASK));
+ if (match_rn == NULL) {
+ csio_rn_flowid(rn) = CSIO_INVALID_IDX;
+ goto alloc_rnode;
+ }
+
+ /*
+ * Now compare the wwpn to confirm that
+ * same port relogged in. If so update the matched rn.
+ * Else, go ahead and alloc a new rnode.
+ */
+ if (!memcmp(csio_rn_wwpn(match_rn), rdevp->wwpn, 8)) {
+ if (csio_is_rnode_ready(rn)) {
+ csio_ln_warn(ln,
+ "rnode is already"
+ "active ssni:x%x\n",
+ rdev_flowid);
+ CSIO_ASSERT(0);
+ }
+ csio_rn_flowid(rn) = CSIO_INVALID_IDX;
+ rn = match_rn;
+
+ /* Update rn */
+ goto found_rnode;
+ }
+ csio_rn_flowid(rn) = CSIO_INVALID_IDX;
+ goto alloc_rnode;
+ }
+
+ /* wwpn match */
+ if (!memcmp(csio_rn_wwpn(rn), rdevp->wwpn, 8))
+ goto found_rnode;
+
+ /* Search for rnode that have same wwpn */
+ match_rn = csio_rn_lookup_wwpn(ln, rdevp->wwpn);
+ if (match_rn != NULL) {
+ csio_ln_dbg(ln,
+ "ssni:x%x changed for rport name(wwpn):%llx "
+ "did:x%x\n", rdev_flowid,
+ wwn_to_u64(rdevp->wwpn),
+ match_rn->nport_id);
+ csio_rn_flowid(rn) = CSIO_INVALID_IDX;
+ rn = match_rn;
+ } else {
+ csio_ln_dbg(ln,
+ "rnode wwpn mismatch found ssni:x%x "
+ "name(wwpn):%llx\n",
+ rdev_flowid,
+ wwn_to_u64(csio_rn_wwpn(rn)));
+ if (csio_is_rnode_ready(rn)) {
+ csio_ln_warn(ln,
+ "rnode is already active "
+ "wwpn:%llx ssni:x%x\n",
+ wwn_to_u64(csio_rn_wwpn(rn)),
+ rdev_flowid);
+ CSIO_ASSERT(0);
+ }
+ csio_rn_flowid(rn) = CSIO_INVALID_IDX;
+ goto alloc_rnode;
+ }
+ }
+
+found_rnode:
+ csio_ln_dbg(ln, "found rnode:%p ssni:x%x name(wwpn):%llx\n",
+ rn, rdev_flowid, wwn_to_u64(rdevp->wwpn));
+
+ /* Update flowid */
+ csio_rn_flowid(rn) = rdev_flowid;
+
+ /* update rdev entry */
+ rn->rdev_entry = rdevp;
+ CSIO_INC_STATS(ln, n_rnode_match);
+ return rn;
+
+alloc_rnode:
+ rn = csio_get_rnode(ln, rdev_flowid);
+ if (!rn)
+ return NULL;
+
+ csio_ln_dbg(ln, "alloc rnode:%p ssni:x%x name(wwpn):%llx\n",
+ rn, rdev_flowid, wwn_to_u64(rdevp->wwpn));
+
+ /* update rdev entry */
+ rn->rdev_entry = rdevp;
+ return rn;
+}
+
+/*
+ * csio_rn_verify_rparams - verify rparams.
+ * @ln: lnode
+ * @rn: rnode
+ * @rdevp: remote device params
+ * returns success if rparams are verified.
+ */
+static int
+csio_rn_verify_rparams(struct csio_lnode *ln, struct csio_rnode *rn,
+ struct fcoe_rdev_entry *rdevp)
+{
+ uint8_t null[8];
+ uint8_t rport_type;
+ uint8_t fc_class;
+ __be32 *did;
+
+ did = (__be32 *) &rdevp->r_id[0];
+ rport_type =
+ FW_RDEV_WR_RPORT_TYPE_GET(rdevp->rd_xfer_rdy_to_rport_type);
+ switch (rport_type) {
+ case FLOGI_VFPORT:
+ rn->role = CSIO_RNFR_FABRIC;
+ if (((ntohl(*did) >> 8) & CSIO_DID_MASK) != FC_FID_FLOGI) {
+ csio_ln_err(ln, "ssni:x%x invalid fabric portid\n",
+ csio_rn_flowid(rn));
+ return -EINVAL;
+ }
+ /* NPIV support */
+ if (FW_RDEV_WR_NPIV_GET(rdevp->vft_to_qos))
+ ln->flags |= CSIO_LNF_NPIVSUPP;
+
+ break;
+
+ case NS_VNPORT:
+ rn->role = CSIO_RNFR_NS;
+ if (((ntohl(*did) >> 8) & CSIO_DID_MASK) != FC_FID_DIR_SERV) {
+ csio_ln_err(ln, "ssni:x%x invalid fabric portid\n",
+ csio_rn_flowid(rn));
+ return -EINVAL;
+ }
+ break;
+
+ case REG_FC4_VNPORT:
+ case REG_VNPORT:
+ rn->role = CSIO_RNFR_NPORT;
+ if (rdevp->event_cause == PRLI_ACC_RCVD ||
+ rdevp->event_cause == PRLI_RCVD) {
+ if (FW_RDEV_WR_TASK_RETRY_ID_GET(
+ rdevp->enh_disc_to_tgt))
+ rn->fcp_flags |= FCP_SPPF_OVLY_ALLOW;
+
+ if (FW_RDEV_WR_RETRY_GET(rdevp->enh_disc_to_tgt))
+ rn->fcp_flags |= FCP_SPPF_RETRY;
+
+ if (FW_RDEV_WR_CONF_CMPL_GET(rdevp->enh_disc_to_tgt))
+ rn->fcp_flags |= FCP_SPPF_CONF_COMPL;
+
+ if (FW_RDEV_WR_TGT_GET(rdevp->enh_disc_to_tgt))
+ rn->role |= CSIO_RNFR_TARGET;
+
+ if (FW_RDEV_WR_INI_GET(rdevp->enh_disc_to_tgt))
+ rn->role |= CSIO_RNFR_INITIATOR;
+ }
+
+ break;
+
+ case FDMI_VNPORT:
+ case FAB_CTLR_VNPORT:
+ rn->role = 0;
+ break;
+
+ default:
+ csio_ln_err(ln, "ssni:x%x invalid rport type recv x%x\n",
+ csio_rn_flowid(rn), rport_type);
+ return -EINVAL;
+ }
+
+ /* validate wwpn/wwnn for Name server/remote port */
+ if (rport_type == REG_VNPORT || rport_type == NS_VNPORT) {
+ memset(null, 0, 8);
+ if (!memcmp(rdevp->wwnn, null, 8)) {
+ csio_ln_err(ln,
+ "ssni:x%x invalid wwnn received from"
+ " rport did:x%x\n",
+ csio_rn_flowid(rn),
+ (ntohl(*did) & CSIO_DID_MASK));
+ return -EINVAL;
+ }
+
+ if (!memcmp(rdevp->wwpn, null, 8)) {
+ csio_ln_err(ln,
+ "ssni:x%x invalid wwpn received from"
+ " rport did:x%x\n",
+ csio_rn_flowid(rn),
+ (ntohl(*did) & CSIO_DID_MASK));
+ return -EINVAL;
+ }
+
+ }
+
+ /* Copy wwnn, wwpn and nport id */
+ rn->nport_id = (ntohl(*did) >> 8) & CSIO_DID_MASK;
+ memcpy(csio_rn_wwnn(rn), rdevp->wwnn, 8);
+ memcpy(csio_rn_wwpn(rn), rdevp->wwpn, 8);
+ rn->rn_sparm.csp.sp_bb_data = rdevp->rcv_fr_sz;
+ fc_class = FW_RDEV_WR_CLASS_GET(rdevp->vft_to_qos);
+ rn->rn_sparm.clsp[fc_class - 1].cp_class = htons(FC_CPC_VALID);
+
+ return 0;
+}
+
+static void
+__csio_reg_rnode(struct csio_rnode *rn)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ spin_unlock_irq(&hw->lock);
+ csio_reg_rnode(rn);
+ spin_lock_irq(&hw->lock);
+
+ if (rn->role & CSIO_RNFR_TARGET)
+ ln->n_scsi_tgts++;
+
+ if (rn->nport_id == FC_FID_MGMT_SERV)
+ csio_ln_fdmi_start(ln, (void *) rn);
+}
+
+static void
+__csio_unreg_rnode(struct csio_rnode *rn)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ LIST_HEAD(tmp_q);
+ int cmpl = 0;
+
+ if (!list_empty(&rn->host_cmpl_q)) {
+ csio_dbg(hw, "Returning completion queue I/Os\n");
+ list_splice_tail_init(&rn->host_cmpl_q, &tmp_q);
+ cmpl = 1;
+ }
+
+ if (rn->role & CSIO_RNFR_TARGET) {
+ ln->n_scsi_tgts--;
+ ln->last_scan_ntgts--;
+ }
+
+ spin_unlock_irq(&hw->lock);
+ csio_unreg_rnode(rn);
+ spin_lock_irq(&hw->lock);
+
+ /* Cleanup I/Os that were waiting for rnode to unregister */
+ if (cmpl)
+ csio_scsi_cleanup_io_q(csio_hw_to_scsim(hw), &tmp_q);
+
+}
+
+/*****************************************************************************/
+/* START: Rnode SM */
+/*****************************************************************************/
+
+/*
+ * csio_rns_uninit -
+ * @rn - rnode
+ * @evt - SM event.
+ *
+ */
+static void
+csio_rns_uninit(struct csio_rnode *rn, enum csio_rn_ev evt)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+ int ret = 0;
+
+ CSIO_INC_STATS(rn, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_RNFE_LOGGED_IN:
+ case CSIO_RNFE_PLOGI_RECV:
+ ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry);
+ if (!ret) {
+ csio_set_state(&rn->sm, csio_rns_ready);
+ __csio_reg_rnode(rn);
+ } else {
+ CSIO_INC_STATS(rn, n_err_inval);
+ }
+ break;
+ case CSIO_RNFE_LOGO_RECV:
+ csio_ln_dbg(ln,
+ "ssni:x%x Ignoring event %d recv "
+ "in rn state[uninit]\n", csio_rn_flowid(rn), evt);
+ CSIO_INC_STATS(rn, n_evt_drop);
+ break;
+ default:
+ csio_ln_dbg(ln,
+ "ssni:x%x unexp event %d recv "
+ "in rn state[uninit]\n", csio_rn_flowid(rn), evt);
+ CSIO_INC_STATS(rn, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_rns_ready -
+ * @rn - rnode
+ * @evt - SM event.
+ *
+ */
+static void
+csio_rns_ready(struct csio_rnode *rn, enum csio_rn_ev evt)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+ int ret = 0;
+
+ CSIO_INC_STATS(rn, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_RNFE_LOGGED_IN:
+ case CSIO_RNFE_PLOGI_RECV:
+ csio_ln_dbg(ln,
+ "ssni:x%x Ignoring event %d recv from did:x%x "
+ "in rn state[ready]\n", csio_rn_flowid(rn), evt,
+ rn->nport_id);
+ CSIO_INC_STATS(rn, n_evt_drop);
+ break;
+
+ case CSIO_RNFE_PRLI_DONE:
+ case CSIO_RNFE_PRLI_RECV:
+ ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry);
+ if (!ret)
+ __csio_reg_rnode(rn);
+ else
+ CSIO_INC_STATS(rn, n_err_inval);
+
+ break;
+ case CSIO_RNFE_DOWN:
+ csio_set_state(&rn->sm, csio_rns_offline);
+ __csio_unreg_rnode(rn);
+
+ /* FW expected to internally aborted outstanding SCSI WRs
+ * and return all SCSI WRs to host with status "ABORTED".
+ */
+ break;
+
+ case CSIO_RNFE_LOGO_RECV:
+ csio_set_state(&rn->sm, csio_rns_offline);
+
+ __csio_unreg_rnode(rn);
+
+ /* FW expected to internally aborted outstanding SCSI WRs
+ * and return all SCSI WRs to host with status "ABORTED".
+ */
+ break;
+
+ case CSIO_RNFE_CLOSE:
+ /*
+ * Each rnode receives CLOSE event when driver is removed or
+ * device is reset
+ * Note: All outstanding IOs on remote port need to returned
+ * to uppper layer with appropriate error before sending
+ * CLOSE event
+ */
+ csio_set_state(&rn->sm, csio_rns_uninit);
+ __csio_unreg_rnode(rn);
+ break;
+
+ case CSIO_RNFE_NAME_MISSING:
+ csio_set_state(&rn->sm, csio_rns_disappeared);
+ __csio_unreg_rnode(rn);
+
+ /*
+ * FW expected to internally aborted outstanding SCSI WRs
+ * and return all SCSI WRs to host with status "ABORTED".
+ */
+
+ break;
+
+ default:
+ csio_ln_dbg(ln,
+ "ssni:x%x unexp event %d recv from did:x%x "
+ "in rn state[uninit]\n", csio_rn_flowid(rn), evt,
+ rn->nport_id);
+ CSIO_INC_STATS(rn, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_rns_offline -
+ * @rn - rnode
+ * @evt - SM event.
+ *
+ */
+static void
+csio_rns_offline(struct csio_rnode *rn, enum csio_rn_ev evt)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+ int ret = 0;
+
+ CSIO_INC_STATS(rn, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_RNFE_LOGGED_IN:
+ case CSIO_RNFE_PLOGI_RECV:
+ ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry);
+ if (!ret) {
+ csio_set_state(&rn->sm, csio_rns_ready);
+ __csio_reg_rnode(rn);
+ } else {
+ CSIO_INC_STATS(rn, n_err_inval);
+ csio_post_event(&rn->sm, CSIO_RNFE_CLOSE);
+ }
+ break;
+
+ case CSIO_RNFE_DOWN:
+ csio_ln_dbg(ln,
+ "ssni:x%x Ignoring event %d recv from did:x%x "
+ "in rn state[offline]\n", csio_rn_flowid(rn), evt,
+ rn->nport_id);
+ CSIO_INC_STATS(rn, n_evt_drop);
+ break;
+
+ case CSIO_RNFE_CLOSE:
+ /* Each rnode receives CLOSE event when driver is removed or
+ * device is reset
+ * Note: All outstanding IOs on remote port need to returned
+ * to uppper layer with appropriate error before sending
+ * CLOSE event
+ */
+ csio_set_state(&rn->sm, csio_rns_uninit);
+ break;
+
+ case CSIO_RNFE_NAME_MISSING:
+ csio_set_state(&rn->sm, csio_rns_disappeared);
+ break;
+
+ default:
+ csio_ln_dbg(ln,
+ "ssni:x%x unexp event %d recv from did:x%x "
+ "in rn state[offline]\n", csio_rn_flowid(rn), evt,
+ rn->nport_id);
+ CSIO_INC_STATS(rn, n_evt_unexp);
+ break;
+ }
+}
+
+/*
+ * csio_rns_disappeared -
+ * @rn - rnode
+ * @evt - SM event.
+ *
+ */
+static void
+csio_rns_disappeared(struct csio_rnode *rn, enum csio_rn_ev evt)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+ int ret = 0;
+
+ CSIO_INC_STATS(rn, n_evt_sm[evt]);
+
+ switch (evt) {
+ case CSIO_RNFE_LOGGED_IN:
+ case CSIO_RNFE_PLOGI_RECV:
+ ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry);
+ if (!ret) {
+ csio_set_state(&rn->sm, csio_rns_ready);
+ __csio_reg_rnode(rn);
+ } else {
+ CSIO_INC_STATS(rn, n_err_inval);
+ csio_post_event(&rn->sm, CSIO_RNFE_CLOSE);
+ }
+ break;
+
+ case CSIO_RNFE_CLOSE:
+ /* Each rnode receives CLOSE event when driver is removed or
+ * device is reset.
+ * Note: All outstanding IOs on remote port need to returned
+ * to uppper layer with appropriate error before sending
+ * CLOSE event
+ */
+ csio_set_state(&rn->sm, csio_rns_uninit);
+ break;
+
+ case CSIO_RNFE_DOWN:
+ case CSIO_RNFE_NAME_MISSING:
+ csio_ln_dbg(ln,
+ "ssni:x%x Ignoring event %d recv from did x%x"
+ "in rn state[disappeared]\n", csio_rn_flowid(rn),
+ evt, rn->nport_id);
+ break;
+
+ default:
+ csio_ln_dbg(ln,
+ "ssni:x%x unexp event %d recv from did x%x"
+ "in rn state[disappeared]\n", csio_rn_flowid(rn),
+ evt, rn->nport_id);
+ CSIO_INC_STATS(rn, n_evt_unexp);
+ break;
+ }
+}
+
+/*****************************************************************************/
+/* END: Rnode SM */
+/*****************************************************************************/
+
+/*
+ * csio_rnode_devloss_handler - Device loss event handler
+ * @rn: rnode
+ *
+ * Post event to close rnode SM and free rnode.
+ */
+void
+csio_rnode_devloss_handler(struct csio_rnode *rn)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+
+ /* ignore if same rnode came back as online */
+ if (csio_is_rnode_ready(rn))
+ return;
+
+ csio_post_event(&rn->sm, CSIO_RNFE_CLOSE);
+
+ /* Free rn if in uninit state */
+ if (csio_is_rnode_uninit(rn))
+ csio_put_rnode(ln, rn);
+}
+
+/**
+ * csio_rnode_fwevt_handler - Event handler for firmware rnode events.
+ * @rn: rnode
+ *
+ */
+void
+csio_rnode_fwevt_handler(struct csio_rnode *rn, uint8_t fwevt)
+{
+ struct csio_lnode *ln = csio_rnode_to_lnode(rn);
+ enum csio_rn_ev evt;
+
+ evt = CSIO_FWE_TO_RNFE(fwevt);
+ if (!evt) {
+ csio_ln_err(ln, "ssni:x%x Unhandled FW Rdev event: %d\n",
+ csio_rn_flowid(rn), fwevt);
+ CSIO_INC_STATS(rn, n_evt_unexp);
+ return;
+ }
+ CSIO_INC_STATS(rn, n_evt_fw[fwevt]);
+
+ /* Track previous & current events for debugging */
+ rn->prev_evt = rn->cur_evt;
+ rn->cur_evt = fwevt;
+
+ /* Post event to rnode SM */
+ csio_post_event(&rn->sm, evt);
+
+ /* Free rn if in uninit state */
+ if (csio_is_rnode_uninit(rn))
+ csio_put_rnode(ln, rn);
+}
+
+/*
+ * csio_rnode_init - Initialize rnode.
+ * @rn: RNode
+ * @ln: Associated lnode
+ *
+ * Caller is responsible for holding the lock. The lock is required
+ * to be held for inserting the rnode in ln->rnhead list.
+ */
+static int
+csio_rnode_init(struct csio_rnode *rn, struct csio_lnode *ln)
+{
+ csio_rnode_to_lnode(rn) = ln;
+ csio_init_state(&rn->sm, csio_rns_uninit);
+ INIT_LIST_HEAD(&rn->host_cmpl_q);
+ csio_rn_flowid(rn) = CSIO_INVALID_IDX;
+
+ /* Add rnode to list of lnodes->rnhead */
+ list_add_tail(&rn->sm.sm_list, &ln->rnhead);
+
+ return 0;
+}
+
+static void
+csio_rnode_exit(struct csio_rnode *rn)
+{
+ list_del_init(&rn->sm.sm_list);
+ CSIO_DB_ASSERT(list_empty(&rn->host_cmpl_q));
+}
diff --git a/drivers/scsi/csiostor/csio_rnode.h b/drivers/scsi/csiostor/csio_rnode.h
new file mode 100644
index 00000000000..a3b434c801d
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_rnode.h
@@ -0,0 +1,141 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CSIO_RNODE_H__
+#define __CSIO_RNODE_H__
+
+#include "csio_defs.h"
+
+/* State machine evets */
+enum csio_rn_ev {
+ CSIO_RNFE_NONE = (uint32_t)0, /* None */
+ CSIO_RNFE_LOGGED_IN, /* [N/F]Port login
+ * complete.
+ */
+ CSIO_RNFE_PRLI_DONE, /* PRLI completed */
+ CSIO_RNFE_PLOGI_RECV, /* Received PLOGI */
+ CSIO_RNFE_PRLI_RECV, /* Received PLOGI */
+ CSIO_RNFE_LOGO_RECV, /* Received LOGO */
+ CSIO_RNFE_PRLO_RECV, /* Received PRLO */
+ CSIO_RNFE_DOWN, /* Rnode is down */
+ CSIO_RNFE_CLOSE, /* Close rnode */
+ CSIO_RNFE_NAME_MISSING, /* Rnode name missing
+ * in name server.
+ */
+ CSIO_RNFE_MAX_EVENT,
+};
+
+/* rnode stats */
+struct csio_rnode_stats {
+ uint32_t n_err; /* error */
+ uint32_t n_err_inval; /* invalid parameter */
+ uint32_t n_err_nomem; /* error nomem */
+ uint32_t n_evt_unexp; /* unexpected event */
+ uint32_t n_evt_drop; /* unexpected event */
+ uint32_t n_evt_fw[RSCN_DEV_LOST]; /* fw events */
+ enum csio_rn_ev n_evt_sm[CSIO_RNFE_MAX_EVENT]; /* State m/c events */
+ uint32_t n_lun_rst; /* Number of resets of
+ * of LUNs under this
+ * target
+ */
+ uint32_t n_lun_rst_fail; /* Number of LUN reset
+ * failures.
+ */
+ uint32_t n_tgt_rst; /* Number of target resets */
+ uint32_t n_tgt_rst_fail; /* Number of target reset
+ * failures.
+ */
+};
+
+/* Defines for rnode role */
+#define CSIO_RNFR_INITIATOR 0x1
+#define CSIO_RNFR_TARGET 0x2
+#define CSIO_RNFR_FABRIC 0x4
+#define CSIO_RNFR_NS 0x8
+#define CSIO_RNFR_NPORT 0x10
+
+struct csio_rnode {
+ struct csio_sm sm; /* State machine -
+ * should be the
+ * 1st member
+ */
+ struct csio_lnode *lnp; /* Pointer to owning
+ * Lnode */
+ uint32_t flowid; /* Firmware ID */
+ struct list_head host_cmpl_q; /* SCSI IOs
+ * pending to completed
+ * to Mid-layer.
+ */
+ /* FC identifiers for remote node */
+ uint32_t nport_id;
+ uint16_t fcp_flags; /* FCP Flags */
+ uint8_t cur_evt; /* Current event */
+ uint8_t prev_evt; /* Previous event */
+ uint32_t role; /* Fabric/Target/
+ * Initiator/NS
+ */
+ struct fcoe_rdev_entry *rdev_entry; /* Rdev entry */
+ struct csio_service_parms rn_sparm;
+
+ /* FC transport attributes */
+ struct fc_rport *rport; /* FC transport rport */
+ uint32_t supp_classes; /* Supported FC classes */
+ uint32_t maxframe_size; /* Max Frame size */
+ uint32_t scsi_id; /* Transport given SCSI id */
+
+ struct csio_rnode_stats stats; /* Common rnode stats */
+};
+
+#define csio_rn_flowid(rn) ((rn)->flowid)
+#define csio_rn_wwpn(rn) ((rn)->rn_sparm.wwpn)
+#define csio_rn_wwnn(rn) ((rn)->rn_sparm.wwnn)
+#define csio_rnode_to_lnode(rn) ((rn)->lnp)
+
+int csio_is_rnode_ready(struct csio_rnode *rn);
+void csio_rnode_state_to_str(struct csio_rnode *rn, int8_t *str);
+
+struct csio_rnode *csio_rnode_lookup_portid(struct csio_lnode *, uint32_t);
+struct csio_rnode *csio_confirm_rnode(struct csio_lnode *,
+ uint32_t, struct fcoe_rdev_entry *);
+
+void csio_rnode_fwevt_handler(struct csio_rnode *rn, uint8_t fwevt);
+
+void csio_put_rnode(struct csio_lnode *ln, struct csio_rnode *rn);
+
+void csio_reg_rnode(struct csio_rnode *);
+void csio_unreg_rnode(struct csio_rnode *);
+
+void csio_rnode_devloss_handler(struct csio_rnode *);
+
+#endif /* ifndef __CSIO_RNODE_H__ */
diff --git a/drivers/scsi/csiostor/csio_scsi.c b/drivers/scsi/csiostor/csio_scsi.c
new file mode 100644
index 00000000000..ddd38e5eb0e
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_scsi.c
@@ -0,0 +1,2555 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/compiler.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <asm/unaligned.h>
+#include <asm/page.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include "csio_hw.h"
+#include "csio_lnode.h"
+#include "csio_rnode.h"
+#include "csio_scsi.h"
+#include "csio_init.h"
+
+int csio_scsi_eqsize = 65536;
+int csio_scsi_iqlen = 128;
+int csio_scsi_ioreqs = 2048;
+uint32_t csio_max_scan_tmo;
+uint32_t csio_delta_scan_tmo = 5;
+int csio_lun_qdepth = 32;
+
+static int csio_ddp_descs = 128;
+
+static int csio_do_abrt_cls(struct csio_hw *,
+ struct csio_ioreq *, bool);
+
+static void csio_scsis_uninit(struct csio_ioreq *, enum csio_scsi_ev);
+static void csio_scsis_io_active(struct csio_ioreq *, enum csio_scsi_ev);
+static void csio_scsis_tm_active(struct csio_ioreq *, enum csio_scsi_ev);
+static void csio_scsis_aborting(struct csio_ioreq *, enum csio_scsi_ev);
+static void csio_scsis_closing(struct csio_ioreq *, enum csio_scsi_ev);
+static void csio_scsis_shost_cmpl_await(struct csio_ioreq *, enum csio_scsi_ev);
+
+/*
+ * csio_scsi_match_io - Match an ioreq with the given SCSI level data.
+ * @ioreq: The I/O request
+ * @sld: Level information
+ *
+ * Should be called with lock held.
+ *
+ */
+static bool
+csio_scsi_match_io(struct csio_ioreq *ioreq, struct csio_scsi_level_data *sld)
+{
+ struct scsi_cmnd *scmnd = csio_scsi_cmnd(ioreq);
+
+ switch (sld->level) {
+ case CSIO_LEV_LUN:
+ if (scmnd == NULL)
+ return false;
+
+ return ((ioreq->lnode == sld->lnode) &&
+ (ioreq->rnode == sld->rnode) &&
+ ((uint64_t)scmnd->device->lun == sld->oslun));
+
+ case CSIO_LEV_RNODE:
+ return ((ioreq->lnode == sld->lnode) &&
+ (ioreq->rnode == sld->rnode));
+ case CSIO_LEV_LNODE:
+ return (ioreq->lnode == sld->lnode);
+ case CSIO_LEV_ALL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * csio_scsi_gather_active_ios - Gather active I/Os based on level
+ * @scm: SCSI module
+ * @sld: Level information
+ * @dest: The queue where these I/Os have to be gathered.
+ *
+ * Should be called with lock held.
+ */
+static void
+csio_scsi_gather_active_ios(struct csio_scsim *scm,
+ struct csio_scsi_level_data *sld,
+ struct list_head *dest)
+{
+ struct list_head *tmp, *next;
+
+ if (list_empty(&scm->active_q))
+ return;
+
+ /* Just splice the entire active_q into dest */
+ if (sld->level == CSIO_LEV_ALL) {
+ list_splice_tail_init(&scm->active_q, dest);
+ return;
+ }
+
+ list_for_each_safe(tmp, next, &scm->active_q) {
+ if (csio_scsi_match_io((struct csio_ioreq *)tmp, sld)) {
+ list_del_init(tmp);
+ list_add_tail(tmp, dest);
+ }
+ }
+}
+
+static inline bool
+csio_scsi_itnexus_loss_error(uint16_t error)
+{
+ switch (error) {
+ case FW_ERR_LINK_DOWN:
+ case FW_RDEV_NOT_READY:
+ case FW_ERR_RDEV_LOST:
+ case FW_ERR_RDEV_LOGO:
+ case FW_ERR_RDEV_IMPL_LOGO:
+ return 1;
+ }
+ return 0;
+}
+
+static inline void
+csio_scsi_tag(struct scsi_cmnd *scmnd, uint8_t *tag, uint8_t hq,
+ uint8_t oq, uint8_t sq)
+{
+ char stag[2];
+
+ if (scsi_populate_tag_msg(scmnd, stag)) {
+ switch (stag[0]) {
+ case HEAD_OF_QUEUE_TAG:
+ *tag = hq;
+ break;
+ case ORDERED_QUEUE_TAG:
+ *tag = oq;
+ break;
+ default:
+ *tag = sq;
+ break;
+ }
+ } else
+ *tag = 0;
+}
+
+/*
+ * csio_scsi_fcp_cmnd - Frame the SCSI FCP command paylod.
+ * @req: IO req structure.
+ * @addr: DMA location to place the payload.
+ *
+ * This routine is shared between FCP_WRITE, FCP_READ and FCP_CMD requests.
+ */
+static inline void
+csio_scsi_fcp_cmnd(struct csio_ioreq *req, void *addr)
+{
+ struct fcp_cmnd *fcp_cmnd = (struct fcp_cmnd *)addr;
+ struct scsi_cmnd *scmnd = csio_scsi_cmnd(req);
+
+ /* Check for Task Management */
+ if (likely(scmnd->SCp.Message == 0)) {
+ int_to_scsilun(scmnd->device->lun, &fcp_cmnd->fc_lun);
+ fcp_cmnd->fc_tm_flags = 0;
+ fcp_cmnd->fc_cmdref = 0;
+ fcp_cmnd->fc_pri_ta = 0;
+
+ memcpy(fcp_cmnd->fc_cdb, scmnd->cmnd, 16);
+ csio_scsi_tag(scmnd, &fcp_cmnd->fc_pri_ta,
+ FCP_PTA_HEADQ, FCP_PTA_ORDERED, FCP_PTA_SIMPLE);
+ fcp_cmnd->fc_dl = cpu_to_be32(scsi_bufflen(scmnd));
+
+ if (req->nsge)
+ if (req->datadir == DMA_TO_DEVICE)
+ fcp_cmnd->fc_flags = FCP_CFL_WRDATA;
+ else
+ fcp_cmnd->fc_flags = FCP_CFL_RDDATA;
+ else
+ fcp_cmnd->fc_flags = 0;
+ } else {
+ memset(fcp_cmnd, 0, sizeof(*fcp_cmnd));
+ int_to_scsilun(scmnd->device->lun, &fcp_cmnd->fc_lun);
+ fcp_cmnd->fc_tm_flags = (uint8_t)scmnd->SCp.Message;
+ }
+}
+
+/*
+ * csio_scsi_init_cmd_wr - Initialize the SCSI CMD WR.
+ * @req: IO req structure.
+ * @addr: DMA location to place the payload.
+ * @size: Size of WR (including FW WR + immed data + rsp SG entry
+ *
+ * Wrapper for populating fw_scsi_cmd_wr.
+ */
+static inline void
+csio_scsi_init_cmd_wr(struct csio_ioreq *req, void *addr, uint32_t size)
+{
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_rnode *rn = req->rnode;
+ struct fw_scsi_cmd_wr *wr = (struct fw_scsi_cmd_wr *)addr;
+ struct csio_dma_buf *dma_buf;
+ uint8_t imm = csio_hw_to_scsim(hw)->proto_cmd_len;
+
+ wr->op_immdlen = cpu_to_be32(FW_WR_OP(FW_SCSI_CMD_WR) |
+ FW_SCSI_CMD_WR_IMMDLEN(imm));
+ wr->flowid_len16 = cpu_to_be32(FW_WR_FLOWID(rn->flowid) |
+ FW_WR_LEN16(
+ DIV_ROUND_UP(size, 16)));
+
+ wr->cookie = (uintptr_t) req;
+ wr->iqid = cpu_to_be16(csio_q_physiqid(hw, req->iq_idx));
+ wr->tmo_val = (uint8_t) req->tmo;
+ wr->r3 = 0;
+ memset(&wr->r5, 0, 8);
+
+ /* Get RSP DMA buffer */
+ dma_buf = &req->dma_buf;
+
+ /* Prepare RSP SGL */
+ wr->rsp_dmalen = cpu_to_be32(dma_buf->len);
+ wr->rsp_dmaaddr = cpu_to_be64(dma_buf->paddr);
+
+ wr->r6 = 0;
+
+ wr->u.fcoe.ctl_pri = 0;
+ wr->u.fcoe.cp_en_class = 0;
+ wr->u.fcoe.r4_lo[0] = 0;
+ wr->u.fcoe.r4_lo[1] = 0;
+
+ /* Frame a FCP command */
+ csio_scsi_fcp_cmnd(req, (void *)((uintptr_t)addr +
+ sizeof(struct fw_scsi_cmd_wr)));
+}
+
+#define CSIO_SCSI_CMD_WR_SZ(_imm) \
+ (sizeof(struct fw_scsi_cmd_wr) + /* WR size */ \
+ ALIGN((_imm), 16)) /* Immed data */
+
+#define CSIO_SCSI_CMD_WR_SZ_16(_imm) \
+ (ALIGN(CSIO_SCSI_CMD_WR_SZ((_imm)), 16))
+
+/*
+ * csio_scsi_cmd - Create a SCSI CMD WR.
+ * @req: IO req structure.
+ *
+ * Gets a WR slot in the ingress queue and initializes it with SCSI CMD WR.
+ *
+ */
+static inline void
+csio_scsi_cmd(struct csio_ioreq *req)
+{
+ struct csio_wr_pair wrp;
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_scsim *scsim = csio_hw_to_scsim(hw);
+ uint32_t size = CSIO_SCSI_CMD_WR_SZ_16(scsim->proto_cmd_len);
+
+ req->drv_status = csio_wr_get(hw, req->eq_idx, size, &wrp);
+ if (unlikely(req->drv_status != 0))
+ return;
+
+ if (wrp.size1 >= size) {
+ /* Initialize WR in one shot */
+ csio_scsi_init_cmd_wr(req, wrp.addr1, size);
+ } else {
+ uint8_t *tmpwr = csio_q_eq_wrap(hw, req->eq_idx);
+
+ /*
+ * Make a temporary copy of the WR and write back
+ * the copy into the WR pair.
+ */
+ csio_scsi_init_cmd_wr(req, (void *)tmpwr, size);
+ memcpy(wrp.addr1, tmpwr, wrp.size1);
+ memcpy(wrp.addr2, tmpwr + wrp.size1, size - wrp.size1);
+ }
+}
+
+/*
+ * csio_scsi_init_ulptx_dsgl - Fill in a ULP_TX_SC_DSGL
+ * @hw: HW module
+ * @req: IO request
+ * @sgl: ULP TX SGL pointer.
+ *
+ */
+static inline void
+csio_scsi_init_ultptx_dsgl(struct csio_hw *hw, struct csio_ioreq *req,
+ struct ulptx_sgl *sgl)
+{
+ struct ulptx_sge_pair *sge_pair = NULL;
+ struct scatterlist *sgel;
+ uint32_t i = 0;
+ uint32_t xfer_len;
+ struct list_head *tmp;
+ struct csio_dma_buf *dma_buf;
+ struct scsi_cmnd *scmnd = csio_scsi_cmnd(req);
+
+ sgl->cmd_nsge = htonl(ULPTX_CMD(ULP_TX_SC_DSGL) | ULPTX_MORE |
+ ULPTX_NSGE(req->nsge));
+ /* Now add the data SGLs */
+ if (likely(!req->dcopy)) {
+ scsi_for_each_sg(scmnd, sgel, req->nsge, i) {
+ if (i == 0) {
+ sgl->addr0 = cpu_to_be64(sg_dma_address(sgel));
+ sgl->len0 = cpu_to_be32(sg_dma_len(sgel));
+ sge_pair = (struct ulptx_sge_pair *)(sgl + 1);
+ continue;
+ }
+ if ((i - 1) & 0x1) {
+ sge_pair->addr[1] = cpu_to_be64(
+ sg_dma_address(sgel));
+ sge_pair->len[1] = cpu_to_be32(
+ sg_dma_len(sgel));
+ sge_pair++;
+ } else {
+ sge_pair->addr[0] = cpu_to_be64(
+ sg_dma_address(sgel));
+ sge_pair->len[0] = cpu_to_be32(
+ sg_dma_len(sgel));
+ }
+ }
+ } else {
+ /* Program sg elements with driver's DDP buffer */
+ xfer_len = scsi_bufflen(scmnd);
+ list_for_each(tmp, &req->gen_list) {
+ dma_buf = (struct csio_dma_buf *)tmp;
+ if (i == 0) {
+ sgl->addr0 = cpu_to_be64(dma_buf->paddr);
+ sgl->len0 = cpu_to_be32(
+ min(xfer_len, dma_buf->len));
+ sge_pair = (struct ulptx_sge_pair *)(sgl + 1);
+ } else if ((i - 1) & 0x1) {
+ sge_pair->addr[1] = cpu_to_be64(dma_buf->paddr);
+ sge_pair->len[1] = cpu_to_be32(
+ min(xfer_len, dma_buf->len));
+ sge_pair++;
+ } else {
+ sge_pair->addr[0] = cpu_to_be64(dma_buf->paddr);
+ sge_pair->len[0] = cpu_to_be32(
+ min(xfer_len, dma_buf->len));
+ }
+ xfer_len -= min(xfer_len, dma_buf->len);
+ i++;
+ }
+ }
+}
+
+/*
+ * csio_scsi_init_read_wr - Initialize the READ SCSI WR.
+ * @req: IO req structure.
+ * @wrp: DMA location to place the payload.
+ * @size: Size of WR (including FW WR + immed data + rsp SG entry + data SGL
+ *
+ * Wrapper for populating fw_scsi_read_wr.
+ */
+static inline void
+csio_scsi_init_read_wr(struct csio_ioreq *req, void *wrp, uint32_t size)
+{
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_rnode *rn = req->rnode;
+ struct fw_scsi_read_wr *wr = (struct fw_scsi_read_wr *)wrp;
+ struct ulptx_sgl *sgl;
+ struct csio_dma_buf *dma_buf;
+ uint8_t imm = csio_hw_to_scsim(hw)->proto_cmd_len;
+ struct scsi_cmnd *scmnd = csio_scsi_cmnd(req);
+
+ wr->op_immdlen = cpu_to_be32(FW_WR_OP(FW_SCSI_READ_WR) |
+ FW_SCSI_READ_WR_IMMDLEN(imm));
+ wr->flowid_len16 = cpu_to_be32(FW_WR_FLOWID(rn->flowid) |
+ FW_WR_LEN16(DIV_ROUND_UP(size, 16)));
+ wr->cookie = (uintptr_t)req;
+ wr->iqid = cpu_to_be16(csio_q_physiqid(hw, req->iq_idx));
+ wr->tmo_val = (uint8_t)(req->tmo);
+ wr->use_xfer_cnt = 1;
+ wr->xfer_cnt = cpu_to_be32(scsi_bufflen(scmnd));
+ wr->ini_xfer_cnt = cpu_to_be32(scsi_bufflen(scmnd));
+ /* Get RSP DMA buffer */
+ dma_buf = &req->dma_buf;
+
+ /* Prepare RSP SGL */
+ wr->rsp_dmalen = cpu_to_be32(dma_buf->len);
+ wr->rsp_dmaaddr = cpu_to_be64(dma_buf->paddr);
+
+ wr->r4 = 0;
+
+ wr->u.fcoe.ctl_pri = 0;
+ wr->u.fcoe.cp_en_class = 0;
+ wr->u.fcoe.r3_lo[0] = 0;
+ wr->u.fcoe.r3_lo[1] = 0;
+ csio_scsi_fcp_cmnd(req, (void *)((uintptr_t)wrp +
+ sizeof(struct fw_scsi_read_wr)));
+
+ /* Move WR pointer past command and immediate data */
+ sgl = (struct ulptx_sgl *)((uintptr_t)wrp +
+ sizeof(struct fw_scsi_read_wr) + ALIGN(imm, 16));
+
+ /* Fill in the DSGL */
+ csio_scsi_init_ultptx_dsgl(hw, req, sgl);
+}
+
+/*
+ * csio_scsi_init_write_wr - Initialize the WRITE SCSI WR.
+ * @req: IO req structure.
+ * @wrp: DMA location to place the payload.
+ * @size: Size of WR (including FW WR + immed data + rsp SG entry + data SGL
+ *
+ * Wrapper for populating fw_scsi_write_wr.
+ */
+static inline void
+csio_scsi_init_write_wr(struct csio_ioreq *req, void *wrp, uint32_t size)
+{
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_rnode *rn = req->rnode;
+ struct fw_scsi_write_wr *wr = (struct fw_scsi_write_wr *)wrp;
+ struct ulptx_sgl *sgl;
+ struct csio_dma_buf *dma_buf;
+ uint8_t imm = csio_hw_to_scsim(hw)->proto_cmd_len;
+ struct scsi_cmnd *scmnd = csio_scsi_cmnd(req);
+
+ wr->op_immdlen = cpu_to_be32(FW_WR_OP(FW_SCSI_WRITE_WR) |
+ FW_SCSI_WRITE_WR_IMMDLEN(imm));
+ wr->flowid_len16 = cpu_to_be32(FW_WR_FLOWID(rn->flowid) |
+ FW_WR_LEN16(DIV_ROUND_UP(size, 16)));
+ wr->cookie = (uintptr_t)req;
+ wr->iqid = cpu_to_be16(csio_q_physiqid(hw, req->iq_idx));
+ wr->tmo_val = (uint8_t)(req->tmo);
+ wr->use_xfer_cnt = 1;
+ wr->xfer_cnt = cpu_to_be32(scsi_bufflen(scmnd));
+ wr->ini_xfer_cnt = cpu_to_be32(scsi_bufflen(scmnd));
+ /* Get RSP DMA buffer */
+ dma_buf = &req->dma_buf;
+
+ /* Prepare RSP SGL */
+ wr->rsp_dmalen = cpu_to_be32(dma_buf->len);
+ wr->rsp_dmaaddr = cpu_to_be64(dma_buf->paddr);
+
+ wr->r4 = 0;
+
+ wr->u.fcoe.ctl_pri = 0;
+ wr->u.fcoe.cp_en_class = 0;
+ wr->u.fcoe.r3_lo[0] = 0;
+ wr->u.fcoe.r3_lo[1] = 0;
+ csio_scsi_fcp_cmnd(req, (void *)((uintptr_t)wrp +
+ sizeof(struct fw_scsi_write_wr)));
+
+ /* Move WR pointer past command and immediate data */
+ sgl = (struct ulptx_sgl *)((uintptr_t)wrp +
+ sizeof(struct fw_scsi_write_wr) + ALIGN(imm, 16));
+
+ /* Fill in the DSGL */
+ csio_scsi_init_ultptx_dsgl(hw, req, sgl);
+}
+
+/* Calculate WR size needed for fw_scsi_read_wr/fw_scsi_write_wr */
+#define CSIO_SCSI_DATA_WRSZ(req, oper, sz, imm) \
+do { \
+ (sz) = sizeof(struct fw_scsi_##oper##_wr) + /* WR size */ \
+ ALIGN((imm), 16) + /* Immed data */ \
+ sizeof(struct ulptx_sgl); /* ulptx_sgl */ \
+ \
+ if (unlikely((req)->nsge > 1)) \
+ (sz) += (sizeof(struct ulptx_sge_pair) * \
+ (ALIGN(((req)->nsge - 1), 2) / 2)); \
+ /* Data SGE */ \
+} while (0)
+
+/*
+ * csio_scsi_read - Create a SCSI READ WR.
+ * @req: IO req structure.
+ *
+ * Gets a WR slot in the ingress queue and initializes it with
+ * SCSI READ WR.
+ *
+ */
+static inline void
+csio_scsi_read(struct csio_ioreq *req)
+{
+ struct csio_wr_pair wrp;
+ uint32_t size;
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_scsim *scsim = csio_hw_to_scsim(hw);
+
+ CSIO_SCSI_DATA_WRSZ(req, read, size, scsim->proto_cmd_len);
+ size = ALIGN(size, 16);
+
+ req->drv_status = csio_wr_get(hw, req->eq_idx, size, &wrp);
+ if (likely(req->drv_status == 0)) {
+ if (likely(wrp.size1 >= size)) {
+ /* Initialize WR in one shot */
+ csio_scsi_init_read_wr(req, wrp.addr1, size);
+ } else {
+ uint8_t *tmpwr = csio_q_eq_wrap(hw, req->eq_idx);
+ /*
+ * Make a temporary copy of the WR and write back
+ * the copy into the WR pair.
+ */
+ csio_scsi_init_read_wr(req, (void *)tmpwr, size);
+ memcpy(wrp.addr1, tmpwr, wrp.size1);
+ memcpy(wrp.addr2, tmpwr + wrp.size1, size - wrp.size1);
+ }
+ }
+}
+
+/*
+ * csio_scsi_write - Create a SCSI WRITE WR.
+ * @req: IO req structure.
+ *
+ * Gets a WR slot in the ingress queue and initializes it with
+ * SCSI WRITE WR.
+ *
+ */
+static inline void
+csio_scsi_write(struct csio_ioreq *req)
+{
+ struct csio_wr_pair wrp;
+ uint32_t size;
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_scsim *scsim = csio_hw_to_scsim(hw);
+
+ CSIO_SCSI_DATA_WRSZ(req, write, size, scsim->proto_cmd_len);
+ size = ALIGN(size, 16);
+
+ req->drv_status = csio_wr_get(hw, req->eq_idx, size, &wrp);
+ if (likely(req->drv_status == 0)) {
+ if (likely(wrp.size1 >= size)) {
+ /* Initialize WR in one shot */
+ csio_scsi_init_write_wr(req, wrp.addr1, size);
+ } else {
+ uint8_t *tmpwr = csio_q_eq_wrap(hw, req->eq_idx);
+ /*
+ * Make a temporary copy of the WR and write back
+ * the copy into the WR pair.
+ */
+ csio_scsi_init_write_wr(req, (void *)tmpwr, size);
+ memcpy(wrp.addr1, tmpwr, wrp.size1);
+ memcpy(wrp.addr2, tmpwr + wrp.size1, size - wrp.size1);
+ }
+ }
+}
+
+/*
+ * csio_setup_ddp - Setup DDP buffers for Read request.
+ * @req: IO req structure.
+ *
+ * Checks SGLs/Data buffers are virtually contiguous required for DDP.
+ * If contiguous,driver posts SGLs in the WR otherwise post internal
+ * buffers for such request for DDP.
+ */
+static inline void
+csio_setup_ddp(struct csio_scsim *scsim, struct csio_ioreq *req)
+{
+#ifdef __CSIO_DEBUG__
+ struct csio_hw *hw = req->lnode->hwp;
+#endif
+ struct scatterlist *sgel = NULL;
+ struct scsi_cmnd *scmnd = csio_scsi_cmnd(req);
+ uint64_t sg_addr = 0;
+ uint32_t ddp_pagesz = 4096;
+ uint32_t buf_off;
+ struct csio_dma_buf *dma_buf = NULL;
+ uint32_t alloc_len = 0;
+ uint32_t xfer_len = 0;
+ uint32_t sg_len = 0;
+ uint32_t i;
+
+ scsi_for_each_sg(scmnd, sgel, req->nsge, i) {
+ sg_addr = sg_dma_address(sgel);
+ sg_len = sg_dma_len(sgel);
+
+ buf_off = sg_addr & (ddp_pagesz - 1);
+
+ /* Except 1st buffer,all buffer addr have to be Page aligned */
+ if (i != 0 && buf_off) {
+ csio_dbg(hw, "SGL addr not DDP aligned (%llx:%d)\n",
+ sg_addr, sg_len);
+ goto unaligned;
+ }
+
+ /* Except last buffer,all buffer must end on page boundary */
+ if ((i != (req->nsge - 1)) &&
+ ((buf_off + sg_len) & (ddp_pagesz - 1))) {
+ csio_dbg(hw,
+ "SGL addr not ending on page boundary"
+ "(%llx:%d)\n", sg_addr, sg_len);
+ goto unaligned;
+ }
+ }
+
+ /* SGL's are virtually contiguous. HW will DDP to SGLs */
+ req->dcopy = 0;
+ csio_scsi_read(req);
+
+ return;
+
+unaligned:
+ CSIO_INC_STATS(scsim, n_unaligned);
+ /*
+ * For unaligned SGLs, driver will allocate internal DDP buffer.
+ * Once command is completed data from DDP buffer copied to SGLs
+ */
+ req->dcopy = 1;
+
+ /* Use gen_list to store the DDP buffers */
+ INIT_LIST_HEAD(&req->gen_list);
+ xfer_len = scsi_bufflen(scmnd);
+
+ i = 0;
+ /* Allocate ddp buffers for this request */
+ while (alloc_len < xfer_len) {
+ dma_buf = csio_get_scsi_ddp(scsim);
+ if (dma_buf == NULL || i > scsim->max_sge) {
+ req->drv_status = -EBUSY;
+ break;
+ }
+ alloc_len += dma_buf->len;
+ /* Added to IO req */
+ list_add_tail(&dma_buf->list, &req->gen_list);
+ i++;
+ }
+
+ if (!req->drv_status) {
+ /* set number of ddp bufs used */
+ req->nsge = i;
+ csio_scsi_read(req);
+ return;
+ }
+
+ /* release dma descs */
+ if (i > 0)
+ csio_put_scsi_ddp_list(scsim, &req->gen_list, i);
+}
+
+/*
+ * csio_scsi_init_abrt_cls_wr - Initialize an ABORT/CLOSE WR.
+ * @req: IO req structure.
+ * @addr: DMA location to place the payload.
+ * @size: Size of WR
+ * @abort: abort OR close
+ *
+ * Wrapper for populating fw_scsi_cmd_wr.
+ */
+static inline void
+csio_scsi_init_abrt_cls_wr(struct csio_ioreq *req, void *addr, uint32_t size,
+ bool abort)
+{
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_rnode *rn = req->rnode;
+ struct fw_scsi_abrt_cls_wr *wr = (struct fw_scsi_abrt_cls_wr *)addr;
+
+ wr->op_immdlen = cpu_to_be32(FW_WR_OP(FW_SCSI_ABRT_CLS_WR));
+ wr->flowid_len16 = cpu_to_be32(FW_WR_FLOWID(rn->flowid) |
+ FW_WR_LEN16(
+ DIV_ROUND_UP(size, 16)));
+
+ wr->cookie = (uintptr_t) req;
+ wr->iqid = cpu_to_be16(csio_q_physiqid(hw, req->iq_idx));
+ wr->tmo_val = (uint8_t) req->tmo;
+ /* 0 for CHK_ALL_IO tells FW to look up t_cookie */
+ wr->sub_opcode_to_chk_all_io =
+ (FW_SCSI_ABRT_CLS_WR_SUB_OPCODE(abort) |
+ FW_SCSI_ABRT_CLS_WR_CHK_ALL_IO(0));
+ wr->r3[0] = 0;
+ wr->r3[1] = 0;
+ wr->r3[2] = 0;
+ wr->r3[3] = 0;
+ /* Since we re-use the same ioreq for abort as well */
+ wr->t_cookie = (uintptr_t) req;
+}
+
+static inline void
+csio_scsi_abrt_cls(struct csio_ioreq *req, bool abort)
+{
+ struct csio_wr_pair wrp;
+ struct csio_hw *hw = req->lnode->hwp;
+ uint32_t size = ALIGN(sizeof(struct fw_scsi_abrt_cls_wr), 16);
+
+ req->drv_status = csio_wr_get(hw, req->eq_idx, size, &wrp);
+ if (req->drv_status != 0)
+ return;
+
+ if (wrp.size1 >= size) {
+ /* Initialize WR in one shot */
+ csio_scsi_init_abrt_cls_wr(req, wrp.addr1, size, abort);
+ } else {
+ uint8_t *tmpwr = csio_q_eq_wrap(hw, req->eq_idx);
+ /*
+ * Make a temporary copy of the WR and write back
+ * the copy into the WR pair.
+ */
+ csio_scsi_init_abrt_cls_wr(req, (void *)tmpwr, size, abort);
+ memcpy(wrp.addr1, tmpwr, wrp.size1);
+ memcpy(wrp.addr2, tmpwr + wrp.size1, size - wrp.size1);
+ }
+}
+
+/*****************************************************************************/
+/* START: SCSI SM */
+/*****************************************************************************/
+static void
+csio_scsis_uninit(struct csio_ioreq *req, enum csio_scsi_ev evt)
+{
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_scsim *scsim = csio_hw_to_scsim(hw);
+
+ switch (evt) {
+ case CSIO_SCSIE_START_IO:
+
+ if (req->nsge) {
+ if (req->datadir == DMA_TO_DEVICE) {
+ req->dcopy = 0;
+ csio_scsi_write(req);
+ } else
+ csio_setup_ddp(scsim, req);
+ } else {
+ csio_scsi_cmd(req);
+ }
+
+ if (likely(req->drv_status == 0)) {
+ /* change state and enqueue on active_q */
+ csio_set_state(&req->sm, csio_scsis_io_active);
+ list_add_tail(&req->sm.sm_list, &scsim->active_q);
+ csio_wr_issue(hw, req->eq_idx, false);
+ CSIO_INC_STATS(scsim, n_active);
+
+ return;
+ }
+ break;
+
+ case CSIO_SCSIE_START_TM:
+ csio_scsi_cmd(req);
+ if (req->drv_status == 0) {
+ /*
+ * NOTE: We collect the affected I/Os prior to issuing
+ * LUN reset, and not after it. This is to prevent
+ * aborting I/Os that get issued after the LUN reset,
+ * but prior to LUN reset completion (in the event that
+ * the host stack has not blocked I/Os to a LUN that is
+ * being reset.
+ */
+ csio_set_state(&req->sm, csio_scsis_tm_active);
+ list_add_tail(&req->sm.sm_list, &scsim->active_q);
+ csio_wr_issue(hw, req->eq_idx, false);
+ CSIO_INC_STATS(scsim, n_tm_active);
+ }
+ return;
+
+ case CSIO_SCSIE_ABORT:
+ case CSIO_SCSIE_CLOSE:
+ /*
+ * NOTE:
+ * We could get here due to :
+ * - a window in the cleanup path of the SCSI module
+ * (csio_scsi_abort_io()). Please see NOTE in this function.
+ * - a window in the time we tried to issue an abort/close
+ * of a request to FW, and the FW completed the request
+ * itself.
+ * Print a message for now, and return INVAL either way.
+ */
+ req->drv_status = -EINVAL;
+ csio_warn(hw, "Trying to abort/close completed IO:%p!\n", req);
+ break;
+
+ default:
+ csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req);
+ CSIO_DB_ASSERT(0);
+ }
+}
+
+static void
+csio_scsis_io_active(struct csio_ioreq *req, enum csio_scsi_ev evt)
+{
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_scsim *scm = csio_hw_to_scsim(hw);
+ struct csio_rnode *rn;
+
+ switch (evt) {
+ case CSIO_SCSIE_COMPLETED:
+ CSIO_DEC_STATS(scm, n_active);
+ list_del_init(&req->sm.sm_list);
+ csio_set_state(&req->sm, csio_scsis_uninit);
+ /*
+ * In MSIX mode, with multiple queues, the SCSI compeltions
+ * could reach us sooner than the FW events sent to indicate
+ * I-T nexus loss (link down, remote device logo etc). We
+ * dont want to be returning such I/Os to the upper layer
+ * immediately, since we wouldnt have reported the I-T nexus
+ * loss itself. This forces us to serialize such completions
+ * with the reporting of the I-T nexus loss. Therefore, we
+ * internally queue up such up such completions in the rnode.
+ * The reporting of I-T nexus loss to the upper layer is then
+ * followed by the returning of I/Os in this internal queue.
+ * Having another state alongwith another queue helps us take
+ * actions for events such as ABORT received while we are
+ * in this rnode queue.
+ */
+ if (unlikely(req->wr_status != FW_SUCCESS)) {
+ rn = req->rnode;
+ /*
+ * FW says remote device is lost, but rnode
+ * doesnt reflect it.
+ */
+ if (csio_scsi_itnexus_loss_error(req->wr_status) &&
+ csio_is_rnode_ready(rn)) {
+ csio_set_state(&req->sm,
+ csio_scsis_shost_cmpl_await);
+ list_add_tail(&req->sm.sm_list,
+ &rn->host_cmpl_q);
+ }
+ }
+
+ break;
+
+ case CSIO_SCSIE_ABORT:
+ csio_scsi_abrt_cls(req, SCSI_ABORT);
+ if (req->drv_status == 0) {
+ csio_wr_issue(hw, req->eq_idx, false);
+ csio_set_state(&req->sm, csio_scsis_aborting);
+ }
+ break;
+
+ case CSIO_SCSIE_CLOSE:
+ csio_scsi_abrt_cls(req, SCSI_CLOSE);
+ if (req->drv_status == 0) {
+ csio_wr_issue(hw, req->eq_idx, false);
+ csio_set_state(&req->sm, csio_scsis_closing);
+ }
+ break;
+
+ case CSIO_SCSIE_DRVCLEANUP:
+ req->wr_status = FW_HOSTERROR;
+ CSIO_DEC_STATS(scm, n_active);
+ csio_set_state(&req->sm, csio_scsis_uninit);
+ break;
+
+ default:
+ csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req);
+ CSIO_DB_ASSERT(0);
+ }
+}
+
+static void
+csio_scsis_tm_active(struct csio_ioreq *req, enum csio_scsi_ev evt)
+{
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_scsim *scm = csio_hw_to_scsim(hw);
+
+ switch (evt) {
+ case CSIO_SCSIE_COMPLETED:
+ CSIO_DEC_STATS(scm, n_tm_active);
+ list_del_init(&req->sm.sm_list);
+ csio_set_state(&req->sm, csio_scsis_uninit);
+
+ break;
+
+ case CSIO_SCSIE_ABORT:
+ csio_scsi_abrt_cls(req, SCSI_ABORT);
+ if (req->drv_status == 0) {
+ csio_wr_issue(hw, req->eq_idx, false);
+ csio_set_state(&req->sm, csio_scsis_aborting);
+ }
+ break;
+
+
+ case CSIO_SCSIE_CLOSE:
+ csio_scsi_abrt_cls(req, SCSI_CLOSE);
+ if (req->drv_status == 0) {
+ csio_wr_issue(hw, req->eq_idx, false);
+ csio_set_state(&req->sm, csio_scsis_closing);
+ }
+ break;
+
+ case CSIO_SCSIE_DRVCLEANUP:
+ req->wr_status = FW_HOSTERROR;
+ CSIO_DEC_STATS(scm, n_tm_active);
+ csio_set_state(&req->sm, csio_scsis_uninit);
+ break;
+
+ default:
+ csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req);
+ CSIO_DB_ASSERT(0);
+ }
+}
+
+static void
+csio_scsis_aborting(struct csio_ioreq *req, enum csio_scsi_ev evt)
+{
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_scsim *scm = csio_hw_to_scsim(hw);
+
+ switch (evt) {
+ case CSIO_SCSIE_COMPLETED:
+ csio_dbg(hw,
+ "ioreq %p recvd cmpltd (wr_status:%d) "
+ "in aborting st\n", req, req->wr_status);
+ /*
+ * Use -ECANCELED to explicitly tell the ABORTED event that
+ * the original I/O was returned to driver by FW.
+ * We dont really care if the I/O was returned with success by
+ * FW (because the ABORT and completion of the I/O crossed each
+ * other), or any other return value. Once we are in aborting
+ * state, the success or failure of the I/O is unimportant to
+ * us.
+ */
+ req->drv_status = -ECANCELED;
+ break;
+
+ case CSIO_SCSIE_ABORT:
+ CSIO_INC_STATS(scm, n_abrt_dups);
+ break;
+
+ case CSIO_SCSIE_ABORTED:
+
+ csio_dbg(hw, "abort of %p return status:0x%x drv_status:%x\n",
+ req, req->wr_status, req->drv_status);
+ /*
+ * Check if original I/O WR completed before the Abort
+ * completion.
+ */
+ if (req->drv_status != -ECANCELED) {
+ csio_warn(hw,
+ "Abort completed before original I/O,"
+ " req:%p\n", req);
+ CSIO_DB_ASSERT(0);
+ }
+
+ /*
+ * There are the following possible scenarios:
+ * 1. The abort completed successfully, FW returned FW_SUCCESS.
+ * 2. The completion of an I/O and the receipt of
+ * abort for that I/O by the FW crossed each other.
+ * The FW returned FW_EINVAL. The original I/O would have
+ * returned with FW_SUCCESS or any other SCSI error.
+ * 3. The FW couldnt sent the abort out on the wire, as there
+ * was an I-T nexus loss (link down, remote device logged
+ * out etc). FW sent back an appropriate IT nexus loss status
+ * for the abort.
+ * 4. FW sent an abort, but abort timed out (remote device
+ * didnt respond). FW replied back with
+ * FW_SCSI_ABORT_TIMEDOUT.
+ * 5. FW couldnt genuinely abort the request for some reason,
+ * and sent us an error.
+ *
+ * The first 3 scenarios are treated as succesful abort
+ * operations by the host, while the last 2 are failed attempts
+ * to abort. Manipulate the return value of the request
+ * appropriately, so that host can convey these results
+ * back to the upper layer.
+ */
+ if ((req->wr_status == FW_SUCCESS) ||
+ (req->wr_status == FW_EINVAL) ||
+ csio_scsi_itnexus_loss_error(req->wr_status))
+ req->wr_status = FW_SCSI_ABORT_REQUESTED;
+
+ CSIO_DEC_STATS(scm, n_active);
+ list_del_init(&req->sm.sm_list);
+ csio_set_state(&req->sm, csio_scsis_uninit);
+ break;
+
+ case CSIO_SCSIE_DRVCLEANUP:
+ req->wr_status = FW_HOSTERROR;
+ CSIO_DEC_STATS(scm, n_active);
+ csio_set_state(&req->sm, csio_scsis_uninit);
+ break;
+
+ case CSIO_SCSIE_CLOSE:
+ /*
+ * We can receive this event from the module
+ * cleanup paths, if the FW forgot to reply to the ABORT WR
+ * and left this ioreq in this state. For now, just ignore
+ * the event. The CLOSE event is sent to this state, as
+ * the LINK may have already gone down.
+ */
+ break;
+
+ default:
+ csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req);
+ CSIO_DB_ASSERT(0);
+ }
+}
+
+static void
+csio_scsis_closing(struct csio_ioreq *req, enum csio_scsi_ev evt)
+{
+ struct csio_hw *hw = req->lnode->hwp;
+ struct csio_scsim *scm = csio_hw_to_scsim(hw);
+
+ switch (evt) {
+ case CSIO_SCSIE_COMPLETED:
+ csio_dbg(hw,
+ "ioreq %p recvd cmpltd (wr_status:%d) "
+ "in closing st\n", req, req->wr_status);
+ /*
+ * Use -ECANCELED to explicitly tell the CLOSED event that
+ * the original I/O was returned to driver by FW.
+ * We dont really care if the I/O was returned with success by
+ * FW (because the CLOSE and completion of the I/O crossed each
+ * other), or any other return value. Once we are in aborting
+ * state, the success or failure of the I/O is unimportant to
+ * us.
+ */
+ req->drv_status = -ECANCELED;
+ break;
+
+ case CSIO_SCSIE_CLOSED:
+ /*
+ * Check if original I/O WR completed before the Close
+ * completion.
+ */
+ if (req->drv_status != -ECANCELED) {
+ csio_fatal(hw,
+ "Close completed before original I/O,"
+ " req:%p\n", req);
+ CSIO_DB_ASSERT(0);
+ }
+
+ /*
+ * Either close succeeded, or we issued close to FW at the
+ * same time FW compelted it to us. Either way, the I/O
+ * is closed.
+ */
+ CSIO_DB_ASSERT((req->wr_status == FW_SUCCESS) ||
+ (req->wr_status == FW_EINVAL));
+ req->wr_status = FW_SCSI_CLOSE_REQUESTED;
+
+ CSIO_DEC_STATS(scm, n_active);
+ list_del_init(&req->sm.sm_list);
+ csio_set_state(&req->sm, csio_scsis_uninit);
+ break;
+
+ case CSIO_SCSIE_CLOSE:
+ break;
+
+ case CSIO_SCSIE_DRVCLEANUP:
+ req->wr_status = FW_HOSTERROR;
+ CSIO_DEC_STATS(scm, n_active);
+ csio_set_state(&req->sm, csio_scsis_uninit);
+ break;
+
+ default:
+ csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req);
+ CSIO_DB_ASSERT(0);
+ }
+}
+
+static void
+csio_scsis_shost_cmpl_await(struct csio_ioreq *req, enum csio_scsi_ev evt)
+{
+ switch (evt) {
+ case CSIO_SCSIE_ABORT:
+ case CSIO_SCSIE_CLOSE:
+ /*
+ * Just succeed the abort request, and hope that
+ * the remote device unregister path will cleanup
+ * this I/O to the upper layer within a sane
+ * amount of time.
+ */
+ /*
+ * A close can come in during a LINK DOWN. The FW would have
+ * returned us the I/O back, but not the remote device lost
+ * FW event. In this interval, if the I/O times out at the upper
+ * layer, a close can come in. Take the same action as abort:
+ * return success, and hope that the remote device unregister
+ * path will cleanup this I/O. If the FW still doesnt send
+ * the msg, the close times out, and the upper layer resorts
+ * to the next level of error recovery.
+ */
+ req->drv_status = 0;
+ break;
+ case CSIO_SCSIE_DRVCLEANUP:
+ csio_set_state(&req->sm, csio_scsis_uninit);
+ break;
+ default:
+ csio_dbg(req->lnode->hwp, "Unhandled event:%d sent to req:%p\n",
+ evt, req);
+ CSIO_DB_ASSERT(0);
+ }
+}
+
+/*
+ * csio_scsi_cmpl_handler - WR completion handler for SCSI.
+ * @hw: HW module.
+ * @wr: The completed WR from the ingress queue.
+ * @len: Length of the WR.
+ * @flb: Freelist buffer array.
+ * @priv: Private object
+ * @scsiwr: Pointer to SCSI WR.
+ *
+ * This is the WR completion handler called per completion from the
+ * ISR. It is called with lock held. It walks past the RSS and CPL message
+ * header where the actual WR is present.
+ * It then gets the status, WR handle (ioreq pointer) and the len of
+ * the WR, based on WR opcode. Only on a non-good status is the entire
+ * WR copied into the WR cache (ioreq->fw_wr).
+ * The ioreq corresponding to the WR is returned to the caller.
+ * NOTE: The SCSI queue doesnt allocate a freelist today, hence
+ * no freelist buffer is expected.
+ */
+struct csio_ioreq *
+csio_scsi_cmpl_handler(struct csio_hw *hw, void *wr, uint32_t len,
+ struct csio_fl_dma_buf *flb, void *priv, uint8_t **scsiwr)
+{
+ struct csio_ioreq *ioreq = NULL;
+ struct cpl_fw6_msg *cpl;
+ uint8_t *tempwr;
+ uint8_t status;
+ struct csio_scsim *scm = csio_hw_to_scsim(hw);
+
+ /* skip RSS header */
+ cpl = (struct cpl_fw6_msg *)((uintptr_t)wr + sizeof(__be64));
+
+ if (unlikely(cpl->opcode != CPL_FW6_MSG)) {
+ csio_warn(hw, "Error: Invalid CPL msg %x recvd on SCSI q\n",
+ cpl->opcode);
+ CSIO_INC_STATS(scm, n_inval_cplop);
+ return NULL;
+ }
+
+ tempwr = (uint8_t *)(cpl->data);
+ status = csio_wr_status(tempwr);
+ *scsiwr = tempwr;
+
+ if (likely((*tempwr == FW_SCSI_READ_WR) ||
+ (*tempwr == FW_SCSI_WRITE_WR) ||
+ (*tempwr == FW_SCSI_CMD_WR))) {
+ ioreq = (struct csio_ioreq *)((uintptr_t)
+ (((struct fw_scsi_read_wr *)tempwr)->cookie));
+ CSIO_DB_ASSERT(virt_addr_valid(ioreq));
+
+ ioreq->wr_status = status;
+
+ return ioreq;
+ }
+
+ if (*tempwr == FW_SCSI_ABRT_CLS_WR) {
+ ioreq = (struct csio_ioreq *)((uintptr_t)
+ (((struct fw_scsi_abrt_cls_wr *)tempwr)->cookie));
+ CSIO_DB_ASSERT(virt_addr_valid(ioreq));
+
+ ioreq->wr_status = status;
+ return ioreq;
+ }
+
+ csio_warn(hw, "WR with invalid opcode in SCSI IQ: %x\n", *tempwr);
+ CSIO_INC_STATS(scm, n_inval_scsiop);
+ return NULL;
+}
+
+/*
+ * csio_scsi_cleanup_io_q - Cleanup the given queue.
+ * @scm: SCSI module.
+ * @q: Queue to be cleaned up.
+ *
+ * Called with lock held. Has to exit with lock held.
+ */
+void
+csio_scsi_cleanup_io_q(struct csio_scsim *scm, struct list_head *q)
+{
+ struct csio_hw *hw = scm->hw;
+ struct csio_ioreq *ioreq;
+ struct list_head *tmp, *next;
+ struct scsi_cmnd *scmnd;
+
+ /* Call back the completion routines of the active_q */
+ list_for_each_safe(tmp, next, q) {
+ ioreq = (struct csio_ioreq *)tmp;
+ csio_scsi_drvcleanup(ioreq);
+ list_del_init(&ioreq->sm.sm_list);
+ scmnd = csio_scsi_cmnd(ioreq);
+ spin_unlock_irq(&hw->lock);
+
+ /*
+ * Upper layers may have cleared this command, hence this
+ * check to avoid accessing stale references.
+ */
+ if (scmnd != NULL)
+ ioreq->io_cbfn(hw, ioreq);
+
+ spin_lock_irq(&scm->freelist_lock);
+ csio_put_scsi_ioreq(scm, ioreq);
+ spin_unlock_irq(&scm->freelist_lock);
+
+ spin_lock_irq(&hw->lock);
+ }
+}
+
+#define CSIO_SCSI_ABORT_Q_POLL_MS 2000
+
+static void
+csio_abrt_cls(struct csio_ioreq *ioreq, struct scsi_cmnd *scmnd)
+{
+ struct csio_lnode *ln = ioreq->lnode;
+ struct csio_hw *hw = ln->hwp;
+ int ready = 0;
+ struct csio_scsim *scsim = csio_hw_to_scsim(hw);
+ int rv;
+
+ if (csio_scsi_cmnd(ioreq) != scmnd) {
+ CSIO_INC_STATS(scsim, n_abrt_race_comp);
+ return;
+ }
+
+ ready = csio_is_lnode_ready(ln);
+
+ rv = csio_do_abrt_cls(hw, ioreq, (ready ? SCSI_ABORT : SCSI_CLOSE));
+ if (rv != 0) {
+ if (ready)
+ CSIO_INC_STATS(scsim, n_abrt_busy_error);
+ else
+ CSIO_INC_STATS(scsim, n_cls_busy_error);
+ }
+}
+
+/*
+ * csio_scsi_abort_io_q - Abort all I/Os on given queue
+ * @scm: SCSI module.
+ * @q: Queue to abort.
+ * @tmo: Timeout in ms
+ *
+ * Attempt to abort all I/Os on given queue, and wait for a max
+ * of tmo milliseconds for them to complete. Returns success
+ * if all I/Os are aborted. Else returns -ETIMEDOUT.
+ * Should be entered with lock held. Exits with lock held.
+ * NOTE:
+ * Lock has to be held across the loop that aborts I/Os, since dropping the lock
+ * in between can cause the list to be corrupted. As a result, the caller
+ * of this function has to ensure that the number of I/os to be aborted
+ * is finite enough to not cause lock-held-for-too-long issues.
+ */
+static int
+csio_scsi_abort_io_q(struct csio_scsim *scm, struct list_head *q, uint32_t tmo)
+{
+ struct csio_hw *hw = scm->hw;
+ struct list_head *tmp, *next;
+ int count = DIV_ROUND_UP(tmo, CSIO_SCSI_ABORT_Q_POLL_MS);
+ struct scsi_cmnd *scmnd;
+
+ if (list_empty(q))
+ return 0;
+
+ csio_dbg(hw, "Aborting SCSI I/Os\n");
+
+ /* Now abort/close I/Os in the queue passed */
+ list_for_each_safe(tmp, next, q) {
+ scmnd = csio_scsi_cmnd((struct csio_ioreq *)tmp);
+ csio_abrt_cls((struct csio_ioreq *)tmp, scmnd);
+ }
+
+ /* Wait till all active I/Os are completed/aborted/closed */
+ while (!list_empty(q) && count--) {
+ spin_unlock_irq(&hw->lock);
+ msleep(CSIO_SCSI_ABORT_Q_POLL_MS);
+ spin_lock_irq(&hw->lock);
+ }
+
+ /* all aborts completed */
+ if (list_empty(q))
+ return 0;
+
+ return -ETIMEDOUT;
+}
+
+/*
+ * csio_scsim_cleanup_io - Cleanup all I/Os in SCSI module.
+ * @scm: SCSI module.
+ * @abort: abort required.
+ * Called with lock held, should exit with lock held.
+ * Can sleep when waiting for I/Os to complete.
+ */
+int
+csio_scsim_cleanup_io(struct csio_scsim *scm, bool abort)
+{
+ struct csio_hw *hw = scm->hw;
+ int rv = 0;
+ int count = DIV_ROUND_UP(60 * 1000, CSIO_SCSI_ABORT_Q_POLL_MS);
+
+ /* No I/Os pending */
+ if (list_empty(&scm->active_q))
+ return 0;
+
+ /* Wait until all active I/Os are completed */
+ while (!list_empty(&scm->active_q) && count--) {
+ spin_unlock_irq(&hw->lock);
+ msleep(CSIO_SCSI_ABORT_Q_POLL_MS);
+ spin_lock_irq(&hw->lock);
+ }
+
+ /* all I/Os completed */
+ if (list_empty(&scm->active_q))
+ return 0;
+
+ /* Else abort */
+ if (abort) {
+ rv = csio_scsi_abort_io_q(scm, &scm->active_q, 30000);
+ if (rv == 0)
+ return rv;
+ csio_dbg(hw, "Some I/O aborts timed out, cleaning up..\n");
+ }
+
+ csio_scsi_cleanup_io_q(scm, &scm->active_q);
+
+ CSIO_DB_ASSERT(list_empty(&scm->active_q));
+
+ return rv;
+}
+
+/*
+ * csio_scsim_cleanup_io_lnode - Cleanup all I/Os of given lnode.
+ * @scm: SCSI module.
+ * @lnode: lnode
+ *
+ * Called with lock held, should exit with lock held.
+ * Can sleep (with dropped lock) when waiting for I/Os to complete.
+ */
+int
+csio_scsim_cleanup_io_lnode(struct csio_scsim *scm, struct csio_lnode *ln)
+{
+ struct csio_hw *hw = scm->hw;
+ struct csio_scsi_level_data sld;
+ int rv;
+ int count = DIV_ROUND_UP(60 * 1000, CSIO_SCSI_ABORT_Q_POLL_MS);
+
+ csio_dbg(hw, "Gathering all SCSI I/Os on lnode %p\n", ln);
+
+ sld.level = CSIO_LEV_LNODE;
+ sld.lnode = ln;
+ INIT_LIST_HEAD(&ln->cmpl_q);
+ csio_scsi_gather_active_ios(scm, &sld, &ln->cmpl_q);
+
+ /* No I/Os pending on this lnode */
+ if (list_empty(&ln->cmpl_q))
+ return 0;
+
+ /* Wait until all active I/Os on this lnode are completed */
+ while (!list_empty(&ln->cmpl_q) && count--) {
+ spin_unlock_irq(&hw->lock);
+ msleep(CSIO_SCSI_ABORT_Q_POLL_MS);
+ spin_lock_irq(&hw->lock);
+ }
+
+ /* all I/Os completed */
+ if (list_empty(&ln->cmpl_q))
+ return 0;
+
+ csio_dbg(hw, "Some I/Os pending on ln:%p, aborting them..\n", ln);
+
+ /* I/Os are pending, abort them */
+ rv = csio_scsi_abort_io_q(scm, &ln->cmpl_q, 30000);
+ if (rv != 0) {
+ csio_dbg(hw, "Some I/O aborts timed out, cleaning up..\n");
+ csio_scsi_cleanup_io_q(scm, &ln->cmpl_q);
+ }
+
+ CSIO_DB_ASSERT(list_empty(&ln->cmpl_q));
+
+ return rv;
+}
+
+static ssize_t
+csio_show_hw_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct csio_lnode *ln = shost_priv(class_to_shost(dev));
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ if (csio_is_hw_ready(hw))
+ return snprintf(buf, PAGE_SIZE, "ready\n");
+ else
+ return snprintf(buf, PAGE_SIZE, "not ready\n");
+}
+
+/* Device reset */
+static ssize_t
+csio_device_reset(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct csio_lnode *ln = shost_priv(class_to_shost(dev));
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+
+ if (*buf != '1')
+ return -EINVAL;
+
+ /* Delete NPIV lnodes */
+ csio_lnodes_exit(hw, 1);
+
+ /* Block upper IOs */
+ csio_lnodes_block_request(hw);
+
+ spin_lock_irq(&hw->lock);
+ csio_hw_reset(hw);
+ spin_unlock_irq(&hw->lock);
+
+ /* Unblock upper IOs */
+ csio_lnodes_unblock_request(hw);
+ return count;
+}
+
+/* disable port */
+static ssize_t
+csio_disable_port(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct csio_lnode *ln = shost_priv(class_to_shost(dev));
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ bool disable;
+
+ if (*buf == '1' || *buf == '0')
+ disable = (*buf == '1') ? true : false;
+ else
+ return -EINVAL;
+
+ /* Block upper IOs */
+ csio_lnodes_block_by_port(hw, ln->portid);
+
+ spin_lock_irq(&hw->lock);
+ csio_disable_lnodes(hw, ln->portid, disable);
+ spin_unlock_irq(&hw->lock);
+
+ /* Unblock upper IOs */
+ csio_lnodes_unblock_by_port(hw, ln->portid);
+ return count;
+}
+
+/* Show debug level */
+static ssize_t
+csio_show_dbg_level(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct csio_lnode *ln = shost_priv(class_to_shost(dev));
+
+ return snprintf(buf, PAGE_SIZE, "%x\n", ln->params.log_level);
+}
+
+/* Store debug level */
+static ssize_t
+csio_store_dbg_level(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct csio_lnode *ln = shost_priv(class_to_shost(dev));
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ uint32_t dbg_level = 0;
+
+ if (!isdigit(buf[0]))
+ return -EINVAL;
+
+ if (sscanf(buf, "%i", &dbg_level))
+ return -EINVAL;
+
+ ln->params.log_level = dbg_level;
+ hw->params.log_level = dbg_level;
+
+ return 0;
+}
+
+static DEVICE_ATTR(hw_state, S_IRUGO, csio_show_hw_state, NULL);
+static DEVICE_ATTR(device_reset, S_IRUGO | S_IWUSR, NULL, csio_device_reset);
+static DEVICE_ATTR(disable_port, S_IRUGO | S_IWUSR, NULL, csio_disable_port);
+static DEVICE_ATTR(dbg_level, S_IRUGO | S_IWUSR, csio_show_dbg_level,
+ csio_store_dbg_level);
+
+static struct device_attribute *csio_fcoe_lport_attrs[] = {
+ &dev_attr_hw_state,
+ &dev_attr_device_reset,
+ &dev_attr_disable_port,
+ &dev_attr_dbg_level,
+ NULL,
+};
+
+static ssize_t
+csio_show_num_reg_rnodes(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct csio_lnode *ln = shost_priv(class_to_shost(dev));
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ln->num_reg_rnodes);
+}
+
+static DEVICE_ATTR(num_reg_rnodes, S_IRUGO, csio_show_num_reg_rnodes, NULL);
+
+static struct device_attribute *csio_fcoe_vport_attrs[] = {
+ &dev_attr_num_reg_rnodes,
+ &dev_attr_dbg_level,
+ NULL,
+};
+
+static inline uint32_t
+csio_scsi_copy_to_sgl(struct csio_hw *hw, struct csio_ioreq *req)
+{
+ struct scsi_cmnd *scmnd = (struct scsi_cmnd *)csio_scsi_cmnd(req);
+ struct scatterlist *sg;
+ uint32_t bytes_left;
+ uint32_t bytes_copy;
+ uint32_t buf_off = 0;
+ uint32_t start_off = 0;
+ uint32_t sg_off = 0;
+ void *sg_addr;
+ void *buf_addr;
+ struct csio_dma_buf *dma_buf;
+
+ bytes_left = scsi_bufflen(scmnd);
+ sg = scsi_sglist(scmnd);
+ dma_buf = (struct csio_dma_buf *)csio_list_next(&req->gen_list);
+
+ /* Copy data from driver buffer to SGs of SCSI CMD */
+ while (bytes_left > 0 && sg && dma_buf) {
+ if (buf_off >= dma_buf->len) {
+ buf_off = 0;
+ dma_buf = (struct csio_dma_buf *)
+ csio_list_next(dma_buf);
+ continue;
+ }
+
+ if (start_off >= sg->length) {
+ start_off -= sg->length;
+ sg = sg_next(sg);
+ continue;
+ }
+
+ buf_addr = dma_buf->vaddr + buf_off;
+ sg_off = sg->offset + start_off;
+ bytes_copy = min((dma_buf->len - buf_off),
+ sg->length - start_off);
+ bytes_copy = min((uint32_t)(PAGE_SIZE - (sg_off & ~PAGE_MASK)),
+ bytes_copy);
+
+ sg_addr = kmap_atomic(sg_page(sg) + (sg_off >> PAGE_SHIFT));
+ if (!sg_addr) {
+ csio_err(hw, "failed to kmap sg:%p of ioreq:%p\n",
+ sg, req);
+ break;
+ }
+
+ csio_dbg(hw, "copy_to_sgl:sg_addr %p sg_off %d buf %p len %d\n",
+ sg_addr, sg_off, buf_addr, bytes_copy);
+ memcpy(sg_addr + (sg_off & ~PAGE_MASK), buf_addr, bytes_copy);
+ kunmap_atomic(sg_addr);
+
+ start_off += bytes_copy;
+ buf_off += bytes_copy;
+ bytes_left -= bytes_copy;
+ }
+
+ if (bytes_left > 0)
+ return DID_ERROR;
+ else
+ return DID_OK;
+}
+
+/*
+ * csio_scsi_err_handler - SCSI error handler.
+ * @hw: HW module.
+ * @req: IO request.
+ *
+ */
+static inline void
+csio_scsi_err_handler(struct csio_hw *hw, struct csio_ioreq *req)
+{
+ struct scsi_cmnd *cmnd = (struct scsi_cmnd *)csio_scsi_cmnd(req);
+ struct csio_scsim *scm = csio_hw_to_scsim(hw);
+ struct fcp_resp_with_ext *fcp_resp;
+ struct fcp_resp_rsp_info *rsp_info;
+ struct csio_dma_buf *dma_buf;
+ uint8_t flags, scsi_status = 0;
+ uint32_t host_status = DID_OK;
+ uint32_t rsp_len = 0, sns_len = 0;
+ struct csio_rnode *rn = (struct csio_rnode *)(cmnd->device->hostdata);
+
+
+ switch (req->wr_status) {
+ case FW_HOSTERROR:
+ if (unlikely(!csio_is_hw_ready(hw)))
+ return;
+
+ host_status = DID_ERROR;
+ CSIO_INC_STATS(scm, n_hosterror);
+
+ break;
+ case FW_SCSI_RSP_ERR:
+ dma_buf = &req->dma_buf;
+ fcp_resp = (struct fcp_resp_with_ext *)dma_buf->vaddr;
+ rsp_info = (struct fcp_resp_rsp_info *)(fcp_resp + 1);
+ flags = fcp_resp->resp.fr_flags;
+ scsi_status = fcp_resp->resp.fr_status;
+
+ if (flags & FCP_RSP_LEN_VAL) {
+ rsp_len = be32_to_cpu(fcp_resp->ext.fr_rsp_len);
+ if ((rsp_len != 0 && rsp_len != 4 && rsp_len != 8) ||
+ (rsp_info->rsp_code != FCP_TMF_CMPL)) {
+ host_status = DID_ERROR;
+ goto out;
+ }
+ }
+
+ if ((flags & FCP_SNS_LEN_VAL) && fcp_resp->ext.fr_sns_len) {
+ sns_len = be32_to_cpu(fcp_resp->ext.fr_sns_len);
+ if (sns_len > SCSI_SENSE_BUFFERSIZE)
+ sns_len = SCSI_SENSE_BUFFERSIZE;
+
+ memcpy(cmnd->sense_buffer,
+ &rsp_info->_fr_resvd[0] + rsp_len, sns_len);
+ CSIO_INC_STATS(scm, n_autosense);
+ }
+
+ scsi_set_resid(cmnd, 0);
+
+ /* Under run */
+ if (flags & FCP_RESID_UNDER) {
+ scsi_set_resid(cmnd,
+ be32_to_cpu(fcp_resp->ext.fr_resid));
+
+ if (!(flags & FCP_SNS_LEN_VAL) &&
+ (scsi_status == SAM_STAT_GOOD) &&
+ ((scsi_bufflen(cmnd) - scsi_get_resid(cmnd))
+ < cmnd->underflow))
+ host_status = DID_ERROR;
+ } else if (flags & FCP_RESID_OVER)
+ host_status = DID_ERROR;
+
+ CSIO_INC_STATS(scm, n_rsperror);
+ break;
+
+ case FW_SCSI_OVER_FLOW_ERR:
+ csio_warn(hw,
+ "Over-flow error,cmnd:0x%x expected len:0x%x"
+ " resid:0x%x\n", cmnd->cmnd[0],
+ scsi_bufflen(cmnd), scsi_get_resid(cmnd));
+ host_status = DID_ERROR;
+ CSIO_INC_STATS(scm, n_ovflerror);
+ break;
+
+ case FW_SCSI_UNDER_FLOW_ERR:
+ csio_warn(hw,
+ "Under-flow error,cmnd:0x%x expected"
+ " len:0x%x resid:0x%x lun:0x%x ssn:0x%x\n",
+ cmnd->cmnd[0], scsi_bufflen(cmnd),
+ scsi_get_resid(cmnd), cmnd->device->lun,
+ rn->flowid);
+ host_status = DID_ERROR;
+ CSIO_INC_STATS(scm, n_unflerror);
+ break;
+
+ case FW_SCSI_ABORT_REQUESTED:
+ case FW_SCSI_ABORTED:
+ case FW_SCSI_CLOSE_REQUESTED:
+ csio_dbg(hw, "Req %p cmd:%p op:%x %s\n", req, cmnd,
+ cmnd->cmnd[0],
+ (req->wr_status == FW_SCSI_CLOSE_REQUESTED) ?
+ "closed" : "aborted");
+ /*
+ * csio_eh_abort_handler checks this value to
+ * succeed or fail the abort request.
+ */
+ host_status = DID_REQUEUE;
+ if (req->wr_status == FW_SCSI_CLOSE_REQUESTED)
+ CSIO_INC_STATS(scm, n_closed);
+ else
+ CSIO_INC_STATS(scm, n_aborted);
+ break;
+
+ case FW_SCSI_ABORT_TIMEDOUT:
+ /* FW timed out the abort itself */
+ csio_dbg(hw, "FW timed out abort req:%p cmnd:%p status:%x\n",
+ req, cmnd, req->wr_status);
+ host_status = DID_ERROR;
+ CSIO_INC_STATS(scm, n_abrt_timedout);
+ break;
+
+ case FW_RDEV_NOT_READY:
+ /*
+ * In firmware, a RDEV can get into this state
+ * temporarily, before moving into dissapeared/lost
+ * state. So, the driver should complete the request equivalent
+ * to device-disappeared!
+ */
+ CSIO_INC_STATS(scm, n_rdev_nr_error);
+ host_status = DID_ERROR;
+ break;
+
+ case FW_ERR_RDEV_LOST:
+ CSIO_INC_STATS(scm, n_rdev_lost_error);
+ host_status = DID_ERROR;
+ break;
+
+ case FW_ERR_RDEV_LOGO:
+ CSIO_INC_STATS(scm, n_rdev_logo_error);
+ host_status = DID_ERROR;
+ break;
+
+ case FW_ERR_RDEV_IMPL_LOGO:
+ host_status = DID_ERROR;
+ break;
+
+ case FW_ERR_LINK_DOWN:
+ CSIO_INC_STATS(scm, n_link_down_error);
+ host_status = DID_ERROR;
+ break;
+
+ case FW_FCOE_NO_XCHG:
+ CSIO_INC_STATS(scm, n_no_xchg_error);
+ host_status = DID_ERROR;
+ break;
+
+ default:
+ csio_err(hw, "Unknown SCSI FW WR status:%d req:%p cmnd:%p\n",
+ req->wr_status, req, cmnd);
+ CSIO_DB_ASSERT(0);
+
+ CSIO_INC_STATS(scm, n_unknown_error);
+ host_status = DID_ERROR;
+ break;
+ }
+
+out:
+ if (req->nsge > 0)
+ scsi_dma_unmap(cmnd);
+
+ cmnd->result = (((host_status) << 16) | scsi_status);
+ cmnd->scsi_done(cmnd);
+
+ /* Wake up waiting threads */
+ csio_scsi_cmnd(req) = NULL;
+ complete_all(&req->cmplobj);
+}
+
+/*
+ * csio_scsi_cbfn - SCSI callback function.
+ * @hw: HW module.
+ * @req: IO request.
+ *
+ */
+static void
+csio_scsi_cbfn(struct csio_hw *hw, struct csio_ioreq *req)
+{
+ struct scsi_cmnd *cmnd = (struct scsi_cmnd *)csio_scsi_cmnd(req);
+ uint8_t scsi_status = SAM_STAT_GOOD;
+ uint32_t host_status = DID_OK;
+
+ if (likely(req->wr_status == FW_SUCCESS)) {
+ if (req->nsge > 0) {
+ scsi_dma_unmap(cmnd);
+ if (req->dcopy)
+ host_status = csio_scsi_copy_to_sgl(hw, req);
+ }
+
+ cmnd->result = (((host_status) << 16) | scsi_status);
+ cmnd->scsi_done(cmnd);
+ csio_scsi_cmnd(req) = NULL;
+ CSIO_INC_STATS(csio_hw_to_scsim(hw), n_tot_success);
+ } else {
+ /* Error handling */
+ csio_scsi_err_handler(hw, req);
+ }
+}
+
+/**
+ * csio_queuecommand - Entry point to kickstart an I/O request.
+ * @host: The scsi_host pointer.
+ * @cmnd: The I/O request from ML.
+ *
+ * This routine does the following:
+ * - Checks for HW and Rnode module readiness.
+ * - Gets a free ioreq structure (which is already initialized
+ * to uninit during its allocation).
+ * - Maps SG elements.
+ * - Initializes ioreq members.
+ * - Kicks off the SCSI state machine for this IO.
+ * - Returns busy status on error.
+ */
+static int
+csio_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmnd)
+{
+ struct csio_lnode *ln = shost_priv(host);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ struct csio_scsim *scsim = csio_hw_to_scsim(hw);
+ struct csio_rnode *rn = (struct csio_rnode *)(cmnd->device->hostdata);
+ struct csio_ioreq *ioreq = NULL;
+ unsigned long flags;
+ int nsge = 0;
+ int rv = SCSI_MLQUEUE_HOST_BUSY, nr;
+ int retval;
+ int cpu;
+ struct csio_scsi_qset *sqset;
+ struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
+
+ if (!blk_rq_cpu_valid(cmnd->request))
+ cpu = smp_processor_id();
+ else
+ cpu = cmnd->request->cpu;
+
+ sqset = &hw->sqset[ln->portid][cpu];
+
+ nr = fc_remote_port_chkready(rport);
+ if (nr) {
+ cmnd->result = nr;
+ CSIO_INC_STATS(scsim, n_rn_nr_error);
+ goto err_done;
+ }
+
+ if (unlikely(!csio_is_hw_ready(hw))) {
+ cmnd->result = (DID_REQUEUE << 16);
+ CSIO_INC_STATS(scsim, n_hw_nr_error);
+ goto err_done;
+ }
+
+ /* Get req->nsge, if there are SG elements to be mapped */
+ nsge = scsi_dma_map(cmnd);
+ if (unlikely(nsge < 0)) {
+ CSIO_INC_STATS(scsim, n_dmamap_error);
+ goto err;
+ }
+
+ /* Do we support so many mappings? */
+ if (unlikely(nsge > scsim->max_sge)) {
+ csio_warn(hw,
+ "More SGEs than can be supported."
+ " SGEs: %d, Max SGEs: %d\n", nsge, scsim->max_sge);
+ CSIO_INC_STATS(scsim, n_unsupp_sge_error);
+ goto err_dma_unmap;
+ }
+
+ /* Get a free ioreq structure - SM is already set to uninit */
+ ioreq = csio_get_scsi_ioreq_lock(hw, scsim);
+ if (!ioreq) {
+ csio_err(hw, "Out of I/O request elements. Active #:%d\n",
+ scsim->stats.n_active);
+ CSIO_INC_STATS(scsim, n_no_req_error);
+ goto err_dma_unmap;
+ }
+
+ ioreq->nsge = nsge;
+ ioreq->lnode = ln;
+ ioreq->rnode = rn;
+ ioreq->iq_idx = sqset->iq_idx;
+ ioreq->eq_idx = sqset->eq_idx;
+ ioreq->wr_status = 0;
+ ioreq->drv_status = 0;
+ csio_scsi_cmnd(ioreq) = (void *)cmnd;
+ ioreq->tmo = 0;
+ ioreq->datadir = cmnd->sc_data_direction;
+
+ if (cmnd->sc_data_direction == DMA_TO_DEVICE) {
+ CSIO_INC_STATS(ln, n_output_requests);
+ ln->stats.n_output_bytes += scsi_bufflen(cmnd);
+ } else if (cmnd->sc_data_direction == DMA_FROM_DEVICE) {
+ CSIO_INC_STATS(ln, n_input_requests);
+ ln->stats.n_input_bytes += scsi_bufflen(cmnd);
+ } else
+ CSIO_INC_STATS(ln, n_control_requests);
+
+ /* Set cbfn */
+ ioreq->io_cbfn = csio_scsi_cbfn;
+
+ /* Needed during abort */
+ cmnd->host_scribble = (unsigned char *)ioreq;
+ cmnd->SCp.Message = 0;
+
+ /* Kick off SCSI IO SM on the ioreq */
+ spin_lock_irqsave(&hw->lock, flags);
+ retval = csio_scsi_start_io(ioreq);
+ spin_unlock_irqrestore(&hw->lock, flags);
+
+ if (retval != 0) {
+ csio_err(hw, "ioreq: %p couldnt be started, status:%d\n",
+ ioreq, retval);
+ CSIO_INC_STATS(scsim, n_busy_error);
+ goto err_put_req;
+ }
+
+ return 0;
+
+err_put_req:
+ csio_put_scsi_ioreq_lock(hw, scsim, ioreq);
+err_dma_unmap:
+ if (nsge > 0)
+ scsi_dma_unmap(cmnd);
+err:
+ return rv;
+
+err_done:
+ cmnd->scsi_done(cmnd);
+ return 0;
+}
+
+static int
+csio_do_abrt_cls(struct csio_hw *hw, struct csio_ioreq *ioreq, bool abort)
+{
+ int rv;
+ int cpu = smp_processor_id();
+ struct csio_lnode *ln = ioreq->lnode;
+ struct csio_scsi_qset *sqset = &hw->sqset[ln->portid][cpu];
+
+ ioreq->tmo = CSIO_SCSI_ABRT_TMO_MS;
+ /*
+ * Use current processor queue for posting the abort/close, but retain
+ * the ingress queue ID of the original I/O being aborted/closed - we
+ * need the abort/close completion to be received on the same queue
+ * as the original I/O.
+ */
+ ioreq->eq_idx = sqset->eq_idx;
+
+ if (abort == SCSI_ABORT)
+ rv = csio_scsi_abort(ioreq);
+ else
+ rv = csio_scsi_close(ioreq);
+
+ return rv;
+}
+
+static int
+csio_eh_abort_handler(struct scsi_cmnd *cmnd)
+{
+ struct csio_ioreq *ioreq;
+ struct csio_lnode *ln = shost_priv(cmnd->device->host);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ struct csio_scsim *scsim = csio_hw_to_scsim(hw);
+ int ready = 0, ret;
+ unsigned long tmo = 0;
+ int rv;
+ struct csio_rnode *rn = (struct csio_rnode *)(cmnd->device->hostdata);
+
+ ret = fc_block_scsi_eh(cmnd);
+ if (ret)
+ return ret;
+
+ ioreq = (struct csio_ioreq *)cmnd->host_scribble;
+ if (!ioreq)
+ return SUCCESS;
+
+ if (!rn)
+ return FAILED;
+
+ csio_dbg(hw,
+ "Request to abort ioreq:%p cmd:%p cdb:%08llx"
+ " ssni:0x%x lun:%d iq:0x%x\n",
+ ioreq, cmnd, *((uint64_t *)cmnd->cmnd), rn->flowid,
+ cmnd->device->lun, csio_q_physiqid(hw, ioreq->iq_idx));
+
+ if (((struct scsi_cmnd *)csio_scsi_cmnd(ioreq)) != cmnd) {
+ CSIO_INC_STATS(scsim, n_abrt_race_comp);
+ return SUCCESS;
+ }
+
+ ready = csio_is_lnode_ready(ln);
+ tmo = CSIO_SCSI_ABRT_TMO_MS;
+
+ spin_lock_irq(&hw->lock);
+ rv = csio_do_abrt_cls(hw, ioreq, (ready ? SCSI_ABORT : SCSI_CLOSE));
+ spin_unlock_irq(&hw->lock);
+
+ if (rv != 0) {
+ if (rv == -EINVAL) {
+ /* Return success, if abort/close request issued on
+ * already completed IO
+ */
+ return SUCCESS;
+ }
+ if (ready)
+ CSIO_INC_STATS(scsim, n_abrt_busy_error);
+ else
+ CSIO_INC_STATS(scsim, n_cls_busy_error);
+
+ goto inval_scmnd;
+ }
+
+ /* Wait for completion */
+ init_completion(&ioreq->cmplobj);
+ wait_for_completion_timeout(&ioreq->cmplobj, msecs_to_jiffies(tmo));
+
+ /* FW didnt respond to abort within our timeout */
+ if (((struct scsi_cmnd *)csio_scsi_cmnd(ioreq)) == cmnd) {
+
+ csio_err(hw, "Abort timed out -- req: %p\n", ioreq);
+ CSIO_INC_STATS(scsim, n_abrt_timedout);
+
+inval_scmnd:
+ if (ioreq->nsge > 0)
+ scsi_dma_unmap(cmnd);
+
+ spin_lock_irq(&hw->lock);
+ csio_scsi_cmnd(ioreq) = NULL;
+ spin_unlock_irq(&hw->lock);
+
+ cmnd->result = (DID_ERROR << 16);
+ cmnd->scsi_done(cmnd);
+
+ return FAILED;
+ }
+
+ /* FW successfully aborted the request */
+ if (host_byte(cmnd->result) == DID_REQUEUE) {
+ csio_info(hw,
+ "Aborted SCSI command to (%d:%d) serial#:0x%lx\n",
+ cmnd->device->id, cmnd->device->lun,
+ cmnd->serial_number);
+ return SUCCESS;
+ } else {
+ csio_info(hw,
+ "Failed to abort SCSI command, (%d:%d) serial#:0x%lx\n",
+ cmnd->device->id, cmnd->device->lun,
+ cmnd->serial_number);
+ return FAILED;
+ }
+}
+
+/*
+ * csio_tm_cbfn - TM callback function.
+ * @hw: HW module.
+ * @req: IO request.
+ *
+ * Cache the result in 'cmnd', since ioreq will be freed soon
+ * after we return from here, and the waiting thread shouldnt trust
+ * the ioreq contents.
+ */
+static void
+csio_tm_cbfn(struct csio_hw *hw, struct csio_ioreq *req)
+{
+ struct scsi_cmnd *cmnd = (struct scsi_cmnd *)csio_scsi_cmnd(req);
+ struct csio_dma_buf *dma_buf;
+ uint8_t flags = 0;
+ struct fcp_resp_with_ext *fcp_resp;
+ struct fcp_resp_rsp_info *rsp_info;
+
+ csio_dbg(hw, "req: %p in csio_tm_cbfn status: %d\n",
+ req, req->wr_status);
+
+ /* Cache FW return status */
+ cmnd->SCp.Status = req->wr_status;
+
+ /* Special handling based on FCP response */
+
+ /*
+ * FW returns us this error, if flags were set. FCP4 says
+ * FCP_RSP_LEN_VAL in flags shall be set for TM completions.
+ * So if a target were to set this bit, we expect that the
+ * rsp_code is set to FCP_TMF_CMPL for a successful TM
+ * completion. Any other rsp_code means TM operation failed.
+ * If a target were to just ignore setting flags, we treat
+ * the TM operation as success, and FW returns FW_SUCCESS.
+ */
+ if (req->wr_status == FW_SCSI_RSP_ERR) {
+ dma_buf = &req->dma_buf;
+ fcp_resp = (struct fcp_resp_with_ext *)dma_buf->vaddr;
+ rsp_info = (struct fcp_resp_rsp_info *)(fcp_resp + 1);
+
+ flags = fcp_resp->resp.fr_flags;
+
+ /* Modify return status if flags indicate success */
+ if (flags & FCP_RSP_LEN_VAL)
+ if (rsp_info->rsp_code == FCP_TMF_CMPL)
+ cmnd->SCp.Status = FW_SUCCESS;
+
+ csio_dbg(hw, "TM FCP rsp code: %d\n", rsp_info->rsp_code);
+ }
+
+ /* Wake up the TM handler thread */
+ csio_scsi_cmnd(req) = NULL;
+}
+
+static int
+csio_eh_lun_reset_handler(struct scsi_cmnd *cmnd)
+{
+ struct csio_lnode *ln = shost_priv(cmnd->device->host);
+ struct csio_hw *hw = csio_lnode_to_hw(ln);
+ struct csio_scsim *scsim = csio_hw_to_scsim(hw);
+ struct csio_rnode *rn = (struct csio_rnode *)(cmnd->device->hostdata);
+ struct csio_ioreq *ioreq = NULL;
+ struct csio_scsi_qset *sqset;
+ unsigned long flags;
+ int retval;
+ int count, ret;
+ LIST_HEAD(local_q);
+ struct csio_scsi_level_data sld;
+
+ if (!rn)
+ goto fail;
+
+ csio_dbg(hw, "Request to reset LUN:%d (ssni:0x%x tgtid:%d)\n",
+ cmnd->device->lun, rn->flowid, rn->scsi_id);
+
+ if (!csio_is_lnode_ready(ln)) {
+ csio_err(hw,
+ "LUN reset cannot be issued on non-ready"
+ " local node vnpi:0x%x (LUN:%d)\n",
+ ln->vnp_flowid, cmnd->device->lun);
+ goto fail;
+ }
+
+ /* Lnode is ready, now wait on rport node readiness */
+ ret = fc_block_scsi_eh(cmnd);
+ if (ret)
+ return ret;
+
+ /*
+ * If we have blocked in the previous call, at this point, either the
+ * remote node has come back online, or device loss timer has fired
+ * and the remote node is destroyed. Allow the LUN reset only for
+ * the former case, since LUN reset is a TMF I/O on the wire, and we
+ * need a valid session to issue it.
+ */
+ if (fc_remote_port_chkready(rn->rport)) {
+ csio_err(hw,
+ "LUN reset cannot be issued on non-ready"
+ " remote node ssni:0x%x (LUN:%d)\n",
+ rn->flowid, cmnd->device->lun);
+ goto fail;
+ }
+
+ /* Get a free ioreq structure - SM is already set to uninit */
+ ioreq = csio_get_scsi_ioreq_lock(hw, scsim);
+
+ if (!ioreq) {
+ csio_err(hw, "Out of IO request elements. Active # :%d\n",
+ scsim->stats.n_active);
+ goto fail;
+ }
+
+ sqset = &hw->sqset[ln->portid][smp_processor_id()];
+ ioreq->nsge = 0;
+ ioreq->lnode = ln;
+ ioreq->rnode = rn;
+ ioreq->iq_idx = sqset->iq_idx;
+ ioreq->eq_idx = sqset->eq_idx;
+
+ csio_scsi_cmnd(ioreq) = cmnd;
+ cmnd->host_scribble = (unsigned char *)ioreq;
+ cmnd->SCp.Status = 0;
+
+ cmnd->SCp.Message = FCP_TMF_LUN_RESET;
+ ioreq->tmo = CSIO_SCSI_LUNRST_TMO_MS / 1000;
+
+ /*
+ * FW times the LUN reset for ioreq->tmo, so we got to wait a little
+ * longer (10s for now) than that to allow FW to return the timed
+ * out command.
+ */
+ count = DIV_ROUND_UP((ioreq->tmo + 10) * 1000, CSIO_SCSI_TM_POLL_MS);
+
+ /* Set cbfn */
+ ioreq->io_cbfn = csio_tm_cbfn;
+
+ /* Save of the ioreq info for later use */
+ sld.level = CSIO_LEV_LUN;
+ sld.lnode = ioreq->lnode;
+ sld.rnode = ioreq->rnode;
+ sld.oslun = (uint64_t)cmnd->device->lun;
+
+ spin_lock_irqsave(&hw->lock, flags);
+ /* Kick off TM SM on the ioreq */
+ retval = csio_scsi_start_tm(ioreq);
+ spin_unlock_irqrestore(&hw->lock, flags);
+
+ if (retval != 0) {
+ csio_err(hw, "Failed to issue LUN reset, req:%p, status:%d\n",
+ ioreq, retval);
+ goto fail_ret_ioreq;
+ }
+
+ csio_dbg(hw, "Waiting max %d secs for LUN reset completion\n",
+ count * (CSIO_SCSI_TM_POLL_MS / 1000));
+ /* Wait for completion */
+ while ((((struct scsi_cmnd *)csio_scsi_cmnd(ioreq)) == cmnd)
+ && count--)
+ msleep(CSIO_SCSI_TM_POLL_MS);
+
+ /* LUN reset timed-out */
+ if (((struct scsi_cmnd *)csio_scsi_cmnd(ioreq)) == cmnd) {
+ csio_err(hw, "LUN reset (%d:%d) timed out\n",
+ cmnd->device->id, cmnd->device->lun);
+
+ spin_lock_irq(&hw->lock);
+ csio_scsi_drvcleanup(ioreq);
+ list_del_init(&ioreq->sm.sm_list);
+ spin_unlock_irq(&hw->lock);
+
+ goto fail_ret_ioreq;
+ }
+
+ /* LUN reset returned, check cached status */
+ if (cmnd->SCp.Status != FW_SUCCESS) {
+ csio_err(hw, "LUN reset failed (%d:%d), status: %d\n",
+ cmnd->device->id, cmnd->device->lun, cmnd->SCp.Status);
+ goto fail;
+ }
+
+ /* LUN reset succeeded, Start aborting affected I/Os */
+ /*
+ * Since the host guarantees during LUN reset that there
+ * will not be any more I/Os to that LUN, until the LUN reset
+ * completes, we gather pending I/Os after the LUN reset.
+ */
+ spin_lock_irq(&hw->lock);
+ csio_scsi_gather_active_ios(scsim, &sld, &local_q);
+
+ retval = csio_scsi_abort_io_q(scsim, &local_q, 30000);
+ spin_unlock_irq(&hw->lock);
+
+ /* Aborts may have timed out */
+ if (retval != 0) {
+ csio_err(hw,
+ "Attempt to abort I/Os during LUN reset of %d"
+ " returned %d\n", cmnd->device->lun, retval);
+ /* Return I/Os back to active_q */
+ spin_lock_irq(&hw->lock);
+ list_splice_tail_init(&local_q, &scsim->active_q);
+ spin_unlock_irq(&hw->lock);
+ goto fail;
+ }
+
+ CSIO_INC_STATS(rn, n_lun_rst);
+
+ csio_info(hw, "LUN reset occurred (%d:%d)\n",
+ cmnd->device->id, cmnd->device->lun);
+
+ return SUCCESS;
+
+fail_ret_ioreq:
+ csio_put_scsi_ioreq_lock(hw, scsim, ioreq);
+fail:
+ CSIO_INC_STATS(rn, n_lun_rst_fail);
+ return FAILED;
+}
+
+static int
+csio_slave_alloc(struct scsi_device *sdev)
+{
+ struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+
+ if (!rport || fc_remote_port_chkready(rport))
+ return -ENXIO;
+
+ sdev->hostdata = *((struct csio_lnode **)(rport->dd_data));
+
+ return 0;
+}
+
+static int
+csio_slave_configure(struct scsi_device *sdev)
+{
+ if (sdev->tagged_supported)
+ scsi_activate_tcq(sdev, csio_lun_qdepth);
+ else
+ scsi_deactivate_tcq(sdev, csio_lun_qdepth);
+
+ return 0;
+}
+
+static void
+csio_slave_destroy(struct scsi_device *sdev)
+{
+ sdev->hostdata = NULL;
+}
+
+static int
+csio_scan_finished(struct Scsi_Host *shost, unsigned long time)
+{
+ struct csio_lnode *ln = shost_priv(shost);
+ int rv = 1;
+
+ spin_lock_irq(shost->host_lock);
+ if (!ln->hwp || csio_list_deleted(&ln->sm.sm_list))
+ goto out;
+
+ rv = csio_scan_done(ln, jiffies, time, csio_max_scan_tmo * HZ,
+ csio_delta_scan_tmo * HZ);
+out:
+ spin_unlock_irq(shost->host_lock);
+
+ return rv;
+}
+
+struct scsi_host_template csio_fcoe_shost_template = {
+ .module = THIS_MODULE,
+ .name = CSIO_DRV_DESC,
+ .proc_name = KBUILD_MODNAME,
+ .queuecommand = csio_queuecommand,
+ .eh_abort_handler = csio_eh_abort_handler,
+ .eh_device_reset_handler = csio_eh_lun_reset_handler,
+ .slave_alloc = csio_slave_alloc,
+ .slave_configure = csio_slave_configure,
+ .slave_destroy = csio_slave_destroy,
+ .scan_finished = csio_scan_finished,
+ .this_id = -1,
+ .sg_tablesize = CSIO_SCSI_MAX_SGE,
+ .cmd_per_lun = CSIO_MAX_CMD_PER_LUN,
+ .use_clustering = ENABLE_CLUSTERING,
+ .shost_attrs = csio_fcoe_lport_attrs,
+ .max_sectors = CSIO_MAX_SECTOR_SIZE,
+};
+
+struct scsi_host_template csio_fcoe_shost_vport_template = {
+ .module = THIS_MODULE,
+ .name = CSIO_DRV_DESC,
+ .proc_name = KBUILD_MODNAME,
+ .queuecommand = csio_queuecommand,
+ .eh_abort_handler = csio_eh_abort_handler,
+ .eh_device_reset_handler = csio_eh_lun_reset_handler,
+ .slave_alloc = csio_slave_alloc,
+ .slave_configure = csio_slave_configure,
+ .slave_destroy = csio_slave_destroy,
+ .scan_finished = csio_scan_finished,
+ .this_id = -1,
+ .sg_tablesize = CSIO_SCSI_MAX_SGE,
+ .cmd_per_lun = CSIO_MAX_CMD_PER_LUN,
+ .use_clustering = ENABLE_CLUSTERING,
+ .shost_attrs = csio_fcoe_vport_attrs,
+ .max_sectors = CSIO_MAX_SECTOR_SIZE,
+};
+
+/*
+ * csio_scsi_alloc_ddp_bufs - Allocate buffers for DDP of unaligned SGLs.
+ * @scm: SCSI Module
+ * @hw: HW device.
+ * @buf_size: buffer size
+ * @num_buf : Number of buffers.
+ *
+ * This routine allocates DMA buffers required for SCSI Data xfer, if
+ * each SGL buffer for a SCSI Read request posted by SCSI midlayer are
+ * not virtually contiguous.
+ */
+static int
+csio_scsi_alloc_ddp_bufs(struct csio_scsim *scm, struct csio_hw *hw,
+ int buf_size, int num_buf)
+{
+ int n = 0;
+ struct list_head *tmp;
+ struct csio_dma_buf *ddp_desc = NULL;
+ uint32_t unit_size = 0;
+
+ if (!num_buf)
+ return 0;
+
+ if (!buf_size)
+ return -EINVAL;
+
+ INIT_LIST_HEAD(&scm->ddp_freelist);
+
+ /* Align buf size to page size */
+ buf_size = (buf_size + PAGE_SIZE - 1) & PAGE_MASK;
+ /* Initialize dma descriptors */
+ for (n = 0; n < num_buf; n++) {
+ /* Set unit size to request size */
+ unit_size = buf_size;
+ ddp_desc = kzalloc(sizeof(struct csio_dma_buf), GFP_KERNEL);
+ if (!ddp_desc) {
+ csio_err(hw,
+ "Failed to allocate ddp descriptors,"
+ " Num allocated = %d.\n",
+ scm->stats.n_free_ddp);
+ goto no_mem;
+ }
+
+ /* Allocate Dma buffers for DDP */
+ ddp_desc->vaddr = pci_alloc_consistent(hw->pdev, unit_size,
+ &ddp_desc->paddr);
+ if (!ddp_desc->vaddr) {
+ csio_err(hw,
+ "SCSI response DMA buffer (ddp) allocation"
+ " failed!\n");
+ kfree(ddp_desc);
+ goto no_mem;
+ }
+
+ ddp_desc->len = unit_size;
+
+ /* Added it to scsi ddp freelist */
+ list_add_tail(&ddp_desc->list, &scm->ddp_freelist);
+ CSIO_INC_STATS(scm, n_free_ddp);
+ }
+
+ return 0;
+no_mem:
+ /* release dma descs back to freelist and free dma memory */
+ list_for_each(tmp, &scm->ddp_freelist) {
+ ddp_desc = (struct csio_dma_buf *) tmp;
+ tmp = csio_list_prev(tmp);
+ pci_free_consistent(hw->pdev, ddp_desc->len, ddp_desc->vaddr,
+ ddp_desc->paddr);
+ list_del_init(&ddp_desc->list);
+ kfree(ddp_desc);
+ }
+ scm->stats.n_free_ddp = 0;
+
+ return -ENOMEM;
+}
+
+/*
+ * csio_scsi_free_ddp_bufs - free DDP buffers of unaligned SGLs.
+ * @scm: SCSI Module
+ * @hw: HW device.
+ *
+ * This routine frees ddp buffers.
+ */
+static void
+csio_scsi_free_ddp_bufs(struct csio_scsim *scm, struct csio_hw *hw)
+{
+ struct list_head *tmp;
+ struct csio_dma_buf *ddp_desc;
+
+ /* release dma descs back to freelist and free dma memory */
+ list_for_each(tmp, &scm->ddp_freelist) {
+ ddp_desc = (struct csio_dma_buf *) tmp;
+ tmp = csio_list_prev(tmp);
+ pci_free_consistent(hw->pdev, ddp_desc->len, ddp_desc->vaddr,
+ ddp_desc->paddr);
+ list_del_init(&ddp_desc->list);
+ kfree(ddp_desc);
+ }
+ scm->stats.n_free_ddp = 0;
+}
+
+/**
+ * csio_scsim_init - Initialize SCSI Module
+ * @scm: SCSI Module
+ * @hw: HW module
+ *
+ */
+int
+csio_scsim_init(struct csio_scsim *scm, struct csio_hw *hw)
+{
+ int i;
+ struct csio_ioreq *ioreq;
+ struct csio_dma_buf *dma_buf;
+
+ INIT_LIST_HEAD(&scm->active_q);
+ scm->hw = hw;
+
+ scm->proto_cmd_len = sizeof(struct fcp_cmnd);
+ scm->proto_rsp_len = CSIO_SCSI_RSP_LEN;
+ scm->max_sge = CSIO_SCSI_MAX_SGE;
+
+ spin_lock_init(&scm->freelist_lock);
+
+ /* Pre-allocate ioreqs and initialize them */
+ INIT_LIST_HEAD(&scm->ioreq_freelist);
+ for (i = 0; i < csio_scsi_ioreqs; i++) {
+
+ ioreq = kzalloc(sizeof(struct csio_ioreq), GFP_KERNEL);
+ if (!ioreq) {
+ csio_err(hw,
+ "I/O request element allocation failed, "
+ " Num allocated = %d.\n",
+ scm->stats.n_free_ioreq);
+
+ goto free_ioreq;
+ }
+
+ /* Allocate Dma buffers for Response Payload */
+ dma_buf = &ioreq->dma_buf;
+ dma_buf->vaddr = pci_pool_alloc(hw->scsi_pci_pool, GFP_KERNEL,
+ &dma_buf->paddr);
+ if (!dma_buf->vaddr) {
+ csio_err(hw,
+ "SCSI response DMA buffer allocation"
+ " failed!\n");
+ kfree(ioreq);
+ goto free_ioreq;
+ }
+
+ dma_buf->len = scm->proto_rsp_len;
+
+ /* Set state to uninit */
+ csio_init_state(&ioreq->sm, csio_scsis_uninit);
+ INIT_LIST_HEAD(&ioreq->gen_list);
+ init_completion(&ioreq->cmplobj);
+
+ list_add_tail(&ioreq->sm.sm_list, &scm->ioreq_freelist);
+ CSIO_INC_STATS(scm, n_free_ioreq);
+ }
+
+ if (csio_scsi_alloc_ddp_bufs(scm, hw, PAGE_SIZE, csio_ddp_descs))
+ goto free_ioreq;
+
+ return 0;
+
+free_ioreq:
+ /*
+ * Free up existing allocations, since an error
+ * from here means we are returning for good
+ */
+ while (!list_empty(&scm->ioreq_freelist)) {
+ struct csio_sm *tmp;
+
+ tmp = list_first_entry(&scm->ioreq_freelist,
+ struct csio_sm, sm_list);
+ list_del_init(&tmp->sm_list);
+ ioreq = (struct csio_ioreq *)tmp;
+
+ dma_buf = &ioreq->dma_buf;
+ pci_pool_free(hw->scsi_pci_pool, dma_buf->vaddr,
+ dma_buf->paddr);
+
+ kfree(ioreq);
+ }
+
+ scm->stats.n_free_ioreq = 0;
+
+ return -ENOMEM;
+}
+
+/**
+ * csio_scsim_exit: Uninitialize SCSI Module
+ * @scm: SCSI Module
+ *
+ */
+void
+csio_scsim_exit(struct csio_scsim *scm)
+{
+ struct csio_ioreq *ioreq;
+ struct csio_dma_buf *dma_buf;
+
+ while (!list_empty(&scm->ioreq_freelist)) {
+ struct csio_sm *tmp;
+
+ tmp = list_first_entry(&scm->ioreq_freelist,
+ struct csio_sm, sm_list);
+ list_del_init(&tmp->sm_list);
+ ioreq = (struct csio_ioreq *)tmp;
+
+ dma_buf = &ioreq->dma_buf;
+ pci_pool_free(scm->hw->scsi_pci_pool, dma_buf->vaddr,
+ dma_buf->paddr);
+
+ kfree(ioreq);
+ }
+
+ scm->stats.n_free_ioreq = 0;
+
+ csio_scsi_free_ddp_bufs(scm, scm->hw);
+}
diff --git a/drivers/scsi/csiostor/csio_scsi.h b/drivers/scsi/csiostor/csio_scsi.h
new file mode 100644
index 00000000000..2257c3dcf72
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_scsi.h
@@ -0,0 +1,342 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CSIO_SCSI_H__
+#define __CSIO_SCSI_H__
+
+#include <linux/spinlock_types.h>
+#include <linux/completion.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/fc/fc_fcp.h>
+
+#include "csio_defs.h"
+#include "csio_wr.h"
+
+extern struct scsi_host_template csio_fcoe_shost_template;
+extern struct scsi_host_template csio_fcoe_shost_vport_template;
+
+extern int csio_scsi_eqsize;
+extern int csio_scsi_iqlen;
+extern int csio_scsi_ioreqs;
+extern uint32_t csio_max_scan_tmo;
+extern uint32_t csio_delta_scan_tmo;
+extern int csio_lun_qdepth;
+
+/*
+ **************************** NOTE *******************************
+ * How do we calculate MAX FCoE SCSI SGEs? Here is the math:
+ * Max Egress WR size = 512 bytes
+ * One SCSI egress WR has the following fixed no of bytes:
+ * 48 (sizeof(struct fw_scsi_write[read]_wr)) - FW WR
+ * + 32 (sizeof(struct fc_fcp_cmnd)) - Immediate FCP_CMD
+ * ------
+ * 80
+ * ------
+ * That leaves us with 512 - 96 = 432 bytes for data SGE. Using
+ * struct ulptx_sgl header for the SGE consumes:
+ * - 4 bytes for cmnd_sge.
+ * - 12 bytes for the first SGL.
+ * That leaves us with 416 bytes for the remaining SGE pairs. Which is
+ * is 416 / 24 (size(struct ulptx_sge_pair)) = 17 SGE pairs,
+ * or 34 SGEs. Adding the first SGE fetches us 35 SGEs.
+ */
+#define CSIO_SCSI_MAX_SGE 35
+#define CSIO_SCSI_ABRT_TMO_MS 60000
+#define CSIO_SCSI_LUNRST_TMO_MS 60000
+#define CSIO_SCSI_TM_POLL_MS 2000 /* should be less than
+ * all TM timeouts.
+ */
+#define CSIO_SCSI_IQ_WRSZ 128
+#define CSIO_SCSI_IQSIZE (csio_scsi_iqlen * CSIO_SCSI_IQ_WRSZ)
+
+#define CSIO_MAX_SNS_LEN 128
+#define CSIO_SCSI_RSP_LEN (FCP_RESP_WITH_EXT + 4 + CSIO_MAX_SNS_LEN)
+
+/* Reference to scsi_cmnd */
+#define csio_scsi_cmnd(req) ((req)->scratch1)
+
+struct csio_scsi_stats {
+ uint64_t n_tot_success; /* Total number of good I/Os */
+ uint32_t n_rn_nr_error; /* No. of remote-node-not-
+ * ready errors
+ */
+ uint32_t n_hw_nr_error; /* No. of hw-module-not-
+ * ready errors
+ */
+ uint32_t n_dmamap_error; /* No. of DMA map erros */
+ uint32_t n_unsupp_sge_error; /* No. of too-many-SGes
+ * errors.
+ */
+ uint32_t n_no_req_error; /* No. of Out-of-ioreqs error */
+ uint32_t n_busy_error; /* No. of -EBUSY errors */
+ uint32_t n_hosterror; /* No. of FW_HOSTERROR I/O */
+ uint32_t n_rsperror; /* No. of response errors */
+ uint32_t n_autosense; /* No. of auto sense replies */
+ uint32_t n_ovflerror; /* No. of overflow errors */
+ uint32_t n_unflerror; /* No. of underflow errors */
+ uint32_t n_rdev_nr_error;/* No. of rdev not
+ * ready errors
+ */
+ uint32_t n_rdev_lost_error;/* No. of rdev lost errors */
+ uint32_t n_rdev_logo_error;/* No. of rdev logo errors */
+ uint32_t n_link_down_error;/* No. of link down errors */
+ uint32_t n_no_xchg_error; /* No. no exchange error */
+ uint32_t n_unknown_error;/* No. of unhandled errors */
+ uint32_t n_aborted; /* No. of aborted I/Os */
+ uint32_t n_abrt_timedout; /* No. of abort timedouts */
+ uint32_t n_abrt_fail; /* No. of abort failures */
+ uint32_t n_abrt_dups; /* No. of duplicate aborts */
+ uint32_t n_abrt_race_comp; /* No. of aborts that raced
+ * with completions.
+ */
+ uint32_t n_abrt_busy_error;/* No. of abort failures
+ * due to -EBUSY.
+ */
+ uint32_t n_closed; /* No. of closed I/Os */
+ uint32_t n_cls_busy_error; /* No. of close failures
+ * due to -EBUSY.
+ */
+ uint32_t n_active; /* No. of IOs in active_q */
+ uint32_t n_tm_active; /* No. of TMs in active_q */
+ uint32_t n_wcbfn; /* No. of I/Os in worker
+ * cbfn q
+ */
+ uint32_t n_free_ioreq; /* No. of freelist entries */
+ uint32_t n_free_ddp; /* No. of DDP freelist */
+ uint32_t n_unaligned; /* No. of Unaligned SGls */
+ uint32_t n_inval_cplop; /* No. invalid CPL op's in IQ */
+ uint32_t n_inval_scsiop; /* No. invalid scsi op's in IQ*/
+};
+
+struct csio_scsim {
+ struct csio_hw *hw; /* Pointer to HW moduel */
+ uint8_t max_sge; /* Max SGE */
+ uint8_t proto_cmd_len; /* Proto specific SCSI
+ * cmd length
+ */
+ uint16_t proto_rsp_len; /* Proto specific SCSI
+ * response length
+ */
+ spinlock_t freelist_lock; /* Lock for ioreq freelist */
+ struct list_head active_q; /* Outstanding SCSI I/Os */
+ struct list_head ioreq_freelist; /* Free list of ioreq's */
+ struct list_head ddp_freelist; /* DDP descriptor freelist */
+ struct csio_scsi_stats stats; /* This module's statistics */
+};
+
+/* State machine defines */
+enum csio_scsi_ev {
+ CSIO_SCSIE_START_IO = 1, /* Start a regular SCSI IO */
+ CSIO_SCSIE_START_TM, /* Start a TM IO */
+ CSIO_SCSIE_COMPLETED, /* IO Completed */
+ CSIO_SCSIE_ABORT, /* Abort IO */
+ CSIO_SCSIE_ABORTED, /* IO Aborted */
+ CSIO_SCSIE_CLOSE, /* Close exchange */
+ CSIO_SCSIE_CLOSED, /* Exchange closed */
+ CSIO_SCSIE_DRVCLEANUP, /* Driver wants to manually
+ * cleanup this I/O.
+ */
+};
+
+enum csio_scsi_lev {
+ CSIO_LEV_ALL = 1,
+ CSIO_LEV_LNODE,
+ CSIO_LEV_RNODE,
+ CSIO_LEV_LUN,
+};
+
+struct csio_scsi_level_data {
+ enum csio_scsi_lev level;
+ struct csio_rnode *rnode;
+ struct csio_lnode *lnode;
+ uint64_t oslun;
+};
+
+static inline struct csio_ioreq *
+csio_get_scsi_ioreq(struct csio_scsim *scm)
+{
+ struct csio_sm *req;
+
+ if (likely(!list_empty(&scm->ioreq_freelist))) {
+ req = list_first_entry(&scm->ioreq_freelist,
+ struct csio_sm, sm_list);
+ list_del_init(&req->sm_list);
+ CSIO_DEC_STATS(scm, n_free_ioreq);
+ return (struct csio_ioreq *)req;
+ } else
+ return NULL;
+}
+
+static inline void
+csio_put_scsi_ioreq(struct csio_scsim *scm, struct csio_ioreq *ioreq)
+{
+ list_add_tail(&ioreq->sm.sm_list, &scm->ioreq_freelist);
+ CSIO_INC_STATS(scm, n_free_ioreq);
+}
+
+static inline void
+csio_put_scsi_ioreq_list(struct csio_scsim *scm, struct list_head *reqlist,
+ int n)
+{
+ list_splice_init(reqlist, &scm->ioreq_freelist);
+ scm->stats.n_free_ioreq += n;
+}
+
+static inline struct csio_dma_buf *
+csio_get_scsi_ddp(struct csio_scsim *scm)
+{
+ struct csio_dma_buf *ddp;
+
+ if (likely(!list_empty(&scm->ddp_freelist))) {
+ ddp = list_first_entry(&scm->ddp_freelist,
+ struct csio_dma_buf, list);
+ list_del_init(&ddp->list);
+ CSIO_DEC_STATS(scm, n_free_ddp);
+ return ddp;
+ } else
+ return NULL;
+}
+
+static inline void
+csio_put_scsi_ddp(struct csio_scsim *scm, struct csio_dma_buf *ddp)
+{
+ list_add_tail(&ddp->list, &scm->ddp_freelist);
+ CSIO_INC_STATS(scm, n_free_ddp);
+}
+
+static inline void
+csio_put_scsi_ddp_list(struct csio_scsim *scm, struct list_head *reqlist,
+ int n)
+{
+ list_splice_tail_init(reqlist, &scm->ddp_freelist);
+ scm->stats.n_free_ddp += n;
+}
+
+static inline void
+csio_scsi_completed(struct csio_ioreq *ioreq, struct list_head *cbfn_q)
+{
+ csio_post_event(&ioreq->sm, CSIO_SCSIE_COMPLETED);
+ if (csio_list_deleted(&ioreq->sm.sm_list))
+ list_add_tail(&ioreq->sm.sm_list, cbfn_q);
+}
+
+static inline void
+csio_scsi_aborted(struct csio_ioreq *ioreq, struct list_head *cbfn_q)
+{
+ csio_post_event(&ioreq->sm, CSIO_SCSIE_ABORTED);
+ list_add_tail(&ioreq->sm.sm_list, cbfn_q);
+}
+
+static inline void
+csio_scsi_closed(struct csio_ioreq *ioreq, struct list_head *cbfn_q)
+{
+ csio_post_event(&ioreq->sm, CSIO_SCSIE_CLOSED);
+ list_add_tail(&ioreq->sm.sm_list, cbfn_q);
+}
+
+static inline void
+csio_scsi_drvcleanup(struct csio_ioreq *ioreq)
+{
+ csio_post_event(&ioreq->sm, CSIO_SCSIE_DRVCLEANUP);
+}
+
+/*
+ * csio_scsi_start_io - Kick starts the IO SM.
+ * @req: io request SM.
+ *
+ * needs to be called with lock held.
+ */
+static inline int
+csio_scsi_start_io(struct csio_ioreq *ioreq)
+{
+ csio_post_event(&ioreq->sm, CSIO_SCSIE_START_IO);
+ return ioreq->drv_status;
+}
+
+/*
+ * csio_scsi_start_tm - Kicks off the Task management IO SM.
+ * @req: io request SM.
+ *
+ * needs to be called with lock held.
+ */
+static inline int
+csio_scsi_start_tm(struct csio_ioreq *ioreq)
+{
+ csio_post_event(&ioreq->sm, CSIO_SCSIE_START_TM);
+ return ioreq->drv_status;
+}
+
+/*
+ * csio_scsi_abort - Abort an IO request
+ * @req: io request SM.
+ *
+ * needs to be called with lock held.
+ */
+static inline int
+csio_scsi_abort(struct csio_ioreq *ioreq)
+{
+ csio_post_event(&ioreq->sm, CSIO_SCSIE_ABORT);
+ return ioreq->drv_status;
+}
+
+/*
+ * csio_scsi_close - Close an IO request
+ * @req: io request SM.
+ *
+ * needs to be called with lock held.
+ */
+static inline int
+csio_scsi_close(struct csio_ioreq *ioreq)
+{
+ csio_post_event(&ioreq->sm, CSIO_SCSIE_CLOSE);
+ return ioreq->drv_status;
+}
+
+void csio_scsi_cleanup_io_q(struct csio_scsim *, struct list_head *);
+int csio_scsim_cleanup_io(struct csio_scsim *, bool abort);
+int csio_scsim_cleanup_io_lnode(struct csio_scsim *,
+ struct csio_lnode *);
+struct csio_ioreq *csio_scsi_cmpl_handler(struct csio_hw *, void *, uint32_t,
+ struct csio_fl_dma_buf *,
+ void *, uint8_t **);
+int csio_scsi_qconfig(struct csio_hw *);
+int csio_scsim_init(struct csio_scsim *, struct csio_hw *);
+void csio_scsim_exit(struct csio_scsim *);
+
+#endif /* __CSIO_SCSI_H__ */
diff --git a/drivers/scsi/csiostor/csio_wr.c b/drivers/scsi/csiostor/csio_wr.c
new file mode 100644
index 00000000000..c32df1bdaa9
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_wr.c
@@ -0,0 +1,1632 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/compiler.h>
+#include <linux/slab.h>
+#include <asm/page.h>
+#include <linux/cache.h>
+
+#include "csio_hw.h"
+#include "csio_wr.h"
+#include "csio_mb.h"
+#include "csio_defs.h"
+
+int csio_intr_coalesce_cnt; /* value:SGE_INGRESS_RX_THRESHOLD[0] */
+static int csio_sge_thresh_reg; /* SGE_INGRESS_RX_THRESHOLD[0] */
+
+int csio_intr_coalesce_time = 10; /* value:SGE_TIMER_VALUE_1 */
+static int csio_sge_timer_reg = 1;
+
+#define CSIO_SET_FLBUF_SIZE(_hw, _reg, _val) \
+ csio_wr_reg32((_hw), (_val), SGE_FL_BUFFER_SIZE##_reg)
+
+static void
+csio_get_flbuf_size(struct csio_hw *hw, struct csio_sge *sge, uint32_t reg)
+{
+ sge->sge_fl_buf_size[reg] = csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE0 +
+ reg * sizeof(uint32_t));
+}
+
+/* Free list buffer size */
+static inline uint32_t
+csio_wr_fl_bufsz(struct csio_sge *sge, struct csio_dma_buf *buf)
+{
+ return sge->sge_fl_buf_size[buf->paddr & 0xF];
+}
+
+/* Size of the egress queue status page */
+static inline uint32_t
+csio_wr_qstat_pgsz(struct csio_hw *hw)
+{
+ return (hw->wrm.sge.sge_control & EGRSTATUSPAGESIZE(1)) ? 128 : 64;
+}
+
+/* Ring freelist doorbell */
+static inline void
+csio_wr_ring_fldb(struct csio_hw *hw, struct csio_q *flq)
+{
+ /*
+ * Ring the doorbell only when we have atleast CSIO_QCREDIT_SZ
+ * number of bytes in the freelist queue. This translates to atleast
+ * 8 freelist buffer pointers (since each pointer is 8 bytes).
+ */
+ if (flq->inc_idx >= 8) {
+ csio_wr_reg32(hw, DBPRIO(1) | QID(flq->un.fl.flid) |
+ PIDX(flq->inc_idx / 8),
+ MYPF_REG(SGE_PF_KDOORBELL));
+ flq->inc_idx &= 7;
+ }
+}
+
+/* Write a 0 cidx increment value to enable SGE interrupts for this queue */
+static void
+csio_wr_sge_intr_enable(struct csio_hw *hw, uint16_t iqid)
+{
+ csio_wr_reg32(hw, CIDXINC(0) |
+ INGRESSQID(iqid) |
+ TIMERREG(X_TIMERREG_RESTART_COUNTER),
+ MYPF_REG(SGE_PF_GTS));
+}
+
+/*
+ * csio_wr_fill_fl - Populate the FL buffers of a FL queue.
+ * @hw: HW module.
+ * @flq: Freelist queue.
+ *
+ * Fill up freelist buffer entries with buffers of size specified
+ * in the size register.
+ *
+ */
+static int
+csio_wr_fill_fl(struct csio_hw *hw, struct csio_q *flq)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_sge *sge = &wrm->sge;
+ __be64 *d = (__be64 *)(flq->vstart);
+ struct csio_dma_buf *buf = &flq->un.fl.bufs[0];
+ uint64_t paddr;
+ int sreg = flq->un.fl.sreg;
+ int n = flq->credits;
+
+ while (n--) {
+ buf->len = sge->sge_fl_buf_size[sreg];
+ buf->vaddr = pci_alloc_consistent(hw->pdev, buf->len,
+ &buf->paddr);
+ if (!buf->vaddr) {
+ csio_err(hw, "Could only fill %d buffers!\n", n + 1);
+ return -ENOMEM;
+ }
+
+ paddr = buf->paddr | (sreg & 0xF);
+
+ *d++ = cpu_to_be64(paddr);
+ buf++;
+ }
+
+ return 0;
+}
+
+/*
+ * csio_wr_update_fl -
+ * @hw: HW module.
+ * @flq: Freelist queue.
+ *
+ *
+ */
+static inline void
+csio_wr_update_fl(struct csio_hw *hw, struct csio_q *flq, uint16_t n)
+{
+
+ flq->inc_idx += n;
+ flq->pidx += n;
+ if (unlikely(flq->pidx >= flq->credits))
+ flq->pidx -= (uint16_t)flq->credits;
+
+ CSIO_INC_STATS(flq, n_flq_refill);
+}
+
+/*
+ * csio_wr_alloc_q - Allocate a WR queue and initialize it.
+ * @hw: HW module
+ * @qsize: Size of the queue in bytes
+ * @wrsize: Since of WR in this queue, if fixed.
+ * @type: Type of queue (Ingress/Egress/Freelist)
+ * @owner: Module that owns this queue.
+ * @nflb: Number of freelist buffers for FL.
+ * @sreg: What is the FL buffer size register?
+ * @iq_int_handler: Ingress queue handler in INTx mode.
+ *
+ * This function allocates and sets up a queue for the caller
+ * of size qsize, aligned at the required boundary. This is subject to
+ * be free entries being available in the queue array. If one is found,
+ * it is initialized with the allocated queue, marked as being used (owner),
+ * and a handle returned to the caller in form of the queue's index
+ * into the q_arr array.
+ * If user has indicated a freelist (by specifying nflb > 0), create
+ * another queue (with its own index into q_arr) for the freelist. Allocate
+ * memory for DMA buffer metadata (vaddr, len etc). Save off the freelist
+ * idx in the ingress queue's flq.idx. This is how a Freelist is associated
+ * with its owning ingress queue.
+ */
+int
+csio_wr_alloc_q(struct csio_hw *hw, uint32_t qsize, uint32_t wrsize,
+ uint16_t type, void *owner, uint32_t nflb, int sreg,
+ iq_handler_t iq_intx_handler)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_q *q, *flq;
+ int free_idx = wrm->free_qidx;
+ int ret_idx = free_idx;
+ uint32_t qsz;
+ int flq_idx;
+
+ if (free_idx >= wrm->num_q) {
+ csio_err(hw, "No more free queues.\n");
+ return -1;
+ }
+
+ switch (type) {
+ case CSIO_EGRESS:
+ qsz = ALIGN(qsize, CSIO_QCREDIT_SZ) + csio_wr_qstat_pgsz(hw);
+ break;
+ case CSIO_INGRESS:
+ switch (wrsize) {
+ case 16:
+ case 32:
+ case 64:
+ case 128:
+ break;
+ default:
+ csio_err(hw, "Invalid Ingress queue WR size:%d\n",
+ wrsize);
+ return -1;
+ }
+
+ /*
+ * Number of elements must be a multiple of 16
+ * So this includes status page size
+ */
+ qsz = ALIGN(qsize/wrsize, 16) * wrsize;
+
+ break;
+ case CSIO_FREELIST:
+ qsz = ALIGN(qsize/wrsize, 8) * wrsize + csio_wr_qstat_pgsz(hw);
+ break;
+ default:
+ csio_err(hw, "Invalid queue type: 0x%x\n", type);
+ return -1;
+ }
+
+ q = wrm->q_arr[free_idx];
+
+ q->vstart = pci_alloc_consistent(hw->pdev, qsz, &q->pstart);
+ if (!q->vstart) {
+ csio_err(hw,
+ "Failed to allocate DMA memory for "
+ "queue at id: %d size: %d\n", free_idx, qsize);
+ return -1;
+ }
+
+ /*
+ * We need to zero out the contents, importantly for ingress,
+ * since we start with a generatiom bit of 1 for ingress.
+ */
+ memset(q->vstart, 0, qsz);
+
+ q->type = type;
+ q->owner = owner;
+ q->pidx = q->cidx = q->inc_idx = 0;
+ q->size = qsz;
+ q->wr_sz = wrsize; /* If using fixed size WRs */
+
+ wrm->free_qidx++;
+
+ if (type == CSIO_INGRESS) {
+ /* Since queue area is set to zero */
+ q->un.iq.genbit = 1;
+
+ /*
+ * Ingress queue status page size is always the size of
+ * the ingress queue entry.
+ */
+ q->credits = (qsz - q->wr_sz) / q->wr_sz;
+ q->vwrap = (void *)((uintptr_t)(q->vstart) + qsz
+ - q->wr_sz);
+
+ /* Allocate memory for FL if requested */
+ if (nflb > 0) {
+ flq_idx = csio_wr_alloc_q(hw, nflb * sizeof(__be64),
+ sizeof(__be64), CSIO_FREELIST,
+ owner, 0, sreg, NULL);
+ if (flq_idx == -1) {
+ csio_err(hw,
+ "Failed to allocate FL queue"
+ " for IQ idx:%d\n", free_idx);
+ return -1;
+ }
+
+ /* Associate the new FL with the Ingress quue */
+ q->un.iq.flq_idx = flq_idx;
+
+ flq = wrm->q_arr[q->un.iq.flq_idx];
+ flq->un.fl.bufs = kzalloc(flq->credits *
+ sizeof(struct csio_dma_buf),
+ GFP_KERNEL);
+ if (!flq->un.fl.bufs) {
+ csio_err(hw,
+ "Failed to allocate FL queue bufs"
+ " for IQ idx:%d\n", free_idx);
+ return -1;
+ }
+
+ flq->un.fl.packen = 0;
+ flq->un.fl.offset = 0;
+ flq->un.fl.sreg = sreg;
+
+ /* Fill up the free list buffers */
+ if (csio_wr_fill_fl(hw, flq))
+ return -1;
+
+ /*
+ * Make sure in a FLQ, atleast 1 credit (8 FL buffers)
+ * remains unpopulated,otherwise HW thinks
+ * FLQ is empty.
+ */
+ flq->pidx = flq->inc_idx = flq->credits - 8;
+ } else {
+ q->un.iq.flq_idx = -1;
+ }
+
+ /* Associate the IQ INTx handler. */
+ q->un.iq.iq_intx_handler = iq_intx_handler;
+
+ csio_q_iqid(hw, ret_idx) = CSIO_MAX_QID;
+
+ } else if (type == CSIO_EGRESS) {
+ q->credits = (qsz - csio_wr_qstat_pgsz(hw)) / CSIO_QCREDIT_SZ;
+ q->vwrap = (void *)((uintptr_t)(q->vstart) + qsz
+ - csio_wr_qstat_pgsz(hw));
+ csio_q_eqid(hw, ret_idx) = CSIO_MAX_QID;
+ } else { /* Freelist */
+ q->credits = (qsz - csio_wr_qstat_pgsz(hw)) / sizeof(__be64);
+ q->vwrap = (void *)((uintptr_t)(q->vstart) + qsz
+ - csio_wr_qstat_pgsz(hw));
+ csio_q_flid(hw, ret_idx) = CSIO_MAX_QID;
+ }
+
+ return ret_idx;
+}
+
+/*
+ * csio_wr_iq_create_rsp - Response handler for IQ creation.
+ * @hw: The HW module.
+ * @mbp: Mailbox.
+ * @iq_idx: Ingress queue that got created.
+ *
+ * Handle FW_IQ_CMD mailbox completion. Save off the assigned IQ/FL ids.
+ */
+static int
+csio_wr_iq_create_rsp(struct csio_hw *hw, struct csio_mb *mbp, int iq_idx)
+{
+ struct csio_iq_params iqp;
+ enum fw_retval retval;
+ uint32_t iq_id;
+ int flq_idx;
+
+ memset(&iqp, 0, sizeof(struct csio_iq_params));
+
+ csio_mb_iq_alloc_write_rsp(hw, mbp, &retval, &iqp);
+
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "IQ cmd returned 0x%x!\n", retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ csio_q_iqid(hw, iq_idx) = iqp.iqid;
+ csio_q_physiqid(hw, iq_idx) = iqp.physiqid;
+ csio_q_pidx(hw, iq_idx) = csio_q_cidx(hw, iq_idx) = 0;
+ csio_q_inc_idx(hw, iq_idx) = 0;
+
+ /* Actual iq-id. */
+ iq_id = iqp.iqid - hw->wrm.fw_iq_start;
+
+ /* Set the iq-id to iq map table. */
+ if (iq_id >= CSIO_MAX_IQ) {
+ csio_err(hw,
+ "Exceeding MAX_IQ(%d) supported!"
+ " iqid:%d rel_iqid:%d FW iq_start:%d\n",
+ CSIO_MAX_IQ, iq_id, iqp.iqid, hw->wrm.fw_iq_start);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+ csio_q_set_intr_map(hw, iq_idx, iq_id);
+
+ /*
+ * During FW_IQ_CMD, FW sets interrupt_sent bit to 1 in the SGE
+ * ingress context of this queue. This will block interrupts to
+ * this queue until the next GTS write. Therefore, we do a
+ * 0-cidx increment GTS write for this queue just to clear the
+ * interrupt_sent bit. This will re-enable interrupts to this
+ * queue.
+ */
+ csio_wr_sge_intr_enable(hw, iqp.physiqid);
+
+ flq_idx = csio_q_iq_flq_idx(hw, iq_idx);
+ if (flq_idx != -1) {
+ struct csio_q *flq = hw->wrm.q_arr[flq_idx];
+
+ csio_q_flid(hw, flq_idx) = iqp.fl0id;
+ csio_q_cidx(hw, flq_idx) = 0;
+ csio_q_pidx(hw, flq_idx) = csio_q_credits(hw, flq_idx) - 8;
+ csio_q_inc_idx(hw, flq_idx) = csio_q_credits(hw, flq_idx) - 8;
+
+ /* Now update SGE about the buffers allocated during init */
+ csio_wr_ring_fldb(hw, flq);
+ }
+
+ mempool_free(mbp, hw->mb_mempool);
+
+ return 0;
+}
+
+/*
+ * csio_wr_iq_create - Configure an Ingress queue with FW.
+ * @hw: The HW module.
+ * @priv: Private data object.
+ * @iq_idx: Ingress queue index in the WR module.
+ * @vec: MSIX vector.
+ * @portid: PCIE Channel to be associated with this queue.
+ * @async: Is this a FW asynchronous message handling queue?
+ * @cbfn: Completion callback.
+ *
+ * This API configures an ingress queue with FW by issuing a FW_IQ_CMD mailbox
+ * with alloc/write bits set.
+ */
+int
+csio_wr_iq_create(struct csio_hw *hw, void *priv, int iq_idx,
+ uint32_t vec, uint8_t portid, bool async,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct csio_mb *mbp;
+ struct csio_iq_params iqp;
+ int flq_idx;
+
+ memset(&iqp, 0, sizeof(struct csio_iq_params));
+ csio_q_portid(hw, iq_idx) = portid;
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ csio_err(hw, "IQ command out of memory!\n");
+ return -ENOMEM;
+ }
+
+ switch (hw->intr_mode) {
+ case CSIO_IM_INTX:
+ case CSIO_IM_MSI:
+ /* For interrupt forwarding queue only */
+ if (hw->intr_iq_idx == iq_idx)
+ iqp.iqandst = X_INTERRUPTDESTINATION_PCIE;
+ else
+ iqp.iqandst = X_INTERRUPTDESTINATION_IQ;
+ iqp.iqandstindex =
+ csio_q_physiqid(hw, hw->intr_iq_idx);
+ break;
+ case CSIO_IM_MSIX:
+ iqp.iqandst = X_INTERRUPTDESTINATION_PCIE;
+ iqp.iqandstindex = (uint16_t)vec;
+ break;
+ case CSIO_IM_NONE:
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ /* Pass in the ingress queue cmd parameters */
+ iqp.pfn = hw->pfn;
+ iqp.vfn = 0;
+ iqp.iq_start = 1;
+ iqp.viid = 0;
+ iqp.type = FW_IQ_TYPE_FL_INT_CAP;
+ iqp.iqasynch = async;
+ if (csio_intr_coalesce_cnt)
+ iqp.iqanus = X_UPDATESCHEDULING_COUNTER_OPTTIMER;
+ else
+ iqp.iqanus = X_UPDATESCHEDULING_TIMER;
+ iqp.iqanud = X_UPDATEDELIVERY_INTERRUPT;
+ iqp.iqpciech = portid;
+ iqp.iqintcntthresh = (uint8_t)csio_sge_thresh_reg;
+
+ switch (csio_q_wr_sz(hw, iq_idx)) {
+ case 16:
+ iqp.iqesize = 0; break;
+ case 32:
+ iqp.iqesize = 1; break;
+ case 64:
+ iqp.iqesize = 2; break;
+ case 128:
+ iqp.iqesize = 3; break;
+ }
+
+ iqp.iqsize = csio_q_size(hw, iq_idx) /
+ csio_q_wr_sz(hw, iq_idx);
+ iqp.iqaddr = csio_q_pstart(hw, iq_idx);
+
+ flq_idx = csio_q_iq_flq_idx(hw, iq_idx);
+ if (flq_idx != -1) {
+ struct csio_q *flq = hw->wrm.q_arr[flq_idx];
+
+ iqp.fl0paden = 1;
+ iqp.fl0packen = flq->un.fl.packen ? 1 : 0;
+ iqp.fl0fbmin = X_FETCHBURSTMIN_64B;
+ iqp.fl0fbmax = X_FETCHBURSTMAX_512B;
+ iqp.fl0size = csio_q_size(hw, flq_idx) / CSIO_QCREDIT_SZ;
+ iqp.fl0addr = csio_q_pstart(hw, flq_idx);
+ }
+
+ csio_mb_iq_alloc_write(hw, mbp, priv, CSIO_MB_DEFAULT_TMO, &iqp, cbfn);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of IQ cmd failed!\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ if (cbfn != NULL)
+ return 0;
+
+ return csio_wr_iq_create_rsp(hw, mbp, iq_idx);
+}
+
+/*
+ * csio_wr_eq_create_rsp - Response handler for EQ creation.
+ * @hw: The HW module.
+ * @mbp: Mailbox.
+ * @eq_idx: Egress queue that got created.
+ *
+ * Handle FW_EQ_OFLD_CMD mailbox completion. Save off the assigned EQ ids.
+ */
+static int
+csio_wr_eq_cfg_rsp(struct csio_hw *hw, struct csio_mb *mbp, int eq_idx)
+{
+ struct csio_eq_params eqp;
+ enum fw_retval retval;
+
+ memset(&eqp, 0, sizeof(struct csio_eq_params));
+
+ csio_mb_eq_ofld_alloc_write_rsp(hw, mbp, &retval, &eqp);
+
+ if (retval != FW_SUCCESS) {
+ csio_err(hw, "EQ OFLD cmd returned 0x%x!\n", retval);
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ csio_q_eqid(hw, eq_idx) = (uint16_t)eqp.eqid;
+ csio_q_physeqid(hw, eq_idx) = (uint16_t)eqp.physeqid;
+ csio_q_pidx(hw, eq_idx) = csio_q_cidx(hw, eq_idx) = 0;
+ csio_q_inc_idx(hw, eq_idx) = 0;
+
+ mempool_free(mbp, hw->mb_mempool);
+
+ return 0;
+}
+
+/*
+ * csio_wr_eq_create - Configure an Egress queue with FW.
+ * @hw: HW module.
+ * @priv: Private data.
+ * @eq_idx: Egress queue index in the WR module.
+ * @iq_idx: Associated ingress queue index.
+ * @cbfn: Completion callback.
+ *
+ * This API configures a offload egress queue with FW by issuing a
+ * FW_EQ_OFLD_CMD (with alloc + write ) mailbox.
+ */
+int
+csio_wr_eq_create(struct csio_hw *hw, void *priv, int eq_idx,
+ int iq_idx, uint8_t portid,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ struct csio_mb *mbp;
+ struct csio_eq_params eqp;
+
+ memset(&eqp, 0, sizeof(struct csio_eq_params));
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp) {
+ csio_err(hw, "EQ command out of memory!\n");
+ return -ENOMEM;
+ }
+
+ eqp.pfn = hw->pfn;
+ eqp.vfn = 0;
+ eqp.eqstart = 1;
+ eqp.hostfcmode = X_HOSTFCMODE_STATUS_PAGE;
+ eqp.iqid = csio_q_iqid(hw, iq_idx);
+ eqp.fbmin = X_FETCHBURSTMIN_64B;
+ eqp.fbmax = X_FETCHBURSTMAX_512B;
+ eqp.cidxfthresh = 0;
+ eqp.pciechn = portid;
+ eqp.eqsize = csio_q_size(hw, eq_idx) / CSIO_QCREDIT_SZ;
+ eqp.eqaddr = csio_q_pstart(hw, eq_idx);
+
+ csio_mb_eq_ofld_alloc_write(hw, mbp, priv, CSIO_MB_DEFAULT_TMO,
+ &eqp, cbfn);
+
+ if (csio_mb_issue(hw, mbp)) {
+ csio_err(hw, "Issue of EQ OFLD cmd failed!\n");
+ mempool_free(mbp, hw->mb_mempool);
+ return -EINVAL;
+ }
+
+ if (cbfn != NULL)
+ return 0;
+
+ return csio_wr_eq_cfg_rsp(hw, mbp, eq_idx);
+}
+
+/*
+ * csio_wr_iq_destroy_rsp - Response handler for IQ removal.
+ * @hw: The HW module.
+ * @mbp: Mailbox.
+ * @iq_idx: Ingress queue that was freed.
+ *
+ * Handle FW_IQ_CMD (free) mailbox completion.
+ */
+static int
+csio_wr_iq_destroy_rsp(struct csio_hw *hw, struct csio_mb *mbp, int iq_idx)
+{
+ enum fw_retval retval = csio_mb_fw_retval(mbp);
+ int rv = 0;
+
+ if (retval != FW_SUCCESS)
+ rv = -EINVAL;
+
+ mempool_free(mbp, hw->mb_mempool);
+
+ return rv;
+}
+
+/*
+ * csio_wr_iq_destroy - Free an ingress queue.
+ * @hw: The HW module.
+ * @priv: Private data object.
+ * @iq_idx: Ingress queue index to destroy
+ * @cbfn: Completion callback.
+ *
+ * This API frees an ingress queue by issuing the FW_IQ_CMD
+ * with the free bit set.
+ */
+static int
+csio_wr_iq_destroy(struct csio_hw *hw, void *priv, int iq_idx,
+ void (*cbfn)(struct csio_hw *, struct csio_mb *))
+{
+ int rv = 0;
+ struct csio_mb *mbp;
+ struct csio_iq_params iqp;
+ int flq_idx;
+
+ memset(&iqp, 0, sizeof(struct csio_iq_params));
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp)
+ return -ENOMEM;
+
+ iqp.pfn = hw->pfn;
+ iqp.vfn = 0;
+ iqp.iqid = csio_q_iqid(hw, iq_idx);
+ iqp.type = FW_IQ_TYPE_FL_INT_CAP;
+
+ flq_idx = csio_q_iq_flq_idx(hw, iq_idx);
+ if (flq_idx != -1)
+ iqp.fl0id = csio_q_flid(hw, flq_idx);
+ else
+ iqp.fl0id = 0xFFFF;
+
+ iqp.fl1id = 0xFFFF;
+
+ csio_mb_iq_free(hw, mbp, priv, CSIO_MB_DEFAULT_TMO, &iqp, cbfn);
+
+ rv = csio_mb_issue(hw, mbp);
+ if (rv != 0) {
+ mempool_free(mbp, hw->mb_mempool);
+ return rv;
+ }
+
+ if (cbfn != NULL)
+ return 0;
+
+ return csio_wr_iq_destroy_rsp(hw, mbp, iq_idx);
+}
+
+/*
+ * csio_wr_eq_destroy_rsp - Response handler for OFLD EQ creation.
+ * @hw: The HW module.
+ * @mbp: Mailbox.
+ * @eq_idx: Egress queue that was freed.
+ *
+ * Handle FW_OFLD_EQ_CMD (free) mailbox completion.
+ */
+static int
+csio_wr_eq_destroy_rsp(struct csio_hw *hw, struct csio_mb *mbp, int eq_idx)
+{
+ enum fw_retval retval = csio_mb_fw_retval(mbp);
+ int rv = 0;
+
+ if (retval != FW_SUCCESS)
+ rv = -EINVAL;
+
+ mempool_free(mbp, hw->mb_mempool);
+
+ return rv;
+}
+
+/*
+ * csio_wr_eq_destroy - Free an Egress queue.
+ * @hw: The HW module.
+ * @priv: Private data object.
+ * @eq_idx: Egress queue index to destroy
+ * @cbfn: Completion callback.
+ *
+ * This API frees an Egress queue by issuing the FW_EQ_OFLD_CMD
+ * with the free bit set.
+ */
+static int
+csio_wr_eq_destroy(struct csio_hw *hw, void *priv, int eq_idx,
+ void (*cbfn) (struct csio_hw *, struct csio_mb *))
+{
+ int rv = 0;
+ struct csio_mb *mbp;
+ struct csio_eq_params eqp;
+
+ memset(&eqp, 0, sizeof(struct csio_eq_params));
+
+ mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC);
+ if (!mbp)
+ return -ENOMEM;
+
+ eqp.pfn = hw->pfn;
+ eqp.vfn = 0;
+ eqp.eqid = csio_q_eqid(hw, eq_idx);
+
+ csio_mb_eq_ofld_free(hw, mbp, priv, CSIO_MB_DEFAULT_TMO, &eqp, cbfn);
+
+ rv = csio_mb_issue(hw, mbp);
+ if (rv != 0) {
+ mempool_free(mbp, hw->mb_mempool);
+ return rv;
+ }
+
+ if (cbfn != NULL)
+ return 0;
+
+ return csio_wr_eq_destroy_rsp(hw, mbp, eq_idx);
+}
+
+/*
+ * csio_wr_cleanup_eq_stpg - Cleanup Egress queue status page
+ * @hw: HW module
+ * @qidx: Egress queue index
+ *
+ * Cleanup the Egress queue status page.
+ */
+static void
+csio_wr_cleanup_eq_stpg(struct csio_hw *hw, int qidx)
+{
+ struct csio_q *q = csio_hw_to_wrm(hw)->q_arr[qidx];
+ struct csio_qstatus_page *stp = (struct csio_qstatus_page *)q->vwrap;
+
+ memset(stp, 0, sizeof(*stp));
+}
+
+/*
+ * csio_wr_cleanup_iq_ftr - Cleanup Footer entries in IQ
+ * @hw: HW module
+ * @qidx: Ingress queue index
+ *
+ * Cleanup the footer entries in the given ingress queue,
+ * set to 1 the internal copy of genbit.
+ */
+static void
+csio_wr_cleanup_iq_ftr(struct csio_hw *hw, int qidx)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_q *q = wrm->q_arr[qidx];
+ void *wr;
+ struct csio_iqwr_footer *ftr;
+ uint32_t i = 0;
+
+ /* set to 1 since we are just about zero out genbit */
+ q->un.iq.genbit = 1;
+
+ for (i = 0; i < q->credits; i++) {
+ /* Get the WR */
+ wr = (void *)((uintptr_t)q->vstart +
+ (i * q->wr_sz));
+ /* Get the footer */
+ ftr = (struct csio_iqwr_footer *)((uintptr_t)wr +
+ (q->wr_sz - sizeof(*ftr)));
+ /* Zero out footer */
+ memset(ftr, 0, sizeof(*ftr));
+ }
+}
+
+int
+csio_wr_destroy_queues(struct csio_hw *hw, bool cmd)
+{
+ int i, flq_idx;
+ struct csio_q *q;
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ int rv;
+
+ for (i = 0; i < wrm->free_qidx; i++) {
+ q = wrm->q_arr[i];
+
+ switch (q->type) {
+ case CSIO_EGRESS:
+ if (csio_q_eqid(hw, i) != CSIO_MAX_QID) {
+ csio_wr_cleanup_eq_stpg(hw, i);
+ if (!cmd) {
+ csio_q_eqid(hw, i) = CSIO_MAX_QID;
+ continue;
+ }
+
+ rv = csio_wr_eq_destroy(hw, NULL, i, NULL);
+ if ((rv == -EBUSY) || (rv == -ETIMEDOUT))
+ cmd = false;
+
+ csio_q_eqid(hw, i) = CSIO_MAX_QID;
+ }
+ case CSIO_INGRESS:
+ if (csio_q_iqid(hw, i) != CSIO_MAX_QID) {
+ csio_wr_cleanup_iq_ftr(hw, i);
+ if (!cmd) {
+ csio_q_iqid(hw, i) = CSIO_MAX_QID;
+ flq_idx = csio_q_iq_flq_idx(hw, i);
+ if (flq_idx != -1)
+ csio_q_flid(hw, flq_idx) =
+ CSIO_MAX_QID;
+ continue;
+ }
+
+ rv = csio_wr_iq_destroy(hw, NULL, i, NULL);
+ if ((rv == -EBUSY) || (rv == -ETIMEDOUT))
+ cmd = false;
+
+ csio_q_iqid(hw, i) = CSIO_MAX_QID;
+ flq_idx = csio_q_iq_flq_idx(hw, i);
+ if (flq_idx != -1)
+ csio_q_flid(hw, flq_idx) = CSIO_MAX_QID;
+ }
+ default:
+ break;
+ }
+ }
+
+ hw->flags &= ~CSIO_HWF_Q_FW_ALLOCED;
+
+ return 0;
+}
+
+/*
+ * csio_wr_get - Get requested size of WR entry/entries from queue.
+ * @hw: HW module.
+ * @qidx: Index of queue.
+ * @size: Cumulative size of Work request(s).
+ * @wrp: Work request pair.
+ *
+ * If requested credits are available, return the start address of the
+ * work request in the work request pair. Set pidx accordingly and
+ * return.
+ *
+ * NOTE about WR pair:
+ * ==================
+ * A WR can start towards the end of a queue, and then continue at the
+ * beginning, since the queue is considered to be circular. This will
+ * require a pair of address/size to be passed back to the caller -
+ * hence Work request pair format.
+ */
+int
+csio_wr_get(struct csio_hw *hw, int qidx, uint32_t size,
+ struct csio_wr_pair *wrp)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_q *q = wrm->q_arr[qidx];
+ void *cwr = (void *)((uintptr_t)(q->vstart) +
+ (q->pidx * CSIO_QCREDIT_SZ));
+ struct csio_qstatus_page *stp = (struct csio_qstatus_page *)q->vwrap;
+ uint16_t cidx = q->cidx = ntohs(stp->cidx);
+ uint16_t pidx = q->pidx;
+ uint32_t req_sz = ALIGN(size, CSIO_QCREDIT_SZ);
+ int req_credits = req_sz / CSIO_QCREDIT_SZ;
+ int credits;
+
+ CSIO_DB_ASSERT(q->owner != NULL);
+ CSIO_DB_ASSERT((qidx >= 0) && (qidx < wrm->free_qidx));
+ CSIO_DB_ASSERT(cidx <= q->credits);
+
+ /* Calculate credits */
+ if (pidx > cidx) {
+ credits = q->credits - (pidx - cidx) - 1;
+ } else if (cidx > pidx) {
+ credits = cidx - pidx - 1;
+ } else {
+ /* cidx == pidx, empty queue */
+ credits = q->credits;
+ CSIO_INC_STATS(q, n_qempty);
+ }
+
+ /*
+ * Check if we have enough credits.
+ * credits = 1 implies queue is full.
+ */
+ if (!credits || (req_credits > credits)) {
+ CSIO_INC_STATS(q, n_qfull);
+ return -EBUSY;
+ }
+
+ /*
+ * If we are here, we have enough credits to satisfy the
+ * request. Check if we are near the end of q, and if WR spills over.
+ * If it does, use the first addr/size to cover the queue until
+ * the end. Fit the remainder portion of the request at the top
+ * of queue and return it in the second addr/len. Set pidx
+ * accordingly.
+ */
+ if (unlikely(((uintptr_t)cwr + req_sz) > (uintptr_t)(q->vwrap))) {
+ wrp->addr1 = cwr;
+ wrp->size1 = (uint32_t)((uintptr_t)q->vwrap - (uintptr_t)cwr);
+ wrp->addr2 = q->vstart;
+ wrp->size2 = req_sz - wrp->size1;
+ q->pidx = (uint16_t)(ALIGN(wrp->size2, CSIO_QCREDIT_SZ) /
+ CSIO_QCREDIT_SZ);
+ CSIO_INC_STATS(q, n_qwrap);
+ CSIO_INC_STATS(q, n_eq_wr_split);
+ } else {
+ wrp->addr1 = cwr;
+ wrp->size1 = req_sz;
+ wrp->addr2 = NULL;
+ wrp->size2 = 0;
+ q->pidx += (uint16_t)req_credits;
+
+ /* We are the end of queue, roll back pidx to top of queue */
+ if (unlikely(q->pidx == q->credits)) {
+ q->pidx = 0;
+ CSIO_INC_STATS(q, n_qwrap);
+ }
+ }
+
+ q->inc_idx = (uint16_t)req_credits;
+
+ CSIO_INC_STATS(q, n_tot_reqs);
+
+ return 0;
+}
+
+/*
+ * csio_wr_copy_to_wrp - Copies given data into WR.
+ * @data_buf - Data buffer
+ * @wrp - Work request pair.
+ * @wr_off - Work request offset.
+ * @data_len - Data length.
+ *
+ * Copies the given data in Work Request. Work request pair(wrp) specifies
+ * address information of Work request.
+ * Returns: none
+ */
+void
+csio_wr_copy_to_wrp(void *data_buf, struct csio_wr_pair *wrp,
+ uint32_t wr_off, uint32_t data_len)
+{
+ uint32_t nbytes;
+
+ /* Number of space available in buffer addr1 of WRP */
+ nbytes = ((wrp->size1 - wr_off) >= data_len) ?
+ data_len : (wrp->size1 - wr_off);
+
+ memcpy((uint8_t *) wrp->addr1 + wr_off, data_buf, nbytes);
+ data_len -= nbytes;
+
+ /* Write the remaining data from the begining of circular buffer */
+ if (data_len) {
+ CSIO_DB_ASSERT(data_len <= wrp->size2);
+ CSIO_DB_ASSERT(wrp->addr2 != NULL);
+ memcpy(wrp->addr2, (uint8_t *) data_buf + nbytes, data_len);
+ }
+}
+
+/*
+ * csio_wr_issue - Notify chip of Work request.
+ * @hw: HW module.
+ * @qidx: Index of queue.
+ * @prio: 0: Low priority, 1: High priority
+ *
+ * Rings the SGE Doorbell by writing the current producer index of the passed
+ * in queue into the register.
+ *
+ */
+int
+csio_wr_issue(struct csio_hw *hw, int qidx, bool prio)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_q *q = wrm->q_arr[qidx];
+
+ CSIO_DB_ASSERT((qidx >= 0) && (qidx < wrm->free_qidx));
+
+ wmb();
+ /* Ring SGE Doorbell writing q->pidx into it */
+ csio_wr_reg32(hw, DBPRIO(prio) | QID(q->un.eq.physeqid) |
+ PIDX(q->inc_idx), MYPF_REG(SGE_PF_KDOORBELL));
+ q->inc_idx = 0;
+
+ return 0;
+}
+
+static inline uint32_t
+csio_wr_avail_qcredits(struct csio_q *q)
+{
+ if (q->pidx > q->cidx)
+ return q->pidx - q->cidx;
+ else if (q->cidx > q->pidx)
+ return q->credits - (q->cidx - q->pidx);
+ else
+ return 0; /* cidx == pidx, empty queue */
+}
+
+/*
+ * csio_wr_inval_flq_buf - Invalidate a free list buffer entry.
+ * @hw: HW module.
+ * @flq: The freelist queue.
+ *
+ * Invalidate the driver's version of a freelist buffer entry,
+ * without freeing the associated the DMA memory. The entry
+ * to be invalidated is picked up from the current Free list
+ * queue cidx.
+ *
+ */
+static inline void
+csio_wr_inval_flq_buf(struct csio_hw *hw, struct csio_q *flq)
+{
+ flq->cidx++;
+ if (flq->cidx == flq->credits) {
+ flq->cidx = 0;
+ CSIO_INC_STATS(flq, n_qwrap);
+ }
+}
+
+/*
+ * csio_wr_process_fl - Process a freelist completion.
+ * @hw: HW module.
+ * @q: The ingress queue attached to the Freelist.
+ * @wr: The freelist completion WR in the ingress queue.
+ * @len_to_qid: The lower 32-bits of the first flit of the RSP footer
+ * @iq_handler: Caller's handler for this completion.
+ * @priv: Private pointer of caller
+ *
+ */
+static inline void
+csio_wr_process_fl(struct csio_hw *hw, struct csio_q *q,
+ void *wr, uint32_t len_to_qid,
+ void (*iq_handler)(struct csio_hw *, void *,
+ uint32_t, struct csio_fl_dma_buf *,
+ void *),
+ void *priv)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_sge *sge = &wrm->sge;
+ struct csio_fl_dma_buf flb;
+ struct csio_dma_buf *buf, *fbuf;
+ uint32_t bufsz, len, lastlen = 0;
+ int n;
+ struct csio_q *flq = hw->wrm.q_arr[q->un.iq.flq_idx];
+
+ CSIO_DB_ASSERT(flq != NULL);
+
+ len = len_to_qid;
+
+ if (len & IQWRF_NEWBUF) {
+ if (flq->un.fl.offset > 0) {
+ csio_wr_inval_flq_buf(hw, flq);
+ flq->un.fl.offset = 0;
+ }
+ len = IQWRF_LEN_GET(len);
+ }
+
+ CSIO_DB_ASSERT(len != 0);
+
+ flb.totlen = len;
+
+ /* Consume all freelist buffers used for len bytes */
+ for (n = 0, fbuf = flb.flbufs; ; n++, fbuf++) {
+ buf = &flq->un.fl.bufs[flq->cidx];
+ bufsz = csio_wr_fl_bufsz(sge, buf);
+
+ fbuf->paddr = buf->paddr;
+ fbuf->vaddr = buf->vaddr;
+
+ flb.offset = flq->un.fl.offset;
+ lastlen = min(bufsz, len);
+ fbuf->len = lastlen;
+
+ len -= lastlen;
+ if (!len)
+ break;
+ csio_wr_inval_flq_buf(hw, flq);
+ }
+
+ flb.defer_free = flq->un.fl.packen ? 0 : 1;
+
+ iq_handler(hw, wr, q->wr_sz - sizeof(struct csio_iqwr_footer),
+ &flb, priv);
+
+ if (flq->un.fl.packen)
+ flq->un.fl.offset += ALIGN(lastlen, sge->csio_fl_align);
+ else
+ csio_wr_inval_flq_buf(hw, flq);
+
+}
+
+/*
+ * csio_is_new_iqwr - Is this a new Ingress queue entry ?
+ * @q: Ingress quueue.
+ * @ftr: Ingress queue WR SGE footer.
+ *
+ * The entry is new if our generation bit matches the corresponding
+ * bit in the footer of the current WR.
+ */
+static inline bool
+csio_is_new_iqwr(struct csio_q *q, struct csio_iqwr_footer *ftr)
+{
+ return (q->un.iq.genbit == (ftr->u.type_gen >> IQWRF_GEN_SHIFT));
+}
+
+/*
+ * csio_wr_process_iq - Process elements in Ingress queue.
+ * @hw: HW pointer
+ * @qidx: Index of queue
+ * @iq_handler: Handler for this queue
+ * @priv: Caller's private pointer
+ *
+ * This routine walks through every entry of the ingress queue, calling
+ * the provided iq_handler with the entry, until the generation bit
+ * flips.
+ */
+int
+csio_wr_process_iq(struct csio_hw *hw, struct csio_q *q,
+ void (*iq_handler)(struct csio_hw *, void *,
+ uint32_t, struct csio_fl_dma_buf *,
+ void *),
+ void *priv)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ void *wr = (void *)((uintptr_t)q->vstart + (q->cidx * q->wr_sz));
+ struct csio_iqwr_footer *ftr;
+ uint32_t wr_type, fw_qid, qid;
+ struct csio_q *q_completed;
+ struct csio_q *flq = csio_iq_has_fl(q) ?
+ wrm->q_arr[q->un.iq.flq_idx] : NULL;
+ int rv = 0;
+
+ /* Get the footer */
+ ftr = (struct csio_iqwr_footer *)((uintptr_t)wr +
+ (q->wr_sz - sizeof(*ftr)));
+
+ /*
+ * When q wrapped around last time, driver should have inverted
+ * ic.genbit as well.
+ */
+ while (csio_is_new_iqwr(q, ftr)) {
+
+ CSIO_DB_ASSERT(((uintptr_t)wr + q->wr_sz) <=
+ (uintptr_t)q->vwrap);
+ rmb();
+ wr_type = IQWRF_TYPE_GET(ftr->u.type_gen);
+
+ switch (wr_type) {
+ case X_RSPD_TYPE_CPL:
+ /* Subtract footer from WR len */
+ iq_handler(hw, wr, q->wr_sz - sizeof(*ftr), NULL, priv);
+ break;
+ case X_RSPD_TYPE_FLBUF:
+ csio_wr_process_fl(hw, q, wr,
+ ntohl(ftr->pldbuflen_qid),
+ iq_handler, priv);
+ break;
+ case X_RSPD_TYPE_INTR:
+ fw_qid = ntohl(ftr->pldbuflen_qid);
+ qid = fw_qid - wrm->fw_iq_start;
+ q_completed = hw->wrm.intr_map[qid];
+
+ if (unlikely(qid ==
+ csio_q_physiqid(hw, hw->intr_iq_idx))) {
+ /*
+ * We are already in the Forward Interrupt
+ * Interrupt Queue Service! Do-not service
+ * again!
+ *
+ */
+ } else {
+ CSIO_DB_ASSERT(q_completed);
+ CSIO_DB_ASSERT(
+ q_completed->un.iq.iq_intx_handler);
+
+ /* Call the queue handler. */
+ q_completed->un.iq.iq_intx_handler(hw, NULL,
+ 0, NULL, (void *)q_completed);
+ }
+ break;
+ default:
+ csio_warn(hw, "Unknown resp type 0x%x received\n",
+ wr_type);
+ CSIO_INC_STATS(q, n_rsp_unknown);
+ break;
+ }
+
+ /*
+ * Ingress *always* has fixed size WR entries. Therefore,
+ * there should always be complete WRs towards the end of
+ * queue.
+ */
+ if (((uintptr_t)wr + q->wr_sz) == (uintptr_t)q->vwrap) {
+
+ /* Roll over to start of queue */
+ q->cidx = 0;
+ wr = q->vstart;
+
+ /* Toggle genbit */
+ q->un.iq.genbit ^= 0x1;
+
+ CSIO_INC_STATS(q, n_qwrap);
+ } else {
+ q->cidx++;
+ wr = (void *)((uintptr_t)(q->vstart) +
+ (q->cidx * q->wr_sz));
+ }
+
+ ftr = (struct csio_iqwr_footer *)((uintptr_t)wr +
+ (q->wr_sz - sizeof(*ftr)));
+ q->inc_idx++;
+
+ } /* while (q->un.iq.genbit == hdr->genbit) */
+
+ /*
+ * We need to re-arm SGE interrupts in case we got a stray interrupt,
+ * especially in msix mode. With INTx, this may be a common occurence.
+ */
+ if (unlikely(!q->inc_idx)) {
+ CSIO_INC_STATS(q, n_stray_comp);
+ rv = -EINVAL;
+ goto restart;
+ }
+
+ /* Replenish free list buffers if pending falls below low water mark */
+ if (flq) {
+ uint32_t avail = csio_wr_avail_qcredits(flq);
+ if (avail <= 16) {
+ /* Make sure in FLQ, atleast 1 credit (8 FL buffers)
+ * remains unpopulated otherwise HW thinks
+ * FLQ is empty.
+ */
+ csio_wr_update_fl(hw, flq, (flq->credits - 8) - avail);
+ csio_wr_ring_fldb(hw, flq);
+ }
+ }
+
+restart:
+ /* Now inform SGE about our incremental index value */
+ csio_wr_reg32(hw, CIDXINC(q->inc_idx) |
+ INGRESSQID(q->un.iq.physiqid) |
+ TIMERREG(csio_sge_timer_reg),
+ MYPF_REG(SGE_PF_GTS));
+ q->stats.n_tot_rsps += q->inc_idx;
+
+ q->inc_idx = 0;
+
+ return rv;
+}
+
+int
+csio_wr_process_iq_idx(struct csio_hw *hw, int qidx,
+ void (*iq_handler)(struct csio_hw *, void *,
+ uint32_t, struct csio_fl_dma_buf *,
+ void *),
+ void *priv)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_q *iq = wrm->q_arr[qidx];
+
+ return csio_wr_process_iq(hw, iq, iq_handler, priv);
+}
+
+static int
+csio_closest_timer(struct csio_sge *s, int time)
+{
+ int i, delta, match = 0, min_delta = INT_MAX;
+
+ for (i = 0; i < ARRAY_SIZE(s->timer_val); i++) {
+ delta = time - s->timer_val[i];
+ if (delta < 0)
+ delta = -delta;
+ if (delta < min_delta) {
+ min_delta = delta;
+ match = i;
+ }
+ }
+ return match;
+}
+
+static int
+csio_closest_thresh(struct csio_sge *s, int cnt)
+{
+ int i, delta, match = 0, min_delta = INT_MAX;
+
+ for (i = 0; i < ARRAY_SIZE(s->counter_val); i++) {
+ delta = cnt - s->counter_val[i];
+ if (delta < 0)
+ delta = -delta;
+ if (delta < min_delta) {
+ min_delta = delta;
+ match = i;
+ }
+ }
+ return match;
+}
+
+static void
+csio_wr_fixup_host_params(struct csio_hw *hw)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_sge *sge = &wrm->sge;
+ uint32_t clsz = L1_CACHE_BYTES;
+ uint32_t s_hps = PAGE_SHIFT - 10;
+ uint32_t ingpad = 0;
+ uint32_t stat_len = clsz > 64 ? 128 : 64;
+
+ csio_wr_reg32(hw, HOSTPAGESIZEPF0(s_hps) | HOSTPAGESIZEPF1(s_hps) |
+ HOSTPAGESIZEPF2(s_hps) | HOSTPAGESIZEPF3(s_hps) |
+ HOSTPAGESIZEPF4(s_hps) | HOSTPAGESIZEPF5(s_hps) |
+ HOSTPAGESIZEPF6(s_hps) | HOSTPAGESIZEPF7(s_hps),
+ SGE_HOST_PAGE_SIZE);
+
+ sge->csio_fl_align = clsz < 32 ? 32 : clsz;
+ ingpad = ilog2(sge->csio_fl_align) - 5;
+
+ csio_set_reg_field(hw, SGE_CONTROL, INGPADBOUNDARY_MASK |
+ EGRSTATUSPAGESIZE(1),
+ INGPADBOUNDARY(ingpad) |
+ EGRSTATUSPAGESIZE(stat_len != 64));
+
+ /* FL BUFFER SIZE#0 is Page size i,e already aligned to cache line */
+ csio_wr_reg32(hw, PAGE_SIZE, SGE_FL_BUFFER_SIZE0);
+ csio_wr_reg32(hw,
+ (csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE2) +
+ sge->csio_fl_align - 1) & ~(sge->csio_fl_align - 1),
+ SGE_FL_BUFFER_SIZE2);
+ csio_wr_reg32(hw,
+ (csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE3) +
+ sge->csio_fl_align - 1) & ~(sge->csio_fl_align - 1),
+ SGE_FL_BUFFER_SIZE3);
+
+ csio_wr_reg32(hw, HPZ0(PAGE_SHIFT - 12), ULP_RX_TDDP_PSZ);
+
+ /* default value of rx_dma_offset of the NIC driver */
+ csio_set_reg_field(hw, SGE_CONTROL, PKTSHIFT_MASK,
+ PKTSHIFT(CSIO_SGE_RX_DMA_OFFSET));
+}
+
+static void
+csio_init_intr_coalesce_parms(struct csio_hw *hw)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_sge *sge = &wrm->sge;
+
+ csio_sge_thresh_reg = csio_closest_thresh(sge, csio_intr_coalesce_cnt);
+ if (csio_intr_coalesce_cnt) {
+ csio_sge_thresh_reg = 0;
+ csio_sge_timer_reg = X_TIMERREG_RESTART_COUNTER;
+ return;
+ }
+
+ csio_sge_timer_reg = csio_closest_timer(sge, csio_intr_coalesce_time);
+}
+
+/*
+ * csio_wr_get_sge - Get SGE register values.
+ * @hw: HW module.
+ *
+ * Used by non-master functions and by master-functions relying on config file.
+ */
+static void
+csio_wr_get_sge(struct csio_hw *hw)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_sge *sge = &wrm->sge;
+ uint32_t ingpad;
+ int i;
+ u32 timer_value_0_and_1, timer_value_2_and_3, timer_value_4_and_5;
+ u32 ingress_rx_threshold;
+
+ sge->sge_control = csio_rd_reg32(hw, SGE_CONTROL);
+
+ ingpad = INGPADBOUNDARY_GET(sge->sge_control);
+
+ switch (ingpad) {
+ case X_INGPCIEBOUNDARY_32B:
+ sge->csio_fl_align = 32; break;
+ case X_INGPCIEBOUNDARY_64B:
+ sge->csio_fl_align = 64; break;
+ case X_INGPCIEBOUNDARY_128B:
+ sge->csio_fl_align = 128; break;
+ case X_INGPCIEBOUNDARY_256B:
+ sge->csio_fl_align = 256; break;
+ case X_INGPCIEBOUNDARY_512B:
+ sge->csio_fl_align = 512; break;
+ case X_INGPCIEBOUNDARY_1024B:
+ sge->csio_fl_align = 1024; break;
+ case X_INGPCIEBOUNDARY_2048B:
+ sge->csio_fl_align = 2048; break;
+ case X_INGPCIEBOUNDARY_4096B:
+ sge->csio_fl_align = 4096; break;
+ }
+
+ for (i = 0; i < CSIO_SGE_FL_SIZE_REGS; i++)
+ csio_get_flbuf_size(hw, sge, i);
+
+ timer_value_0_and_1 = csio_rd_reg32(hw, SGE_TIMER_VALUE_0_AND_1);
+ timer_value_2_and_3 = csio_rd_reg32(hw, SGE_TIMER_VALUE_2_AND_3);
+ timer_value_4_and_5 = csio_rd_reg32(hw, SGE_TIMER_VALUE_4_AND_5);
+
+ sge->timer_val[0] = (uint16_t)csio_core_ticks_to_us(hw,
+ TIMERVALUE0_GET(timer_value_0_and_1));
+ sge->timer_val[1] = (uint16_t)csio_core_ticks_to_us(hw,
+ TIMERVALUE1_GET(timer_value_0_and_1));
+ sge->timer_val[2] = (uint16_t)csio_core_ticks_to_us(hw,
+ TIMERVALUE2_GET(timer_value_2_and_3));
+ sge->timer_val[3] = (uint16_t)csio_core_ticks_to_us(hw,
+ TIMERVALUE3_GET(timer_value_2_and_3));
+ sge->timer_val[4] = (uint16_t)csio_core_ticks_to_us(hw,
+ TIMERVALUE4_GET(timer_value_4_and_5));
+ sge->timer_val[5] = (uint16_t)csio_core_ticks_to_us(hw,
+ TIMERVALUE5_GET(timer_value_4_and_5));
+
+ ingress_rx_threshold = csio_rd_reg32(hw, SGE_INGRESS_RX_THRESHOLD);
+ sge->counter_val[0] = THRESHOLD_0_GET(ingress_rx_threshold);
+ sge->counter_val[1] = THRESHOLD_1_GET(ingress_rx_threshold);
+ sge->counter_val[2] = THRESHOLD_2_GET(ingress_rx_threshold);
+ sge->counter_val[3] = THRESHOLD_3_GET(ingress_rx_threshold);
+
+ csio_init_intr_coalesce_parms(hw);
+}
+
+/*
+ * csio_wr_set_sge - Initialize SGE registers
+ * @hw: HW module.
+ *
+ * Used by Master function to initialize SGE registers in the absence
+ * of a config file.
+ */
+static void
+csio_wr_set_sge(struct csio_hw *hw)
+{
+ struct csio_wrm *wrm = csio_hw_to_wrm(hw);
+ struct csio_sge *sge = &wrm->sge;
+ int i;
+
+ /*
+ * Set up our basic SGE mode to deliver CPL messages to our Ingress
+ * Queue and Packet Date to the Free List.
+ */
+ csio_set_reg_field(hw, SGE_CONTROL, RXPKTCPLMODE(1), RXPKTCPLMODE(1));
+
+ sge->sge_control = csio_rd_reg32(hw, SGE_CONTROL);
+
+ /* sge->csio_fl_align is set up by csio_wr_fixup_host_params(). */
+
+ /*
+ * Set up to drop DOORBELL writes when the DOORBELL FIFO overflows
+ * and generate an interrupt when this occurs so we can recover.
+ */
+ csio_set_reg_field(hw, SGE_DBFIFO_STATUS,
+ HP_INT_THRESH(HP_INT_THRESH_MASK) |
+ LP_INT_THRESH(LP_INT_THRESH_MASK),
+ HP_INT_THRESH(CSIO_SGE_DBFIFO_INT_THRESH) |
+ LP_INT_THRESH(CSIO_SGE_DBFIFO_INT_THRESH));
+ csio_set_reg_field(hw, SGE_DOORBELL_CONTROL, ENABLE_DROP,
+ ENABLE_DROP);
+
+ /* SGE_FL_BUFFER_SIZE0 is set up by csio_wr_fixup_host_params(). */
+
+ CSIO_SET_FLBUF_SIZE(hw, 1, CSIO_SGE_FLBUF_SIZE1);
+ CSIO_SET_FLBUF_SIZE(hw, 2, CSIO_SGE_FLBUF_SIZE2);
+ CSIO_SET_FLBUF_SIZE(hw, 3, CSIO_SGE_FLBUF_SIZE3);
+ CSIO_SET_FLBUF_SIZE(hw, 4, CSIO_SGE_FLBUF_SIZE4);
+ CSIO_SET_FLBUF_SIZE(hw, 5, CSIO_SGE_FLBUF_SIZE5);
+ CSIO_SET_FLBUF_SIZE(hw, 6, CSIO_SGE_FLBUF_SIZE6);
+ CSIO_SET_FLBUF_SIZE(hw, 7, CSIO_SGE_FLBUF_SIZE7);
+ CSIO_SET_FLBUF_SIZE(hw, 8, CSIO_SGE_FLBUF_SIZE8);
+
+ for (i = 0; i < CSIO_SGE_FL_SIZE_REGS; i++)
+ csio_get_flbuf_size(hw, sge, i);
+
+ /* Initialize interrupt coalescing attributes */
+ sge->timer_val[0] = CSIO_SGE_TIMER_VAL_0;
+ sge->timer_val[1] = CSIO_SGE_TIMER_VAL_1;
+ sge->timer_val[2] = CSIO_SGE_TIMER_VAL_2;
+ sge->timer_val[3] = CSIO_SGE_TIMER_VAL_3;
+ sge->timer_val[4] = CSIO_SGE_TIMER_VAL_4;
+ sge->timer_val[5] = CSIO_SGE_TIMER_VAL_5;
+
+ sge->counter_val[0] = CSIO_SGE_INT_CNT_VAL_0;
+ sge->counter_val[1] = CSIO_SGE_INT_CNT_VAL_1;
+ sge->counter_val[2] = CSIO_SGE_INT_CNT_VAL_2;
+ sge->counter_val[3] = CSIO_SGE_INT_CNT_VAL_3;
+
+ csio_wr_reg32(hw, THRESHOLD_0(sge->counter_val[0]) |
+ THRESHOLD_1(sge->counter_val[1]) |
+ THRESHOLD_2(sge->counter_val[2]) |
+ THRESHOLD_3(sge->counter_val[3]),
+ SGE_INGRESS_RX_THRESHOLD);
+
+ csio_wr_reg32(hw,
+ TIMERVALUE0(csio_us_to_core_ticks(hw, sge->timer_val[0])) |
+ TIMERVALUE1(csio_us_to_core_ticks(hw, sge->timer_val[1])),
+ SGE_TIMER_VALUE_0_AND_1);
+
+ csio_wr_reg32(hw,
+ TIMERVALUE2(csio_us_to_core_ticks(hw, sge->timer_val[2])) |
+ TIMERVALUE3(csio_us_to_core_ticks(hw, sge->timer_val[3])),
+ SGE_TIMER_VALUE_2_AND_3);
+
+ csio_wr_reg32(hw,
+ TIMERVALUE4(csio_us_to_core_ticks(hw, sge->timer_val[4])) |
+ TIMERVALUE5(csio_us_to_core_ticks(hw, sge->timer_val[5])),
+ SGE_TIMER_VALUE_4_AND_5);
+
+ csio_init_intr_coalesce_parms(hw);
+}
+
+void
+csio_wr_sge_init(struct csio_hw *hw)
+{
+ /*
+ * If we are master:
+ * - If we plan to use the config file, we need to fixup some
+ * host specific registers, and read the rest of the SGE
+ * configuration.
+ * - If we dont plan to use the config file, we need to initialize
+ * SGE entirely, including fixing the host specific registers.
+ * If we arent the master, we are only allowed to read and work off of
+ * the already initialized SGE values.
+ *
+ * Therefore, before calling this function, we assume that the master-
+ * ship of the card, and whether to use config file or not, have
+ * already been decided. In other words, CSIO_HWF_USING_SOFT_PARAMS and
+ * CSIO_HWF_MASTER should be set/unset.
+ */
+ if (csio_is_hw_master(hw)) {
+ csio_wr_fixup_host_params(hw);
+
+ if (hw->flags & CSIO_HWF_USING_SOFT_PARAMS)
+ csio_wr_get_sge(hw);
+ else
+ csio_wr_set_sge(hw);
+ } else
+ csio_wr_get_sge(hw);
+}
+
+/*
+ * csio_wrm_init - Initialize Work request module.
+ * @wrm: WR module
+ * @hw: HW pointer
+ *
+ * Allocates memory for an array of queue pointers starting at q_arr.
+ */
+int
+csio_wrm_init(struct csio_wrm *wrm, struct csio_hw *hw)
+{
+ int i;
+
+ if (!wrm->num_q) {
+ csio_err(hw, "Num queues is not set\n");
+ return -EINVAL;
+ }
+
+ wrm->q_arr = kzalloc(sizeof(struct csio_q *) * wrm->num_q, GFP_KERNEL);
+ if (!wrm->q_arr)
+ goto err;
+
+ for (i = 0; i < wrm->num_q; i++) {
+ wrm->q_arr[i] = kzalloc(sizeof(struct csio_q), GFP_KERNEL);
+ if (!wrm->q_arr[i]) {
+ while (--i >= 0)
+ kfree(wrm->q_arr[i]);
+ goto err_free_arr;
+ }
+ }
+ wrm->free_qidx = 0;
+
+ return 0;
+
+err_free_arr:
+ kfree(wrm->q_arr);
+err:
+ return -ENOMEM;
+}
+
+/*
+ * csio_wrm_exit - Initialize Work request module.
+ * @wrm: WR module
+ * @hw: HW module
+ *
+ * Uninitialize WR module. Free q_arr and pointers in it.
+ * We have the additional job of freeing the DMA memory associated
+ * with the queues.
+ */
+void
+csio_wrm_exit(struct csio_wrm *wrm, struct csio_hw *hw)
+{
+ int i;
+ uint32_t j;
+ struct csio_q *q;
+ struct csio_dma_buf *buf;
+
+ for (i = 0; i < wrm->num_q; i++) {
+ q = wrm->q_arr[i];
+
+ if (wrm->free_qidx && (i < wrm->free_qidx)) {
+ if (q->type == CSIO_FREELIST) {
+ if (!q->un.fl.bufs)
+ continue;
+ for (j = 0; j < q->credits; j++) {
+ buf = &q->un.fl.bufs[j];
+ if (!buf->vaddr)
+ continue;
+ pci_free_consistent(hw->pdev, buf->len,
+ buf->vaddr,
+ buf->paddr);
+ }
+ kfree(q->un.fl.bufs);
+ }
+ pci_free_consistent(hw->pdev, q->size,
+ q->vstart, q->pstart);
+ }
+ kfree(q);
+ }
+
+ hw->flags &= ~CSIO_HWF_Q_MEM_ALLOCED;
+
+ kfree(wrm->q_arr);
+}
diff --git a/drivers/scsi/csiostor/csio_wr.h b/drivers/scsi/csiostor/csio_wr.h
new file mode 100644
index 00000000000..8d30e7ac1f5
--- /dev/null
+++ b/drivers/scsi/csiostor/csio_wr.h
@@ -0,0 +1,512 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __CSIO_WR_H__
+#define __CSIO_WR_H__
+
+#include <linux/cache.h>
+
+#include "csio_defs.h"
+#include "t4fw_api.h"
+#include "t4fw_api_stor.h"
+
+/*
+ * SGE register field values.
+ */
+#define X_INGPCIEBOUNDARY_32B 0
+#define X_INGPCIEBOUNDARY_64B 1
+#define X_INGPCIEBOUNDARY_128B 2
+#define X_INGPCIEBOUNDARY_256B 3
+#define X_INGPCIEBOUNDARY_512B 4
+#define X_INGPCIEBOUNDARY_1024B 5
+#define X_INGPCIEBOUNDARY_2048B 6
+#define X_INGPCIEBOUNDARY_4096B 7
+
+/* GTS register */
+#define X_TIMERREG_COUNTER0 0
+#define X_TIMERREG_COUNTER1 1
+#define X_TIMERREG_COUNTER2 2
+#define X_TIMERREG_COUNTER3 3
+#define X_TIMERREG_COUNTER4 4
+#define X_TIMERREG_COUNTER5 5
+#define X_TIMERREG_RESTART_COUNTER 6
+#define X_TIMERREG_UPDATE_CIDX 7
+
+/*
+ * Egress Context field values
+ */
+#define X_FETCHBURSTMIN_16B 0
+#define X_FETCHBURSTMIN_32B 1
+#define X_FETCHBURSTMIN_64B 2
+#define X_FETCHBURSTMIN_128B 3
+
+#define X_FETCHBURSTMAX_64B 0
+#define X_FETCHBURSTMAX_128B 1
+#define X_FETCHBURSTMAX_256B 2
+#define X_FETCHBURSTMAX_512B 3
+
+#define X_HOSTFCMODE_NONE 0
+#define X_HOSTFCMODE_INGRESS_QUEUE 1
+#define X_HOSTFCMODE_STATUS_PAGE 2
+#define X_HOSTFCMODE_BOTH 3
+
+/*
+ * Ingress Context field values
+ */
+#define X_UPDATESCHEDULING_TIMER 0
+#define X_UPDATESCHEDULING_COUNTER_OPTTIMER 1
+
+#define X_UPDATEDELIVERY_NONE 0
+#define X_UPDATEDELIVERY_INTERRUPT 1
+#define X_UPDATEDELIVERY_STATUS_PAGE 2
+#define X_UPDATEDELIVERY_BOTH 3
+
+#define X_INTERRUPTDESTINATION_PCIE 0
+#define X_INTERRUPTDESTINATION_IQ 1
+
+#define X_RSPD_TYPE_FLBUF 0
+#define X_RSPD_TYPE_CPL 1
+#define X_RSPD_TYPE_INTR 2
+
+/* WR status is at the same position as retval in a CMD header */
+#define csio_wr_status(_wr) \
+ (FW_CMD_RETVAL_GET(ntohl(((struct fw_cmd_hdr *)(_wr))->lo)))
+
+struct csio_hw;
+
+extern int csio_intr_coalesce_cnt;
+extern int csio_intr_coalesce_time;
+
+/* Ingress queue params */
+struct csio_iq_params {
+
+ uint8_t iq_start:1;
+ uint8_t iq_stop:1;
+ uint8_t pfn:3;
+
+ uint8_t vfn;
+
+ uint16_t physiqid;
+ uint16_t iqid;
+
+ uint16_t fl0id;
+ uint16_t fl1id;
+
+ uint8_t viid;
+
+ uint8_t type;
+ uint8_t iqasynch;
+ uint8_t reserved4;
+
+ uint8_t iqandst;
+ uint8_t iqanus;
+ uint8_t iqanud;
+
+ uint16_t iqandstindex;
+
+ uint8_t iqdroprss;
+ uint8_t iqpciech;
+ uint8_t iqdcaen;
+
+ uint8_t iqdcacpu;
+ uint8_t iqintcntthresh;
+ uint8_t iqo;
+
+ uint8_t iqcprio;
+ uint8_t iqesize;
+
+ uint16_t iqsize;
+
+ uint64_t iqaddr;
+
+ uint8_t iqflintiqhsen;
+ uint8_t reserved5;
+ uint8_t iqflintcongen;
+ uint8_t iqflintcngchmap;
+
+ uint32_t reserved6;
+
+ uint8_t fl0hostfcmode;
+ uint8_t fl0cprio;
+ uint8_t fl0paden;
+ uint8_t fl0packen;
+ uint8_t fl0congen;
+ uint8_t fl0dcaen;
+
+ uint8_t fl0dcacpu;
+ uint8_t fl0fbmin;
+
+ uint8_t fl0fbmax;
+ uint8_t fl0cidxfthresho;
+ uint8_t fl0cidxfthresh;
+
+ uint16_t fl0size;
+
+ uint64_t fl0addr;
+
+ uint64_t reserved7;
+
+ uint8_t fl1hostfcmode;
+ uint8_t fl1cprio;
+ uint8_t fl1paden;
+ uint8_t fl1packen;
+ uint8_t fl1congen;
+ uint8_t fl1dcaen;
+
+ uint8_t fl1dcacpu;
+ uint8_t fl1fbmin;
+
+ uint8_t fl1fbmax;
+ uint8_t fl1cidxfthresho;
+ uint8_t fl1cidxfthresh;
+
+ uint16_t fl1size;
+
+ uint64_t fl1addr;
+};
+
+/* Egress queue params */
+struct csio_eq_params {
+
+ uint8_t pfn;
+ uint8_t vfn;
+
+ uint8_t eqstart:1;
+ uint8_t eqstop:1;
+
+ uint16_t physeqid;
+ uint32_t eqid;
+
+ uint8_t hostfcmode:2;
+ uint8_t cprio:1;
+ uint8_t pciechn:3;
+
+ uint16_t iqid;
+
+ uint8_t dcaen:1;
+ uint8_t dcacpu:5;
+
+ uint8_t fbmin:3;
+ uint8_t fbmax:3;
+
+ uint8_t cidxfthresho:1;
+ uint8_t cidxfthresh:3;
+
+ uint16_t eqsize;
+
+ uint64_t eqaddr;
+};
+
+struct csio_dma_buf {
+ struct list_head list;
+ void *vaddr; /* Virtual address */
+ dma_addr_t paddr; /* Physical address */
+ uint32_t len; /* Buffer size */
+};
+
+/* Generic I/O request structure */
+struct csio_ioreq {
+ struct csio_sm sm; /* SM, List
+ * should be the first member
+ */
+ int iq_idx; /* Ingress queue index */
+ int eq_idx; /* Egress queue index */
+ uint32_t nsge; /* Number of SG elements */
+ uint32_t tmo; /* Driver timeout */
+ uint32_t datadir; /* Data direction */
+ struct csio_dma_buf dma_buf; /* Req/resp DMA buffers */
+ uint16_t wr_status; /* WR completion status */
+ int16_t drv_status; /* Driver internal status */
+ struct csio_lnode *lnode; /* Owner lnode */
+ struct csio_rnode *rnode; /* Src/destination rnode */
+ void (*io_cbfn) (struct csio_hw *, struct csio_ioreq *);
+ /* completion callback */
+ void *scratch1; /* Scratch area 1.
+ */
+ void *scratch2; /* Scratch area 2. */
+ struct list_head gen_list; /* Any list associated with
+ * this ioreq.
+ */
+ uint64_t fw_handle; /* Unique handle passed
+ * to FW
+ */
+ uint8_t dcopy; /* Data copy required */
+ uint8_t reserved1;
+ uint16_t reserved2;
+ struct completion cmplobj; /* ioreq completion object */
+} ____cacheline_aligned_in_smp;
+
+/*
+ * Egress status page for egress cidx updates
+ */
+struct csio_qstatus_page {
+ __be32 qid;
+ __be16 cidx;
+ __be16 pidx;
+};
+
+
+enum {
+ CSIO_MAX_FLBUF_PER_IQWR = 4,
+ CSIO_QCREDIT_SZ = 64, /* pidx/cidx increments
+ * in bytes
+ */
+ CSIO_MAX_QID = 0xFFFF,
+ CSIO_MAX_IQ = 128,
+
+ CSIO_SGE_NTIMERS = 6,
+ CSIO_SGE_NCOUNTERS = 4,
+ CSIO_SGE_FL_SIZE_REGS = 16,
+};
+
+/* Defines for type */
+enum {
+ CSIO_EGRESS = 1,
+ CSIO_INGRESS = 2,
+ CSIO_FREELIST = 3,
+};
+
+/*
+ * Structure for footer (last 2 flits) of Ingress Queue Entry.
+ */
+struct csio_iqwr_footer {
+ __be32 hdrbuflen_pidx;
+ __be32 pldbuflen_qid;
+ union {
+ u8 type_gen;
+ __be64 last_flit;
+ } u;
+};
+
+#define IQWRF_NEWBUF (1 << 31)
+#define IQWRF_LEN_GET(x) (((x) >> 0) & 0x7fffffffU)
+#define IQWRF_GEN_SHIFT 7
+#define IQWRF_TYPE_GET(x) (((x) >> 4) & 0x3U)
+
+
+/*
+ * WR pair:
+ * ========
+ * A WR can start towards the end of a queue, and then continue at the
+ * beginning, since the queue is considered to be circular. This will
+ * require a pair of address/len to be passed back to the caller -
+ * hence the Work request pair structure.
+ */
+struct csio_wr_pair {
+ void *addr1;
+ uint32_t size1;
+ void *addr2;
+ uint32_t size2;
+};
+
+/*
+ * The following structure is used by ingress processing to return the
+ * free list buffers to consumers.
+ */
+struct csio_fl_dma_buf {
+ struct csio_dma_buf flbufs[CSIO_MAX_FLBUF_PER_IQWR];
+ /* Freelist DMA buffers */
+ int offset; /* Offset within the
+ * first FL buf.
+ */
+ uint32_t totlen; /* Total length */
+ uint8_t defer_free; /* Free of buffer can
+ * deferred
+ */
+};
+
+/* Data-types */
+typedef void (*iq_handler_t)(struct csio_hw *, void *, uint32_t,
+ struct csio_fl_dma_buf *, void *);
+
+struct csio_iq {
+ uint16_t iqid; /* Queue ID */
+ uint16_t physiqid; /* Physical Queue ID */
+ uint16_t genbit; /* Generation bit,
+ * initially set to 1
+ */
+ int flq_idx; /* Freelist queue index */
+ iq_handler_t iq_intx_handler; /* IQ INTx handler routine */
+};
+
+struct csio_eq {
+ uint16_t eqid; /* Qid */
+ uint16_t physeqid; /* Physical Queue ID */
+ uint8_t wrap[512]; /* Temp area for q-wrap around*/
+};
+
+struct csio_fl {
+ uint16_t flid; /* Qid */
+ uint16_t packen; /* Packing enabled? */
+ int offset; /* Offset within FL buf */
+ int sreg; /* Size register */
+ struct csio_dma_buf *bufs; /* Free list buffer ptr array
+ * indexed using flq->cidx/pidx
+ */
+};
+
+struct csio_qstats {
+ uint32_t n_tot_reqs; /* Total no. of Requests */
+ uint32_t n_tot_rsps; /* Total no. of responses */
+ uint32_t n_qwrap; /* Queue wraps */
+ uint32_t n_eq_wr_split; /* Number of split EQ WRs */
+ uint32_t n_qentry; /* Queue entry */
+ uint32_t n_qempty; /* Queue empty */
+ uint32_t n_qfull; /* Queue fulls */
+ uint32_t n_rsp_unknown; /* Unknown response type */
+ uint32_t n_stray_comp; /* Stray completion intr */
+ uint32_t n_flq_refill; /* Number of FL refills */
+};
+
+/* Queue metadata */
+struct csio_q {
+ uint16_t type; /* Type: Ingress/Egress/FL */
+ uint16_t pidx; /* producer index */
+ uint16_t cidx; /* consumer index */
+ uint16_t inc_idx; /* Incremental index */
+ uint32_t wr_sz; /* Size of all WRs in this q
+ * if fixed
+ */
+ void *vstart; /* Base virtual address
+ * of queue
+ */
+ void *vwrap; /* Virtual end address to
+ * wrap around at
+ */
+ uint32_t credits; /* Size of queue in credits */
+ void *owner; /* Owner */
+ union { /* Queue contexts */
+ struct csio_iq iq;
+ struct csio_eq eq;
+ struct csio_fl fl;
+ } un;
+
+ dma_addr_t pstart; /* Base physical address of
+ * queue
+ */
+ uint32_t portid; /* PCIE Channel */
+ uint32_t size; /* Size of queue in bytes */
+ struct csio_qstats stats; /* Statistics */
+} ____cacheline_aligned_in_smp;
+
+struct csio_sge {
+ uint32_t csio_fl_align; /* Calculated and cached
+ * for fast path
+ */
+ uint32_t sge_control; /* padding, boundaries,
+ * lengths, etc.
+ */
+ uint32_t sge_host_page_size; /* Host page size */
+ uint32_t sge_fl_buf_size[CSIO_SGE_FL_SIZE_REGS];
+ /* free list buffer sizes */
+ uint16_t timer_val[CSIO_SGE_NTIMERS];
+ uint8_t counter_val[CSIO_SGE_NCOUNTERS];
+};
+
+/* Work request module */
+struct csio_wrm {
+ int num_q; /* Number of queues */
+ struct csio_q **q_arr; /* Array of queue pointers
+ * allocated dynamically
+ * based on configured values
+ */
+ uint32_t fw_iq_start; /* Start ID of IQ for this fn*/
+ uint32_t fw_eq_start; /* Start ID of EQ for this fn*/
+ struct csio_q *intr_map[CSIO_MAX_IQ];
+ /* IQ-id to IQ map table. */
+ int free_qidx; /* queue idx of free queue */
+ struct csio_sge sge; /* SGE params */
+};
+
+#define csio_get_q(__hw, __idx) ((__hw)->wrm.q_arr[__idx])
+#define csio_q_type(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->type)
+#define csio_q_pidx(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->pidx)
+#define csio_q_cidx(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->cidx)
+#define csio_q_inc_idx(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->inc_idx)
+#define csio_q_vstart(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->vstart)
+#define csio_q_pstart(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->pstart)
+#define csio_q_size(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->size)
+#define csio_q_credits(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->credits)
+#define csio_q_portid(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->portid)
+#define csio_q_wr_sz(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->wr_sz)
+#define csio_q_iqid(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->un.iq.iqid)
+#define csio_q_physiqid(__hw, __idx) \
+ ((__hw)->wrm.q_arr[(__idx)]->un.iq.physiqid)
+#define csio_q_iq_flq_idx(__hw, __idx) \
+ ((__hw)->wrm.q_arr[(__idx)]->un.iq.flq_idx)
+#define csio_q_eqid(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->un.eq.eqid)
+#define csio_q_flid(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->un.fl.flid)
+
+#define csio_q_physeqid(__hw, __idx) \
+ ((__hw)->wrm.q_arr[(__idx)]->un.eq.physeqid)
+#define csio_iq_has_fl(__iq) ((__iq)->un.iq.flq_idx != -1)
+
+#define csio_q_iq_to_flid(__hw, __iq_idx) \
+ csio_q_flid((__hw), (__hw)->wrm.q_arr[(__iq_qidx)]->un.iq.flq_idx)
+#define csio_q_set_intr_map(__hw, __iq_idx, __rel_iq_id) \
+ (__hw)->wrm.intr_map[__rel_iq_id] = csio_get_q(__hw, __iq_idx)
+#define csio_q_eq_wrap(__hw, __idx) ((__hw)->wrm.q_arr[(__idx)]->un.eq.wrap)
+
+struct csio_mb;
+
+int csio_wr_alloc_q(struct csio_hw *, uint32_t, uint32_t,
+ uint16_t, void *, uint32_t, int, iq_handler_t);
+int csio_wr_iq_create(struct csio_hw *, void *, int,
+ uint32_t, uint8_t, bool,
+ void (*)(struct csio_hw *, struct csio_mb *));
+int csio_wr_eq_create(struct csio_hw *, void *, int, int, uint8_t,
+ void (*)(struct csio_hw *, struct csio_mb *));
+int csio_wr_destroy_queues(struct csio_hw *, bool cmd);
+
+
+int csio_wr_get(struct csio_hw *, int, uint32_t,
+ struct csio_wr_pair *);
+void csio_wr_copy_to_wrp(void *, struct csio_wr_pair *, uint32_t, uint32_t);
+int csio_wr_issue(struct csio_hw *, int, bool);
+int csio_wr_process_iq(struct csio_hw *, struct csio_q *,
+ void (*)(struct csio_hw *, void *,
+ uint32_t, struct csio_fl_dma_buf *,
+ void *),
+ void *);
+int csio_wr_process_iq_idx(struct csio_hw *, int,
+ void (*)(struct csio_hw *, void *,
+ uint32_t, struct csio_fl_dma_buf *,
+ void *),
+ void *);
+
+void csio_wr_sge_init(struct csio_hw *);
+int csio_wrm_init(struct csio_wrm *, struct csio_hw *);
+void csio_wrm_exit(struct csio_wrm *, struct csio_hw *);
+
+#endif /* ifndef __CSIO_WR_H__ */
diff --git a/drivers/scsi/csiostor/t4fw_api_stor.h b/drivers/scsi/csiostor/t4fw_api_stor.h
new file mode 100644
index 00000000000..1223e0d5fc0
--- /dev/null
+++ b/drivers/scsi/csiostor/t4fw_api_stor.h
@@ -0,0 +1,578 @@
+/*
+ * This file is part of the Chelsio FCoE driver for Linux.
+ *
+ * Copyright (c) 2009-2010 Chelsio Communications, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _T4FW_API_STOR_H_
+#define _T4FW_API_STOR_H_
+
+
+/******************************************************************************
+ * R E T U R N V A L U E S
+ ********************************/
+
+enum fw_retval {
+ FW_SUCCESS = 0, /* completed sucessfully */
+ FW_EPERM = 1, /* operation not permitted */
+ FW_ENOENT = 2, /* no such file or directory */
+ FW_EIO = 5, /* input/output error; hw bad */
+ FW_ENOEXEC = 8, /* exec format error; inv microcode */
+ FW_EAGAIN = 11, /* try again */
+ FW_ENOMEM = 12, /* out of memory */
+ FW_EFAULT = 14, /* bad address; fw bad */
+ FW_EBUSY = 16, /* resource busy */
+ FW_EEXIST = 17, /* file exists */
+ FW_EINVAL = 22, /* invalid argument */
+ FW_ENOSPC = 28, /* no space left on device */
+ FW_ENOSYS = 38, /* functionality not implemented */
+ FW_EPROTO = 71, /* protocol error */
+ FW_EADDRINUSE = 98, /* address already in use */
+ FW_EADDRNOTAVAIL = 99, /* cannot assigned requested address */
+ FW_ENETDOWN = 100, /* network is down */
+ FW_ENETUNREACH = 101, /* network is unreachable */
+ FW_ENOBUFS = 105, /* no buffer space available */
+ FW_ETIMEDOUT = 110, /* timeout */
+ FW_EINPROGRESS = 115, /* fw internal */
+ FW_SCSI_ABORT_REQUESTED = 128, /* */
+ FW_SCSI_ABORT_TIMEDOUT = 129, /* */
+ FW_SCSI_ABORTED = 130, /* */
+ FW_SCSI_CLOSE_REQUESTED = 131, /* */
+ FW_ERR_LINK_DOWN = 132, /* */
+ FW_RDEV_NOT_READY = 133, /* */
+ FW_ERR_RDEV_LOST = 134, /* */
+ FW_ERR_RDEV_LOGO = 135, /* */
+ FW_FCOE_NO_XCHG = 136, /* */
+ FW_SCSI_RSP_ERR = 137, /* */
+ FW_ERR_RDEV_IMPL_LOGO = 138, /* */
+ FW_SCSI_UNDER_FLOW_ERR = 139, /* */
+ FW_SCSI_OVER_FLOW_ERR = 140, /* */
+ FW_SCSI_DDP_ERR = 141, /* DDP error*/
+ FW_SCSI_TASK_ERR = 142, /* No SCSI tasks available */
+};
+
+enum fw_fcoe_link_sub_op {
+ FCOE_LINK_DOWN = 0x0,
+ FCOE_LINK_UP = 0x1,
+ FCOE_LINK_COND = 0x2,
+};
+
+enum fw_fcoe_link_status {
+ FCOE_LINKDOWN = 0x0,
+ FCOE_LINKUP = 0x1,
+};
+
+enum fw_ofld_prot {
+ PROT_FCOE = 0x1,
+ PROT_ISCSI = 0x2,
+};
+
+enum rport_type_fcoe {
+ FLOGI_VFPORT = 0x1, /* 0xfffffe */
+ FDISC_VFPORT = 0x2, /* 0xfffffe */
+ NS_VNPORT = 0x3, /* 0xfffffc */
+ REG_FC4_VNPORT = 0x4, /* any FC4 type VN_PORT */
+ REG_VNPORT = 0x5, /* 0xfffxxx - non FC4 port in switch */
+ FDMI_VNPORT = 0x6, /* 0xfffffa */
+ FAB_CTLR_VNPORT = 0x7, /* 0xfffffd */
+};
+
+enum event_cause_fcoe {
+ PLOGI_ACC_RCVD = 0x01,
+ PLOGI_RJT_RCVD = 0x02,
+ PLOGI_RCVD = 0x03,
+ PLOGO_RCVD = 0x04,
+ PRLI_ACC_RCVD = 0x05,
+ PRLI_RJT_RCVD = 0x06,
+ PRLI_RCVD = 0x07,
+ PRLO_RCVD = 0x08,
+ NPORT_ID_CHGD = 0x09,
+ FLOGO_RCVD = 0x0a,
+ CLR_VIRT_LNK_RCVD = 0x0b,
+ FLOGI_ACC_RCVD = 0x0c,
+ FLOGI_RJT_RCVD = 0x0d,
+ FDISC_ACC_RCVD = 0x0e,
+ FDISC_RJT_RCVD = 0x0f,
+ FLOGI_TMO_MAX_RETRY = 0x10,
+ IMPL_LOGO_ADISC_ACC = 0x11,
+ IMPL_LOGO_ADISC_RJT = 0x12,
+ IMPL_LOGO_ADISC_CNFLT = 0x13,
+ PRLI_TMO = 0x14,
+ ADISC_TMO = 0x15,
+ RSCN_DEV_LOST = 0x16,
+ SCR_ACC_RCVD = 0x17,
+ ADISC_RJT_RCVD = 0x18,
+ LOGO_SNT = 0x19,
+ PROTO_ERR_IMPL_LOGO = 0x1a,
+};
+
+enum fcoe_cmn_type {
+ FCOE_ELS,
+ FCOE_CT,
+ FCOE_SCSI_CMD,
+ FCOE_UNSOL_ELS,
+};
+
+enum fw_wr_stor_opcodes {
+ FW_RDEV_WR = 0x38,
+ FW_FCOE_ELS_CT_WR = 0x30,
+ FW_SCSI_WRITE_WR = 0x31,
+ FW_SCSI_READ_WR = 0x32,
+ FW_SCSI_CMD_WR = 0x33,
+ FW_SCSI_ABRT_CLS_WR = 0x34,
+};
+
+struct fw_rdev_wr {
+ __be32 op_to_immdlen;
+ __be32 alloc_to_len16;
+ __be64 cookie;
+ u8 protocol;
+ u8 event_cause;
+ u8 cur_state;
+ u8 prev_state;
+ __be32 flags_to_assoc_flowid;
+ union rdev_entry {
+ struct fcoe_rdev_entry {
+ __be32 flowid;
+ u8 protocol;
+ u8 event_cause;
+ u8 flags;
+ u8 rjt_reason;
+ u8 cur_login_st;
+ u8 prev_login_st;
+ __be16 rcv_fr_sz;
+ u8 rd_xfer_rdy_to_rport_type;
+ u8 vft_to_qos;
+ u8 org_proc_assoc_to_acc_rsp_code;
+ u8 enh_disc_to_tgt;
+ u8 wwnn[8];
+ u8 wwpn[8];
+ __be16 iqid;
+ u8 fc_oui[3];
+ u8 r_id[3];
+ } fcoe_rdev;
+ struct iscsi_rdev_entry {
+ __be32 flowid;
+ u8 protocol;
+ u8 event_cause;
+ u8 flags;
+ u8 r3;
+ __be16 iscsi_opts;
+ __be16 tcp_opts;
+ __be16 ip_opts;
+ __be16 max_rcv_len;
+ __be16 max_snd_len;
+ __be16 first_brst_len;
+ __be16 max_brst_len;
+ __be16 r4;
+ __be16 def_time2wait;
+ __be16 def_time2ret;
+ __be16 nop_out_intrvl;
+ __be16 non_scsi_to;
+ __be16 isid;
+ __be16 tsid;
+ __be16 port;
+ __be16 tpgt;
+ u8 r5[6];
+ __be16 iqid;
+ } iscsi_rdev;
+ } u;
+};
+
+#define FW_RDEV_WR_FLOWID_GET(x) (((x) >> 8) & 0xfffff)
+#define FW_RDEV_WR_ASSOC_FLOWID_GET(x) (((x) >> 0) & 0xfffff)
+#define FW_RDEV_WR_RPORT_TYPE_GET(x) (((x) >> 0) & 0x1f)
+#define FW_RDEV_WR_NPIV_GET(x) (((x) >> 6) & 0x1)
+#define FW_RDEV_WR_CLASS_GET(x) (((x) >> 4) & 0x3)
+#define FW_RDEV_WR_TASK_RETRY_ID_GET(x) (((x) >> 5) & 0x1)
+#define FW_RDEV_WR_RETRY_GET(x) (((x) >> 4) & 0x1)
+#define FW_RDEV_WR_CONF_CMPL_GET(x) (((x) >> 3) & 0x1)
+#define FW_RDEV_WR_INI_GET(x) (((x) >> 1) & 0x1)
+#define FW_RDEV_WR_TGT_GET(x) (((x) >> 0) & 0x1)
+
+struct fw_fcoe_els_ct_wr {
+ __be32 op_immdlen;
+ __be32 flowid_len16;
+ u64 cookie;
+ __be16 iqid;
+ u8 tmo_val;
+ u8 els_ct_type;
+ u8 ctl_pri;
+ u8 cp_en_class;
+ __be16 xfer_cnt;
+ u8 fl_to_sp;
+ u8 l_id[3];
+ u8 r5;
+ u8 r_id[3];
+ __be64 rsp_dmaaddr;
+ __be32 rsp_dmalen;
+ __be32 r6;
+};
+
+#define FW_FCOE_ELS_CT_WR_OPCODE(x) ((x) << 24)
+#define FW_FCOE_ELS_CT_WR_OPCODE_GET(x) (((x) >> 24) & 0xff)
+#define FW_FCOE_ELS_CT_WR_IMMDLEN(x) ((x) << 0)
+#define FW_FCOE_ELS_CT_WR_IMMDLEN_GET(x) (((x) >> 0) & 0xff)
+#define FW_FCOE_ELS_CT_WR_SP(x) ((x) << 0)
+
+struct fw_scsi_write_wr {
+ __be32 op_immdlen;
+ __be32 flowid_len16;
+ u64 cookie;
+ __be16 iqid;
+ u8 tmo_val;
+ u8 use_xfer_cnt;
+ union fw_scsi_write_priv {
+ struct fcoe_write_priv {
+ u8 ctl_pri;
+ u8 cp_en_class;
+ u8 r3_lo[2];
+ } fcoe;
+ struct iscsi_write_priv {
+ u8 r3[4];
+ } iscsi;
+ } u;
+ __be32 xfer_cnt;
+ __be32 ini_xfer_cnt;
+ __be64 rsp_dmaaddr;
+ __be32 rsp_dmalen;
+ __be32 r4;
+};
+
+#define FW_SCSI_WRITE_WR_IMMDLEN(x) ((x) << 0)
+
+struct fw_scsi_read_wr {
+ __be32 op_immdlen;
+ __be32 flowid_len16;
+ u64 cookie;
+ __be16 iqid;
+ u8 tmo_val;
+ u8 use_xfer_cnt;
+ union fw_scsi_read_priv {
+ struct fcoe_read_priv {
+ u8 ctl_pri;
+ u8 cp_en_class;
+ u8 r3_lo[2];
+ } fcoe;
+ struct iscsi_read_priv {
+ u8 r3[4];
+ } iscsi;
+ } u;
+ __be32 xfer_cnt;
+ __be32 ini_xfer_cnt;
+ __be64 rsp_dmaaddr;
+ __be32 rsp_dmalen;
+ __be32 r4;
+};
+
+#define FW_SCSI_READ_WR_IMMDLEN(x) ((x) << 0)
+
+struct fw_scsi_cmd_wr {
+ __be32 op_immdlen;
+ __be32 flowid_len16;
+ u64 cookie;
+ __be16 iqid;
+ u8 tmo_val;
+ u8 r3;
+ union fw_scsi_cmd_priv {
+ struct fcoe_cmd_priv {
+ u8 ctl_pri;
+ u8 cp_en_class;
+ u8 r4_lo[2];
+ } fcoe;
+ struct iscsi_cmd_priv {
+ u8 r4[4];
+ } iscsi;
+ } u;
+ u8 r5[8];
+ __be64 rsp_dmaaddr;
+ __be32 rsp_dmalen;
+ __be32 r6;
+};
+
+#define FW_SCSI_CMD_WR_IMMDLEN(x) ((x) << 0)
+
+#define SCSI_ABORT 0
+#define SCSI_CLOSE 1
+
+struct fw_scsi_abrt_cls_wr {
+ __be32 op_immdlen;
+ __be32 flowid_len16;
+ u64 cookie;
+ __be16 iqid;
+ u8 tmo_val;
+ u8 sub_opcode_to_chk_all_io;
+ u8 r3[4];
+ u64 t_cookie;
+};
+
+#define FW_SCSI_ABRT_CLS_WR_SUB_OPCODE(x) ((x) << 2)
+#define FW_SCSI_ABRT_CLS_WR_SUB_OPCODE_GET(x) (((x) >> 2) & 0x3f)
+#define FW_SCSI_ABRT_CLS_WR_CHK_ALL_IO(x) ((x) << 0)
+
+enum fw_cmd_stor_opcodes {
+ FW_FCOE_RES_INFO_CMD = 0x31,
+ FW_FCOE_LINK_CMD = 0x32,
+ FW_FCOE_VNP_CMD = 0x33,
+ FW_FCOE_SPARAMS_CMD = 0x35,
+ FW_FCOE_STATS_CMD = 0x37,
+ FW_FCOE_FCF_CMD = 0x38,
+};
+
+struct fw_fcoe_res_info_cmd {
+ __be32 op_to_read;
+ __be32 retval_len16;
+ __be16 e_d_tov;
+ __be16 r_a_tov_seq;
+ __be16 r_a_tov_els;
+ __be16 r_r_tov;
+ __be32 max_xchgs;
+ __be32 max_ssns;
+ __be32 used_xchgs;
+ __be32 used_ssns;
+ __be32 max_fcfs;
+ __be32 max_vnps;
+ __be32 used_fcfs;
+ __be32 used_vnps;
+};
+
+struct fw_fcoe_link_cmd {
+ __be32 op_to_portid;
+ __be32 retval_len16;
+ __be32 sub_opcode_fcfi;
+ u8 r3;
+ u8 lstatus;
+ __be16 flags;
+ u8 r4;
+ u8 set_vlan;
+ __be16 vlan_id;
+ __be32 vnpi_pkd;
+ __be16 r6;
+ u8 phy_mac[6];
+ u8 vnport_wwnn[8];
+ u8 vnport_wwpn[8];
+};
+
+#define FW_FCOE_LINK_CMD_PORTID(x) ((x) << 0)
+#define FW_FCOE_LINK_CMD_PORTID_GET(x) (((x) >> 0) & 0xf)
+#define FW_FCOE_LINK_CMD_SUB_OPCODE(x) ((x) << 24U)
+#define FW_FCOE_LINK_CMD_FCFI(x) ((x) << 0)
+#define FW_FCOE_LINK_CMD_FCFI_GET(x) (((x) >> 0) & 0xffffff)
+#define FW_FCOE_LINK_CMD_VNPI_GET(x) (((x) >> 0) & 0xfffff)
+
+struct fw_fcoe_vnp_cmd {
+ __be32 op_to_fcfi;
+ __be32 alloc_to_len16;
+ __be32 gen_wwn_to_vnpi;
+ __be32 vf_id;
+ __be16 iqid;
+ u8 vnport_mac[6];
+ u8 vnport_wwnn[8];
+ u8 vnport_wwpn[8];
+ u8 cmn_srv_parms[16];
+ u8 clsp_word_0_1[8];
+};
+
+#define FW_FCOE_VNP_CMD_FCFI(x) ((x) << 0)
+#define FW_FCOE_VNP_CMD_ALLOC (1U << 31)
+#define FW_FCOE_VNP_CMD_FREE (1U << 30)
+#define FW_FCOE_VNP_CMD_MODIFY (1U << 29)
+#define FW_FCOE_VNP_CMD_GEN_WWN (1U << 22)
+#define FW_FCOE_VNP_CMD_VFID_EN (1U << 20)
+#define FW_FCOE_VNP_CMD_VNPI(x) ((x) << 0)
+#define FW_FCOE_VNP_CMD_VNPI_GET(x) (((x) >> 0) & 0xfffff)
+
+struct fw_fcoe_sparams_cmd {
+ __be32 op_to_portid;
+ __be32 retval_len16;
+ u8 r3[7];
+ u8 cos;
+ u8 lport_wwnn[8];
+ u8 lport_wwpn[8];
+ u8 cmn_srv_parms[16];
+ u8 cls_srv_parms[16];
+};
+
+#define FW_FCOE_SPARAMS_CMD_PORTID(x) ((x) << 0)
+
+struct fw_fcoe_stats_cmd {
+ __be32 op_to_flowid;
+ __be32 free_to_len16;
+ union fw_fcoe_stats {
+ struct fw_fcoe_stats_ctl {
+ u8 nstats_port;
+ u8 port_valid_ix;
+ __be16 r6;
+ __be32 r7;
+ __be64 stat0;
+ __be64 stat1;
+ __be64 stat2;
+ __be64 stat3;
+ __be64 stat4;
+ __be64 stat5;
+ } ctl;
+ struct fw_fcoe_port_stats {
+ __be64 tx_bcast_bytes;
+ __be64 tx_bcast_frames;
+ __be64 tx_mcast_bytes;
+ __be64 tx_mcast_frames;
+ __be64 tx_ucast_bytes;
+ __be64 tx_ucast_frames;
+ __be64 tx_drop_frames;
+ __be64 tx_offload_bytes;
+ __be64 tx_offload_frames;
+ __be64 rx_bcast_bytes;
+ __be64 rx_bcast_frames;
+ __be64 rx_mcast_bytes;
+ __be64 rx_mcast_frames;
+ __be64 rx_ucast_bytes;
+ __be64 rx_ucast_frames;
+ __be64 rx_err_frames;
+ } port_stats;
+ struct fw_fcoe_fcf_stats {
+ __be32 fip_tx_bytes;
+ __be32 fip_tx_fr;
+ __be64 fcf_ka;
+ __be64 mcast_adv_rcvd;
+ __be16 ucast_adv_rcvd;
+ __be16 sol_sent;
+ __be16 vlan_req;
+ __be16 vlan_rpl;
+ __be16 clr_vlink;
+ __be16 link_down;
+ __be16 link_up;
+ __be16 logo;
+ __be16 flogi_req;
+ __be16 flogi_rpl;
+ __be16 fdisc_req;
+ __be16 fdisc_rpl;
+ __be16 fka_prd_chg;
+ __be16 fc_map_chg;
+ __be16 vfid_chg;
+ u8 no_fka_req;
+ u8 no_vnp;
+ } fcf_stats;
+ struct fw_fcoe_pcb_stats {
+ __be64 tx_bytes;
+ __be64 tx_frames;
+ __be64 rx_bytes;
+ __be64 rx_frames;
+ __be32 vnp_ka;
+ __be32 unsol_els_rcvd;
+ __be64 unsol_cmd_rcvd;
+ __be16 implicit_logo;
+ __be16 flogi_inv_sparm;
+ __be16 fdisc_inv_sparm;
+ __be16 flogi_rjt;
+ __be16 fdisc_rjt;
+ __be16 no_ssn;
+ __be16 mac_flt_fail;
+ __be16 inv_fr_rcvd;
+ } pcb_stats;
+ struct fw_fcoe_scb_stats {
+ __be64 tx_bytes;
+ __be64 tx_frames;
+ __be64 rx_bytes;
+ __be64 rx_frames;
+ __be32 host_abrt_req;
+ __be32 adap_auto_abrt;
+ __be32 adap_abrt_rsp;
+ __be32 host_ios_req;
+ __be16 ssn_offl_ios;
+ __be16 ssn_not_rdy_ios;
+ u8 rx_data_ddp_err;
+ u8 ddp_flt_set_err;
+ __be16 rx_data_fr_err;
+ u8 bad_st_abrt_req;
+ u8 no_io_abrt_req;
+ u8 abort_tmo;
+ u8 abort_tmo_2;
+ __be32 abort_req;
+ u8 no_ppod_res_tmo;
+ u8 bp_tmo;
+ u8 adap_auto_cls;
+ u8 no_io_cls_req;
+ __be32 host_cls_req;
+ __be64 unsol_cmd_rcvd;
+ __be32 plogi_req_rcvd;
+ __be32 prli_req_rcvd;
+ __be16 logo_req_rcvd;
+ __be16 prlo_req_rcvd;
+ __be16 plogi_rjt_rcvd;
+ __be16 prli_rjt_rcvd;
+ __be32 adisc_req_rcvd;
+ __be32 rscn_rcvd;
+ __be32 rrq_req_rcvd;
+ __be32 unsol_els_rcvd;
+ u8 adisc_rjt_rcvd;
+ u8 scr_rjt;
+ u8 ct_rjt;
+ u8 inval_bls_rcvd;
+ __be32 ba_rjt_rcvd;
+ } scb_stats;
+ } u;
+};
+
+#define FW_FCOE_STATS_CMD_FLOWID(x) ((x) << 0)
+#define FW_FCOE_STATS_CMD_FREE (1U << 30)
+#define FW_FCOE_STATS_CMD_NSTATS(x) ((x) << 4)
+#define FW_FCOE_STATS_CMD_PORT(x) ((x) << 0)
+#define FW_FCOE_STATS_CMD_PORT_VALID (1U << 7)
+#define FW_FCOE_STATS_CMD_IX(x) ((x) << 0)
+
+struct fw_fcoe_fcf_cmd {
+ __be32 op_to_fcfi;
+ __be32 retval_len16;
+ __be16 priority_pkd;
+ u8 mac[6];
+ u8 name_id[8];
+ u8 fabric[8];
+ __be16 vf_id;
+ __be16 max_fcoe_size;
+ u8 vlan_id;
+ u8 fc_map[3];
+ __be32 fka_adv;
+ __be32 r6;
+ u8 r7_hi;
+ u8 fpma_to_portid;
+ u8 spma_mac[6];
+ __be64 r8;
+};
+
+#define FW_FCOE_FCF_CMD_FCFI(x) ((x) << 0)
+#define FW_FCOE_FCF_CMD_FCFI_GET(x) (((x) >> 0) & 0xfffff)
+#define FW_FCOE_FCF_CMD_PRIORITY_GET(x) (((x) >> 0) & 0xff)
+#define FW_FCOE_FCF_CMD_FPMA_GET(x) (((x) >> 6) & 0x1)
+#define FW_FCOE_FCF_CMD_SPMA_GET(x) (((x) >> 5) & 0x1)
+#define FW_FCOE_FCF_CMD_LOGIN_GET(x) (((x) >> 4) & 0x1)
+#define FW_FCOE_FCF_CMD_PORTID_GET(x) (((x) >> 0) & 0xf)
+
+#endif /* _T4FW_API_STOR_H_ */
diff --git a/drivers/scsi/hptiop.c b/drivers/scsi/hptiop.c
index 192724ed7a3..138e573f37e 100644
--- a/drivers/scsi/hptiop.c
+++ b/drivers/scsi/hptiop.c
@@ -1,6 +1,6 @@
/*
* HighPoint RR3xxx/4xxx controller driver for Linux
- * Copyright (C) 2006-2009 HighPoint Technologies, Inc. All Rights Reserved.
+ * Copyright (C) 2006-2012 HighPoint Technologies, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -42,7 +42,7 @@ MODULE_DESCRIPTION("HighPoint RocketRAID 3xxx/4xxx Controller Driver");
static char driver_name[] = "hptiop";
static const char driver_name_long[] = "RocketRAID 3xxx/4xxx Controller driver";
-static const char driver_ver[] = "v1.6 (091225)";
+static const char driver_ver[] = "v1.8";
static int iop_send_sync_msg(struct hptiop_hba *hba, u32 msg, u32 millisec);
static void hptiop_finish_scsi_req(struct hptiop_hba *hba, u32 tag,
@@ -77,6 +77,11 @@ static int iop_wait_ready_mv(struct hptiop_hba *hba, u32 millisec)
return iop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_NOP, millisec);
}
+static int iop_wait_ready_mvfrey(struct hptiop_hba *hba, u32 millisec)
+{
+ return iop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_NOP, millisec);
+}
+
static void hptiop_request_callback_itl(struct hptiop_hba *hba, u32 tag)
{
if (tag & IOPMU_QUEUE_ADDR_HOST_BIT)
@@ -230,6 +235,74 @@ static int iop_intr_mv(struct hptiop_hba *hba)
return ret;
}
+static void hptiop_request_callback_mvfrey(struct hptiop_hba *hba, u32 _tag)
+{
+ u32 req_type = _tag & 0xf;
+ struct hpt_iop_request_scsi_command *req;
+
+ switch (req_type) {
+ case IOP_REQUEST_TYPE_GET_CONFIG:
+ case IOP_REQUEST_TYPE_SET_CONFIG:
+ hba->msg_done = 1;
+ break;
+
+ case IOP_REQUEST_TYPE_SCSI_COMMAND:
+ req = hba->reqs[(_tag >> 4) & 0xff].req_virt;
+ if (likely(_tag & IOPMU_QUEUE_REQUEST_RESULT_BIT))
+ req->header.result = IOP_RESULT_SUCCESS;
+ hptiop_finish_scsi_req(hba, (_tag >> 4) & 0xff, req);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int iop_intr_mvfrey(struct hptiop_hba *hba)
+{
+ u32 _tag, status, cptr, cur_rptr;
+ int ret = 0;
+
+ if (hba->initialized)
+ writel(0, &(hba->u.mvfrey.mu->pcie_f0_int_enable));
+
+ status = readl(&(hba->u.mvfrey.mu->f0_doorbell));
+ if (status) {
+ writel(status, &(hba->u.mvfrey.mu->f0_doorbell));
+ if (status & CPU_TO_F0_DRBL_MSG_BIT) {
+ u32 msg = readl(&(hba->u.mvfrey.mu->cpu_to_f0_msg_a));
+ dprintk("received outbound msg %x\n", msg);
+ hptiop_message_callback(hba, msg);
+ }
+ ret = 1;
+ }
+
+ status = readl(&(hba->u.mvfrey.mu->isr_cause));
+ if (status) {
+ writel(status, &(hba->u.mvfrey.mu->isr_cause));
+ do {
+ cptr = *hba->u.mvfrey.outlist_cptr & 0xff;
+ cur_rptr = hba->u.mvfrey.outlist_rptr;
+ while (cur_rptr != cptr) {
+ cur_rptr++;
+ if (cur_rptr == hba->u.mvfrey.list_count)
+ cur_rptr = 0;
+
+ _tag = hba->u.mvfrey.outlist[cur_rptr].val;
+ BUG_ON(!(_tag & IOPMU_QUEUE_MASK_HOST_BITS));
+ hptiop_request_callback_mvfrey(hba, _tag);
+ ret = 1;
+ }
+ hba->u.mvfrey.outlist_rptr = cur_rptr;
+ } while (cptr != (*hba->u.mvfrey.outlist_cptr & 0xff));
+ }
+
+ if (hba->initialized)
+ writel(0x1010, &(hba->u.mvfrey.mu->pcie_f0_int_enable));
+
+ return ret;
+}
+
static int iop_send_sync_request_itl(struct hptiop_hba *hba,
void __iomem *_req, u32 millisec)
{
@@ -272,6 +345,26 @@ static int iop_send_sync_request_mv(struct hptiop_hba *hba,
return -1;
}
+static int iop_send_sync_request_mvfrey(struct hptiop_hba *hba,
+ u32 size_bits, u32 millisec)
+{
+ struct hpt_iop_request_header *reqhdr =
+ hba->u.mvfrey.internal_req.req_virt;
+ u32 i;
+
+ hba->msg_done = 0;
+ reqhdr->flags |= cpu_to_le32(IOP_REQUEST_FLAG_SYNC_REQUEST);
+ hba->ops->post_req(hba, &(hba->u.mvfrey.internal_req));
+
+ for (i = 0; i < millisec; i++) {
+ iop_intr_mvfrey(hba);
+ if (hba->msg_done)
+ break;
+ msleep(1);
+ }
+ return hba->msg_done ? 0 : -1;
+}
+
static void hptiop_post_msg_itl(struct hptiop_hba *hba, u32 msg)
{
writel(msg, &hba->u.itl.iop->inbound_msgaddr0);
@@ -285,11 +378,18 @@ static void hptiop_post_msg_mv(struct hptiop_hba *hba, u32 msg)
readl(&hba->u.mv.regs->inbound_doorbell);
}
+static void hptiop_post_msg_mvfrey(struct hptiop_hba *hba, u32 msg)
+{
+ writel(msg, &(hba->u.mvfrey.mu->f0_to_cpu_msg_a));
+ readl(&(hba->u.mvfrey.mu->f0_to_cpu_msg_a));
+}
+
static int iop_send_sync_msg(struct hptiop_hba *hba, u32 msg, u32 millisec)
{
u32 i;
hba->msg_done = 0;
+ hba->ops->disable_intr(hba);
hba->ops->post_msg(hba, msg);
for (i = 0; i < millisec; i++) {
@@ -301,6 +401,7 @@ static int iop_send_sync_msg(struct hptiop_hba *hba, u32 msg, u32 millisec)
msleep(1);
}
+ hba->ops->enable_intr(hba);
return hba->msg_done? 0 : -1;
}
@@ -354,6 +455,28 @@ static int iop_get_config_mv(struct hptiop_hba *hba,
return 0;
}
+static int iop_get_config_mvfrey(struct hptiop_hba *hba,
+ struct hpt_iop_request_get_config *config)
+{
+ struct hpt_iop_request_get_config *info = hba->u.mvfrey.config;
+
+ if (info->header.size != sizeof(struct hpt_iop_request_get_config) ||
+ info->header.type != IOP_REQUEST_TYPE_GET_CONFIG)
+ return -1;
+
+ config->interface_version = info->interface_version;
+ config->firmware_version = info->firmware_version;
+ config->max_requests = info->max_requests;
+ config->request_size = info->request_size;
+ config->max_sg_count = info->max_sg_count;
+ config->data_transfer_length = info->data_transfer_length;
+ config->alignment_mask = info->alignment_mask;
+ config->max_devices = info->max_devices;
+ config->sdram_size = info->sdram_size;
+
+ return 0;
+}
+
static int iop_set_config_itl(struct hptiop_hba *hba,
struct hpt_iop_request_set_config *config)
{
@@ -408,6 +531,29 @@ static int iop_set_config_mv(struct hptiop_hba *hba,
return 0;
}
+static int iop_set_config_mvfrey(struct hptiop_hba *hba,
+ struct hpt_iop_request_set_config *config)
+{
+ struct hpt_iop_request_set_config *req =
+ hba->u.mvfrey.internal_req.req_virt;
+
+ memcpy(req, config, sizeof(struct hpt_iop_request_set_config));
+ req->header.flags = cpu_to_le32(IOP_REQUEST_FLAG_OUTPUT_CONTEXT);
+ req->header.type = cpu_to_le32(IOP_REQUEST_TYPE_SET_CONFIG);
+ req->header.size =
+ cpu_to_le32(sizeof(struct hpt_iop_request_set_config));
+ req->header.result = cpu_to_le32(IOP_RESULT_PENDING);
+ req->header.context = cpu_to_le32(IOP_REQUEST_TYPE_SET_CONFIG<<5);
+ req->header.context_hi32 = 0;
+
+ if (iop_send_sync_request_mvfrey(hba, 0, 20000)) {
+ dprintk("Set config send cmd failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
static void hptiop_enable_intr_itl(struct hptiop_hba *hba)
{
writel(~(IOPMU_OUTBOUND_INT_POSTQUEUE | IOPMU_OUTBOUND_INT_MSG0),
@@ -420,6 +566,13 @@ static void hptiop_enable_intr_mv(struct hptiop_hba *hba)
&hba->u.mv.regs->outbound_intmask);
}
+static void hptiop_enable_intr_mvfrey(struct hptiop_hba *hba)
+{
+ writel(CPU_TO_F0_DRBL_MSG_BIT, &(hba->u.mvfrey.mu->f0_doorbell_enable));
+ writel(0x1, &(hba->u.mvfrey.mu->isr_enable));
+ writel(0x1010, &(hba->u.mvfrey.mu->pcie_f0_int_enable));
+}
+
static int hptiop_initialize_iop(struct hptiop_hba *hba)
{
/* enable interrupts */
@@ -502,17 +655,39 @@ static int hptiop_map_pci_bar_mv(struct hptiop_hba *hba)
return 0;
}
+static int hptiop_map_pci_bar_mvfrey(struct hptiop_hba *hba)
+{
+ hba->u.mvfrey.config = hptiop_map_pci_bar(hba, 0);
+ if (hba->u.mvfrey.config == NULL)
+ return -1;
+
+ hba->u.mvfrey.mu = hptiop_map_pci_bar(hba, 2);
+ if (hba->u.mvfrey.mu == NULL) {
+ iounmap(hba->u.mvfrey.config);
+ return -1;
+ }
+
+ return 0;
+}
+
static void hptiop_unmap_pci_bar_mv(struct hptiop_hba *hba)
{
iounmap(hba->u.mv.regs);
iounmap(hba->u.mv.mu);
}
+static void hptiop_unmap_pci_bar_mvfrey(struct hptiop_hba *hba)
+{
+ iounmap(hba->u.mvfrey.config);
+ iounmap(hba->u.mvfrey.mu);
+}
+
static void hptiop_message_callback(struct hptiop_hba *hba, u32 msg)
{
dprintk("iop message 0x%x\n", msg);
- if (msg == IOPMU_INBOUND_MSG0_NOP)
+ if (msg == IOPMU_INBOUND_MSG0_NOP ||
+ msg == IOPMU_INBOUND_MSG0_RESET_COMM)
hba->msg_done = 1;
if (!hba->initialized)
@@ -592,6 +767,7 @@ static void hptiop_finish_scsi_req(struct hptiop_hba *hba, u32 tag,
memcpy(scp->sense_buffer, &req->sg_list,
min_t(size_t, SCSI_SENSE_BUFFERSIZE,
le32_to_cpu(req->dataxfer_length)));
+ goto skip_resid;
break;
default:
@@ -599,6 +775,10 @@ static void hptiop_finish_scsi_req(struct hptiop_hba *hba, u32 tag,
break;
}
+ scsi_set_resid(scp,
+ scsi_bufflen(scp) - le32_to_cpu(req->dataxfer_length));
+
+skip_resid:
dprintk("scsi_done(%p)\n", scp);
scp->scsi_done(scp);
free_req(hba, &hba->reqs[tag]);
@@ -692,7 +872,8 @@ static int hptiop_buildsgl(struct scsi_cmnd *scp, struct hpt_iopsg *psg)
BUG_ON(HPT_SCP(scp)->sgcnt > hba->max_sg_descriptors);
scsi_for_each_sg(scp, sg, HPT_SCP(scp)->sgcnt, idx) {
- psg[idx].pci_address = cpu_to_le64(sg_dma_address(sg));
+ psg[idx].pci_address = cpu_to_le64(sg_dma_address(sg)) |
+ hba->ops->host_phy_flag;
psg[idx].size = cpu_to_le32(sg_dma_len(sg));
psg[idx].eot = (idx == HPT_SCP(scp)->sgcnt - 1) ?
cpu_to_le32(1) : 0;
@@ -751,6 +932,78 @@ static void hptiop_post_req_mv(struct hptiop_hba *hba,
MVIOP_MU_QUEUE_ADDR_HOST_BIT | size_bit, hba);
}
+static void hptiop_post_req_mvfrey(struct hptiop_hba *hba,
+ struct hptiop_request *_req)
+{
+ struct hpt_iop_request_header *reqhdr = _req->req_virt;
+ u32 index;
+
+ reqhdr->flags |= cpu_to_le32(IOP_REQUEST_FLAG_OUTPUT_CONTEXT |
+ IOP_REQUEST_FLAG_ADDR_BITS |
+ ((_req->req_shifted_phy >> 11) & 0xffff0000));
+ reqhdr->context = cpu_to_le32(IOPMU_QUEUE_ADDR_HOST_BIT |
+ (_req->index << 4) | reqhdr->type);
+ reqhdr->context_hi32 = cpu_to_le32((_req->req_shifted_phy << 5) &
+ 0xffffffff);
+
+ hba->u.mvfrey.inlist_wptr++;
+ index = hba->u.mvfrey.inlist_wptr & 0x3fff;
+
+ if (index == hba->u.mvfrey.list_count) {
+ index = 0;
+ hba->u.mvfrey.inlist_wptr &= ~0x3fff;
+ hba->u.mvfrey.inlist_wptr ^= CL_POINTER_TOGGLE;
+ }
+
+ hba->u.mvfrey.inlist[index].addr =
+ (dma_addr_t)_req->req_shifted_phy << 5;
+ hba->u.mvfrey.inlist[index].intrfc_len = (reqhdr->size + 3) / 4;
+ writel(hba->u.mvfrey.inlist_wptr,
+ &(hba->u.mvfrey.mu->inbound_write_ptr));
+ readl(&(hba->u.mvfrey.mu->inbound_write_ptr));
+}
+
+static int hptiop_reset_comm_itl(struct hptiop_hba *hba)
+{
+ return 0;
+}
+
+static int hptiop_reset_comm_mv(struct hptiop_hba *hba)
+{
+ return 0;
+}
+
+static int hptiop_reset_comm_mvfrey(struct hptiop_hba *hba)
+{
+ u32 list_count = hba->u.mvfrey.list_count;
+
+ if (iop_send_sync_msg(hba, IOPMU_INBOUND_MSG0_RESET_COMM, 3000))
+ return -1;
+
+ /* wait 100ms for MCU ready */
+ msleep(100);
+
+ writel(cpu_to_le32(hba->u.mvfrey.inlist_phy & 0xffffffff),
+ &(hba->u.mvfrey.mu->inbound_base));
+ writel(cpu_to_le32((hba->u.mvfrey.inlist_phy >> 16) >> 16),
+ &(hba->u.mvfrey.mu->inbound_base_high));
+
+ writel(cpu_to_le32(hba->u.mvfrey.outlist_phy & 0xffffffff),
+ &(hba->u.mvfrey.mu->outbound_base));
+ writel(cpu_to_le32((hba->u.mvfrey.outlist_phy >> 16) >> 16),
+ &(hba->u.mvfrey.mu->outbound_base_high));
+
+ writel(cpu_to_le32(hba->u.mvfrey.outlist_cptr_phy & 0xffffffff),
+ &(hba->u.mvfrey.mu->outbound_shadow_base));
+ writel(cpu_to_le32((hba->u.mvfrey.outlist_cptr_phy >> 16) >> 16),
+ &(hba->u.mvfrey.mu->outbound_shadow_base_high));
+
+ hba->u.mvfrey.inlist_wptr = (list_count - 1) | CL_POINTER_TOGGLE;
+ *hba->u.mvfrey.outlist_cptr = (list_count - 1) | CL_POINTER_TOGGLE;
+ hba->u.mvfrey.outlist_rptr = list_count - 1;
+ return 0;
+}
+
static int hptiop_queuecommand_lck(struct scsi_cmnd *scp,
void (*done)(struct scsi_cmnd *))
{
@@ -771,14 +1024,15 @@ static int hptiop_queuecommand_lck(struct scsi_cmnd *scp,
_req->scp = scp;
- dprintk("hptiop_queuecmd(scp=%p) %d/%d/%d/%d cdb=(%x-%x-%x) "
+ dprintk("hptiop_queuecmd(scp=%p) %d/%d/%d/%d cdb=(%08x-%08x-%08x-%08x) "
"req_index=%d, req=%p\n",
scp,
host->host_no, scp->device->channel,
scp->device->id, scp->device->lun,
- ((u32 *)scp->cmnd)[0],
- ((u32 *)scp->cmnd)[1],
- ((u32 *)scp->cmnd)[2],
+ cpu_to_be32(((u32 *)scp->cmnd)[0]),
+ cpu_to_be32(((u32 *)scp->cmnd)[1]),
+ cpu_to_be32(((u32 *)scp->cmnd)[2]),
+ cpu_to_be32(((u32 *)scp->cmnd)[3]),
_req->index, _req->req_virt);
scp->result = 0;
@@ -933,6 +1187,11 @@ static struct scsi_host_template driver_template = {
.change_queue_depth = hptiop_adjust_disk_queue_depth,
};
+static int hptiop_internal_memalloc_itl(struct hptiop_hba *hba)
+{
+ return 0;
+}
+
static int hptiop_internal_memalloc_mv(struct hptiop_hba *hba)
{
hba->u.mv.internal_req = dma_alloc_coherent(&hba->pcidev->dev,
@@ -943,6 +1202,63 @@ static int hptiop_internal_memalloc_mv(struct hptiop_hba *hba)
return -1;
}
+static int hptiop_internal_memalloc_mvfrey(struct hptiop_hba *hba)
+{
+ u32 list_count = readl(&hba->u.mvfrey.mu->inbound_conf_ctl);
+ char *p;
+ dma_addr_t phy;
+
+ BUG_ON(hba->max_request_size == 0);
+
+ if (list_count == 0) {
+ BUG_ON(1);
+ return -1;
+ }
+
+ list_count >>= 16;
+
+ hba->u.mvfrey.list_count = list_count;
+ hba->u.mvfrey.internal_mem_size = 0x800 +
+ list_count * sizeof(struct mvfrey_inlist_entry) +
+ list_count * sizeof(struct mvfrey_outlist_entry) +
+ sizeof(int);
+
+ p = dma_alloc_coherent(&hba->pcidev->dev,
+ hba->u.mvfrey.internal_mem_size, &phy, GFP_KERNEL);
+ if (!p)
+ return -1;
+
+ hba->u.mvfrey.internal_req.req_virt = p;
+ hba->u.mvfrey.internal_req.req_shifted_phy = phy >> 5;
+ hba->u.mvfrey.internal_req.scp = NULL;
+ hba->u.mvfrey.internal_req.next = NULL;
+
+ p += 0x800;
+ phy += 0x800;
+
+ hba->u.mvfrey.inlist = (struct mvfrey_inlist_entry *)p;
+ hba->u.mvfrey.inlist_phy = phy;
+
+ p += list_count * sizeof(struct mvfrey_inlist_entry);
+ phy += list_count * sizeof(struct mvfrey_inlist_entry);
+
+ hba->u.mvfrey.outlist = (struct mvfrey_outlist_entry *)p;
+ hba->u.mvfrey.outlist_phy = phy;
+
+ p += list_count * sizeof(struct mvfrey_outlist_entry);
+ phy += list_count * sizeof(struct mvfrey_outlist_entry);
+
+ hba->u.mvfrey.outlist_cptr = (__le32 *)p;
+ hba->u.mvfrey.outlist_cptr_phy = phy;
+
+ return 0;
+}
+
+static int hptiop_internal_memfree_itl(struct hptiop_hba *hba)
+{
+ return 0;
+}
+
static int hptiop_internal_memfree_mv(struct hptiop_hba *hba)
{
if (hba->u.mv.internal_req) {
@@ -953,6 +1269,19 @@ static int hptiop_internal_memfree_mv(struct hptiop_hba *hba)
return -1;
}
+static int hptiop_internal_memfree_mvfrey(struct hptiop_hba *hba)
+{
+ if (hba->u.mvfrey.internal_req.req_virt) {
+ dma_free_coherent(&hba->pcidev->dev,
+ hba->u.mvfrey.internal_mem_size,
+ hba->u.mvfrey.internal_req.req_virt,
+ (dma_addr_t)
+ hba->u.mvfrey.internal_req.req_shifted_phy << 5);
+ return 0;
+ } else
+ return -1;
+}
+
static int __devinit hptiop_probe(struct pci_dev *pcidev,
const struct pci_device_id *id)
{
@@ -1027,7 +1356,7 @@ static int __devinit hptiop_probe(struct pci_dev *pcidev,
goto unmap_pci_bar;
}
- if (hba->ops->internal_memalloc) {
+ if (hba->ops->family == MV_BASED_IOP) {
if (hba->ops->internal_memalloc(hba)) {
printk(KERN_ERR "scsi%d: internal_memalloc failed\n",
hba->host->host_no);
@@ -1050,6 +1379,19 @@ static int __devinit hptiop_probe(struct pci_dev *pcidev,
hba->interface_version = le32_to_cpu(iop_config.interface_version);
hba->sdram_size = le32_to_cpu(iop_config.sdram_size);
+ if (hba->ops->family == MVFREY_BASED_IOP) {
+ if (hba->ops->internal_memalloc(hba)) {
+ printk(KERN_ERR "scsi%d: internal_memalloc failed\n",
+ hba->host->host_no);
+ goto unmap_pci_bar;
+ }
+ if (hba->ops->reset_comm(hba)) {
+ printk(KERN_ERR "scsi%d: reset comm failed\n",
+ hba->host->host_no);
+ goto unmap_pci_bar;
+ }
+ }
+
if (hba->firmware_version > 0x01020000 ||
hba->interface_version > 0x01020000)
hba->iopintf_v2 = 1;
@@ -1104,14 +1446,13 @@ static int __devinit hptiop_probe(struct pci_dev *pcidev,
hba->dma_coherent = start_virt;
hba->dma_coherent_handle = start_phy;
- if ((start_phy & 0x1f) != 0)
- {
+ if ((start_phy & 0x1f) != 0) {
offset = ((start_phy + 0x1f) & ~0x1f) - start_phy;
start_phy += offset;
start_virt += offset;
}
- hba->req_list = start_virt;
+ hba->req_list = NULL;
for (i = 0; i < hba->max_requests; i++) {
hba->reqs[i].next = NULL;
hba->reqs[i].req_virt = start_virt;
@@ -1132,7 +1473,6 @@ static int __devinit hptiop_probe(struct pci_dev *pcidev,
goto free_request_mem;
}
-
scsi_scan_host(host);
dprintk("scsi%d: hptiop_probe successfully\n", hba->host->host_no);
@@ -1147,8 +1487,7 @@ free_request_irq:
free_irq(hba->pcidev->irq, hba);
unmap_pci_bar:
- if (hba->ops->internal_memfree)
- hba->ops->internal_memfree(hba);
+ hba->ops->internal_memfree(hba);
hba->ops->unmap_pci_bar(hba);
@@ -1198,6 +1537,16 @@ static void hptiop_disable_intr_mv(struct hptiop_hba *hba)
readl(&hba->u.mv.regs->outbound_intmask);
}
+static void hptiop_disable_intr_mvfrey(struct hptiop_hba *hba)
+{
+ writel(0, &(hba->u.mvfrey.mu->f0_doorbell_enable));
+ readl(&(hba->u.mvfrey.mu->f0_doorbell_enable));
+ writel(0, &(hba->u.mvfrey.mu->isr_enable));
+ readl(&(hba->u.mvfrey.mu->isr_enable));
+ writel(0, &(hba->u.mvfrey.mu->pcie_f0_int_enable));
+ readl(&(hba->u.mvfrey.mu->pcie_f0_int_enable));
+}
+
static void hptiop_remove(struct pci_dev *pcidev)
{
struct Scsi_Host *host = pci_get_drvdata(pcidev);
@@ -1216,8 +1565,7 @@ static void hptiop_remove(struct pci_dev *pcidev)
hba->dma_coherent,
hba->dma_coherent_handle);
- if (hba->ops->internal_memfree)
- hba->ops->internal_memfree(hba);
+ hba->ops->internal_memfree(hba);
hba->ops->unmap_pci_bar(hba);
@@ -1229,9 +1577,10 @@ static void hptiop_remove(struct pci_dev *pcidev)
}
static struct hptiop_adapter_ops hptiop_itl_ops = {
+ .family = INTEL_BASED_IOP,
.iop_wait_ready = iop_wait_ready_itl,
- .internal_memalloc = NULL,
- .internal_memfree = NULL,
+ .internal_memalloc = hptiop_internal_memalloc_itl,
+ .internal_memfree = hptiop_internal_memfree_itl,
.map_pci_bar = hptiop_map_pci_bar_itl,
.unmap_pci_bar = hptiop_unmap_pci_bar_itl,
.enable_intr = hptiop_enable_intr_itl,
@@ -1242,9 +1591,12 @@ static struct hptiop_adapter_ops hptiop_itl_ops = {
.post_msg = hptiop_post_msg_itl,
.post_req = hptiop_post_req_itl,
.hw_dma_bit_mask = 64,
+ .reset_comm = hptiop_reset_comm_itl,
+ .host_phy_flag = cpu_to_le64(0),
};
static struct hptiop_adapter_ops hptiop_mv_ops = {
+ .family = MV_BASED_IOP,
.iop_wait_ready = iop_wait_ready_mv,
.internal_memalloc = hptiop_internal_memalloc_mv,
.internal_memfree = hptiop_internal_memfree_mv,
@@ -1258,6 +1610,27 @@ static struct hptiop_adapter_ops hptiop_mv_ops = {
.post_msg = hptiop_post_msg_mv,
.post_req = hptiop_post_req_mv,
.hw_dma_bit_mask = 33,
+ .reset_comm = hptiop_reset_comm_mv,
+ .host_phy_flag = cpu_to_le64(0),
+};
+
+static struct hptiop_adapter_ops hptiop_mvfrey_ops = {
+ .family = MVFREY_BASED_IOP,
+ .iop_wait_ready = iop_wait_ready_mvfrey,
+ .internal_memalloc = hptiop_internal_memalloc_mvfrey,
+ .internal_memfree = hptiop_internal_memfree_mvfrey,
+ .map_pci_bar = hptiop_map_pci_bar_mvfrey,
+ .unmap_pci_bar = hptiop_unmap_pci_bar_mvfrey,
+ .enable_intr = hptiop_enable_intr_mvfrey,
+ .disable_intr = hptiop_disable_intr_mvfrey,
+ .get_config = iop_get_config_mvfrey,
+ .set_config = iop_set_config_mvfrey,
+ .iop_intr = iop_intr_mvfrey,
+ .post_msg = hptiop_post_msg_mvfrey,
+ .post_req = hptiop_post_req_mvfrey,
+ .hw_dma_bit_mask = 64,
+ .reset_comm = hptiop_reset_comm_mvfrey,
+ .host_phy_flag = cpu_to_le64(1),
};
static struct pci_device_id hptiop_id_table[] = {
@@ -1283,6 +1656,8 @@ static struct pci_device_id hptiop_id_table[] = {
{ PCI_VDEVICE(TTI, 0x3120), (kernel_ulong_t)&hptiop_mv_ops },
{ PCI_VDEVICE(TTI, 0x3122), (kernel_ulong_t)&hptiop_mv_ops },
{ PCI_VDEVICE(TTI, 0x3020), (kernel_ulong_t)&hptiop_mv_ops },
+ { PCI_VDEVICE(TTI, 0x4520), (kernel_ulong_t)&hptiop_mvfrey_ops },
+ { PCI_VDEVICE(TTI, 0x4522), (kernel_ulong_t)&hptiop_mvfrey_ops },
{},
};
diff --git a/drivers/scsi/hptiop.h b/drivers/scsi/hptiop.h
index baa648d87fd..020619d60b0 100644
--- a/drivers/scsi/hptiop.h
+++ b/drivers/scsi/hptiop.h
@@ -1,6 +1,6 @@
/*
* HighPoint RR3xxx/4xxx controller driver for Linux
- * Copyright (C) 2006-2009 HighPoint Technologies, Inc. All Rights Reserved.
+ * Copyright (C) 2006-2012 HighPoint Technologies, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -75,6 +75,45 @@ struct hpt_iopmv_regs {
__le32 outbound_intmask;
};
+#pragma pack(1)
+struct hpt_iopmu_mvfrey {
+ __le32 reserved0[(0x4000 - 0) / 4];
+ __le32 inbound_base;
+ __le32 inbound_base_high;
+ __le32 reserved1[(0x4018 - 0x4008) / 4];
+ __le32 inbound_write_ptr;
+ __le32 reserved2[(0x402c - 0x401c) / 4];
+ __le32 inbound_conf_ctl;
+ __le32 reserved3[(0x4050 - 0x4030) / 4];
+ __le32 outbound_base;
+ __le32 outbound_base_high;
+ __le32 outbound_shadow_base;
+ __le32 outbound_shadow_base_high;
+ __le32 reserved4[(0x4088 - 0x4060) / 4];
+ __le32 isr_cause;
+ __le32 isr_enable;
+ __le32 reserved5[(0x1020c - 0x4090) / 4];
+ __le32 pcie_f0_int_enable;
+ __le32 reserved6[(0x10400 - 0x10210) / 4];
+ __le32 f0_to_cpu_msg_a;
+ __le32 reserved7[(0x10420 - 0x10404) / 4];
+ __le32 cpu_to_f0_msg_a;
+ __le32 reserved8[(0x10480 - 0x10424) / 4];
+ __le32 f0_doorbell;
+ __le32 f0_doorbell_enable;
+};
+
+struct mvfrey_inlist_entry {
+ dma_addr_t addr;
+ __le32 intrfc_len;
+ __le32 reserved;
+};
+
+struct mvfrey_outlist_entry {
+ __le32 val;
+};
+#pragma pack()
+
#define MVIOP_MU_QUEUE_ADDR_HOST_MASK (~(0x1full))
#define MVIOP_MU_QUEUE_ADDR_HOST_BIT 4
@@ -87,6 +126,9 @@ struct hpt_iopmv_regs {
#define MVIOP_MU_OUTBOUND_INT_MSG 1
#define MVIOP_MU_OUTBOUND_INT_POSTQUEUE 2
+#define CL_POINTER_TOGGLE 0x00004000
+#define CPU_TO_F0_DRBL_MSG_BIT 0x02000000
+
enum hpt_iopmu_message {
/* host-to-iop messages */
IOPMU_INBOUND_MSG0_NOP = 0,
@@ -95,6 +137,7 @@ enum hpt_iopmu_message {
IOPMU_INBOUND_MSG0_SHUTDOWN,
IOPMU_INBOUND_MSG0_STOP_BACKGROUND_TASK,
IOPMU_INBOUND_MSG0_START_BACKGROUND_TASK,
+ IOPMU_INBOUND_MSG0_RESET_COMM,
IOPMU_INBOUND_MSG0_MAX = 0xff,
/* iop-to-host messages */
IOPMU_OUTBOUND_MSG0_REGISTER_DEVICE_0 = 0x100,
@@ -118,6 +161,7 @@ struct hpt_iop_request_header {
#define IOP_REQUEST_FLAG_BIST_REQUEST 2
#define IOP_REQUEST_FLAG_REMAPPED 4
#define IOP_REQUEST_FLAG_OUTPUT_CONTEXT 8
+#define IOP_REQUEST_FLAG_ADDR_BITS 0x40 /* flags[31:16] is phy_addr[47:32] */
enum hpt_iop_request_type {
IOP_REQUEST_TYPE_GET_CONFIG = 0,
@@ -223,6 +267,13 @@ struct hpt_scsi_pointer {
#define HPT_SCP(scp) ((struct hpt_scsi_pointer *)&(scp)->SCp)
+enum hptiop_family {
+ UNKNOWN_BASED_IOP,
+ INTEL_BASED_IOP,
+ MV_BASED_IOP,
+ MVFREY_BASED_IOP
+} ;
+
struct hptiop_hba {
struct hptiop_adapter_ops *ops;
union {
@@ -236,6 +287,22 @@ struct hptiop_hba {
void *internal_req;
dma_addr_t internal_req_phy;
} mv;
+ struct {
+ struct hpt_iop_request_get_config __iomem *config;
+ struct hpt_iopmu_mvfrey __iomem *mu;
+
+ int internal_mem_size;
+ struct hptiop_request internal_req;
+ int list_count;
+ struct mvfrey_inlist_entry *inlist;
+ dma_addr_t inlist_phy;
+ __le32 inlist_wptr;
+ struct mvfrey_outlist_entry *outlist;
+ dma_addr_t outlist_phy;
+ __le32 *outlist_cptr; /* copy pointer shadow */
+ dma_addr_t outlist_cptr_phy;
+ __le32 outlist_rptr;
+ } mvfrey;
} u;
struct Scsi_Host *host;
@@ -283,6 +350,7 @@ struct hpt_ioctl_k {
};
struct hptiop_adapter_ops {
+ enum hptiop_family family;
int (*iop_wait_ready)(struct hptiop_hba *hba, u32 millisec);
int (*internal_memalloc)(struct hptiop_hba *hba);
int (*internal_memfree)(struct hptiop_hba *hba);
@@ -298,6 +366,8 @@ struct hptiop_adapter_ops {
void (*post_msg)(struct hptiop_hba *hba, u32 msg);
void (*post_req)(struct hptiop_hba *hba, struct hptiop_request *_req);
int hw_dma_bit_mask;
+ int (*reset_comm)(struct hptiop_hba *hba);
+ __le64 host_phy_flag;
};
#define HPT_IOCTL_RESULT_OK 0
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index efc6e72f09f..aec2e0da501 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -1800,7 +1800,7 @@ out:
* @dev:domain device to be detect.
* @src_dev: the device which originated BROADCAST(CHANGE).
*
- * Add self-configuration expander suport. Suppose two expander cascading,
+ * Add self-configuration expander support. Suppose two expander cascading,
* when the first level expander is self-configuring, hotplug the disks in
* second level expander, BROADCAST(CHANGE) will not only be originated
* in the second level expander, but also be originated in the first level
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 69b59935b53..df4c13a5534 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -689,6 +689,7 @@ struct lpfc_hba {
#define LPFC_FCF_PRIORITY 2 /* Priority fcf failover */
uint32_t cfg_fcf_failover_policy;
uint32_t cfg_fcp_io_sched;
+ uint32_t cfg_fcp2_no_tgt_reset;
uint32_t cfg_cr_delay;
uint32_t cfg_cr_count;
uint32_t cfg_multi_ring_support;
@@ -714,6 +715,7 @@ struct lpfc_hba {
uint32_t cfg_log_verbose;
uint32_t cfg_aer_support;
uint32_t cfg_sriov_nr_virtfn;
+ uint32_t cfg_request_firmware_upgrade;
uint32_t cfg_iocb_cnt;
uint32_t cfg_suppress_link_up;
#define LPFC_INITIALIZE_LINK 0 /* do normal init_link mbox */
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index ad16e54ac38..a364cae9e98 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -3618,6 +3618,77 @@ static DEVICE_ATTR(lpfc_sriov_nr_virtfn, S_IRUGO | S_IWUSR,
lpfc_sriov_nr_virtfn_show, lpfc_sriov_nr_virtfn_store);
/**
+ * lpfc_request_firmware_store - Request for Linux generic firmware upgrade
+ *
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: containing the string the number of vfs to be enabled.
+ * @count: unused variable.
+ *
+ * Description:
+ *
+ * Returns:
+ * length of the buf on success if val is in range the intended mode
+ * is supported.
+ * -EINVAL if val out of range or intended mode is not supported.
+ **/
+static ssize_t
+lpfc_request_firmware_upgrade_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ int val = 0, rc = -EINVAL;
+
+ /* Sanity check on user data */
+ if (!isdigit(buf[0]))
+ return -EINVAL;
+ if (sscanf(buf, "%i", &val) != 1)
+ return -EINVAL;
+ if (val != 1)
+ return -EINVAL;
+
+ rc = lpfc_sli4_request_firmware_update(phba, RUN_FW_UPGRADE);
+ if (rc)
+ rc = -EPERM;
+ else
+ rc = strlen(buf);
+ return rc;
+}
+
+static int lpfc_req_fw_upgrade;
+module_param(lpfc_req_fw_upgrade, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(lpfc_req_fw_upgrade, "Enable Linux generic firmware upgrade");
+lpfc_param_show(request_firmware_upgrade)
+
+/**
+ * lpfc_request_firmware_upgrade_init - Enable initial linux generic fw upgrade
+ * @phba: lpfc_hba pointer.
+ * @val: 0 or 1.
+ *
+ * Description:
+ * Set the initial Linux generic firmware upgrade enable or disable flag.
+ *
+ * Returns:
+ * zero if val saved.
+ * -EINVAL val out of range
+ **/
+static int
+lpfc_request_firmware_upgrade_init(struct lpfc_hba *phba, int val)
+{
+ if (val >= 0 && val <= 1) {
+ phba->cfg_request_firmware_upgrade = val;
+ return 0;
+ }
+ return -EINVAL;
+}
+static DEVICE_ATTR(lpfc_req_fw_upgrade, S_IRUGO | S_IWUSR,
+ lpfc_request_firmware_upgrade_show,
+ lpfc_request_firmware_upgrade_store);
+
+/**
* lpfc_fcp_imax_store
*
* @dev: class device that is converted into a Scsi_host.
@@ -3788,6 +3859,16 @@ LPFC_ATTR_RW(fcp_io_sched, 0, 0, 1, "Determine scheduling algrithmn for "
"issuing commands [0] - Round Robin, [1] - Current CPU");
/*
+# lpfc_fcp2_no_tgt_reset: Determine bus reset behavior
+# range is [0,1]. Default value is 0.
+# For [0], bus reset issues target reset to ALL devices
+# For [1], bus reset issues target reset to non-FCP2 devices
+*/
+LPFC_ATTR_RW(fcp2_no_tgt_reset, 0, 0, 1, "Determine bus reset behavior for "
+ "FCP2 devices [0] - issue tgt reset, [1] - no tgt reset");
+
+
+/*
# lpfc_cr_delay & lpfc_cr_count: Default values for I/O colaesing
# cr_delay (msec) or cr_count outstanding commands. cr_delay can take
# value [0,63]. cr_count can take value [1,255]. Default value of cr_delay
@@ -4029,6 +4110,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_scan_down,
&dev_attr_lpfc_link_speed,
&dev_attr_lpfc_fcp_io_sched,
+ &dev_attr_lpfc_fcp2_no_tgt_reset,
&dev_attr_lpfc_cr_delay,
&dev_attr_lpfc_cr_count,
&dev_attr_lpfc_multi_ring_support,
@@ -4069,6 +4151,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_aer_support,
&dev_attr_lpfc_aer_state_cleanup,
&dev_attr_lpfc_sriov_nr_virtfn,
+ &dev_attr_lpfc_req_fw_upgrade,
&dev_attr_lpfc_suppress_link_up,
&dev_attr_lpfc_iocb_cnt,
&dev_attr_iocb_hw,
@@ -5019,6 +5102,7 @@ void
lpfc_get_cfgparam(struct lpfc_hba *phba)
{
lpfc_fcp_io_sched_init(phba, lpfc_fcp_io_sched);
+ lpfc_fcp2_no_tgt_reset_init(phba, lpfc_fcp2_no_tgt_reset);
lpfc_cr_delay_init(phba, lpfc_cr_delay);
lpfc_cr_count_init(phba, lpfc_cr_count);
lpfc_multi_ring_support_init(phba, lpfc_multi_ring_support);
@@ -5051,6 +5135,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
lpfc_hba_log_verbose_init(phba, lpfc_log_verbose);
lpfc_aer_support_init(phba, lpfc_aer_support);
lpfc_sriov_nr_virtfn_init(phba, lpfc_sriov_nr_virtfn);
+ lpfc_request_firmware_upgrade_init(phba, lpfc_req_fw_upgrade);
lpfc_suppress_link_up_init(phba, lpfc_suppress_link_up);
lpfc_iocb_cnt_init(phba, lpfc_iocb_cnt);
phba->cfg_enable_dss = 1;
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index 4380a44000b..69d66e3662c 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -468,3 +468,4 @@ void lpfc_sli4_node_prep(struct lpfc_hba *);
int lpfc_sli4_xri_sgl_update(struct lpfc_hba *);
void lpfc_free_sgl_list(struct lpfc_hba *, struct list_head *);
uint32_t lpfc_sli_port_speed_get(struct lpfc_hba *);
+int lpfc_sli4_request_firmware_update(struct lpfc_hba *, uint8_t);
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
index 7ffabb7e3af..65f9fb6862e 100644
--- a/drivers/scsi/lpfc/lpfc_ct.c
+++ b/drivers/scsi/lpfc/lpfc_ct.c
@@ -634,7 +634,7 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
/* Check for retry */
if (vport->fc_ns_retry < LPFC_MAX_NS_RETRY) {
if (irsp->ulpStatus != IOSTAT_LOCAL_REJECT ||
- (irsp->un.ulpWord[4] && IOERR_PARAM_MASK) !=
+ (irsp->un.ulpWord[4] & IOERR_PARAM_MASK) !=
IOERR_NO_RESOURCES)
vport->fc_ns_retry++;
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index f19e9b6f9f1..b9440deaad4 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -1182,8 +1182,6 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
sp->cmn.w2.r_a_tov = 0;
sp->cmn.virtual_fabric_support = 0;
sp->cls1.classValid = 0;
- sp->cls2.seqDelivery = 1;
- sp->cls3.seqDelivery = 1;
if (sp->cmn.fcphLow < FC_PH3)
sp->cmn.fcphLow = FC_PH3;
if (sp->cmn.fcphHigh < FC_PH3)
@@ -1198,7 +1196,13 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
/* Set the fcfi to the fcfi we registered with */
elsiocb->iocb.ulpContext = phba->fcf.fcfi;
}
+ /* Can't do SLI4 class2 without support sequence coalescing */
+ sp->cls2.classValid = 0;
+ sp->cls2.seqDelivery = 0;
} else {
+ /* Historical, setting sequential-delivery bit for SLI3 */
+ sp->cls2.seqDelivery = (sp->cls2.classValid) ? 1 : 0;
+ sp->cls3.seqDelivery = (sp->cls3.classValid) ? 1 : 0;
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 */
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
index 2cdeb5434fb..a47cfbdd05f 100644
--- a/drivers/scsi/lpfc/lpfc_hw4.h
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -3219,6 +3219,9 @@ struct wqe_common {
#define wqe_dif_SHIFT 0
#define wqe_dif_MASK 0x00000003
#define wqe_dif_WORD word7
+#define LPFC_WQE_DIF_PASSTHRU 1
+#define LPFC_WQE_DIF_STRIP 2
+#define LPFC_WQE_DIF_INSERT 3
#define wqe_ct_SHIFT 2
#define wqe_ct_MASK 0x00000003
#define wqe_ct_WORD word7
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 7dc4218d9c4..c20eec78adc 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -3854,7 +3854,7 @@ static void
lpfc_sli4_async_sli_evt(struct lpfc_hba *phba, struct lpfc_acqe_sli *acqe_sli)
{
char port_name;
- char message[80];
+ char message[128];
uint8_t status;
struct lpfc_acqe_misconfigured_event *misconfigured;
@@ -9450,7 +9450,7 @@ lpfc_write_firmware(const struct firmware *fw, void *context)
struct lpfc_dmabuf *dmabuf, *next;
uint32_t offset = 0, temp_offset = 0;
- /* It can be null, sanity check */
+ /* It can be null in no-wait mode, sanity check */
if (!fw) {
rc = -ENXIO;
goto out;
@@ -9528,11 +9528,48 @@ release_out:
release_firmware(fw);
out:
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "3024 Firmware update done: %d.", rc);
+ "3024 Firmware update done: %d.\n", rc);
return;
}
/**
+ * lpfc_sli4_request_firmware_update - Request linux generic firmware upgrade
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is called to perform Linux generic firmware upgrade on device
+ * that supports such feature.
+ **/
+int
+lpfc_sli4_request_firmware_update(struct lpfc_hba *phba, uint8_t fw_upgrade)
+{
+ uint8_t file_name[ELX_MODEL_NAME_SIZE];
+ int ret;
+ const struct firmware *fw;
+
+ /* Only supported on SLI4 interface type 2 for now */
+ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+ LPFC_SLI_INTF_IF_TYPE_2)
+ return -EPERM;
+
+ snprintf(file_name, ELX_MODEL_NAME_SIZE, "%s.grp", phba->ModelName);
+
+ if (fw_upgrade == INT_FW_UPGRADE) {
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ file_name, &phba->pcidev->dev,
+ GFP_KERNEL, (void *)phba,
+ lpfc_write_firmware);
+ } else if (fw_upgrade == RUN_FW_UPGRADE) {
+ ret = request_firmware(&fw, file_name, &phba->pcidev->dev);
+ if (!ret)
+ lpfc_write_firmware(fw, (void *)phba);
+ } else {
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/**
* lpfc_pci_probe_one_s4 - PCI probe func to reg SLI-4 device to PCI subsys
* @pdev: pointer to PCI device
* @pid: pointer to PCI device identifier
@@ -9560,7 +9597,6 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
uint32_t cfg_mode, intr_mode;
int mcnt;
int adjusted_fcp_io_channel;
- uint8_t file_name[ELX_MODEL_NAME_SIZE];
/* Allocate memory for HBA structure */
phba = lpfc_hba_alloc(pdev);
@@ -9703,16 +9739,9 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
/* Perform post initialization setup */
lpfc_post_init_setup(phba);
- /* check for firmware upgrade or downgrade (if_type 2 only) */
- if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) ==
- LPFC_SLI_INTF_IF_TYPE_2) {
- snprintf(file_name, ELX_MODEL_NAME_SIZE, "%s.grp",
- phba->ModelName);
- ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
- file_name, &phba->pcidev->dev,
- GFP_KERNEL, (void *)phba,
- lpfc_write_firmware);
- }
+ /* check for firmware upgrade or downgrade */
+ if (phba->cfg_request_firmware_upgrade)
+ ret = lpfc_sli4_request_firmware_update(phba, INT_FW_UPGRADE);
/* Check if there are static vports to be created. */
lpfc_create_static_vport(phba);
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index 7f45ac9964a..60e5a177644 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -3227,6 +3227,21 @@ lpfc_bg_scsi_prep_dma_buf_s4(struct lpfc_hba *phba,
}
}
+ switch (scsi_get_prot_op(scsi_cmnd)) {
+ case SCSI_PROT_WRITE_STRIP:
+ case SCSI_PROT_READ_STRIP:
+ lpfc_cmd->cur_iocbq.iocb_flag |= LPFC_IO_DIF_STRIP;
+ break;
+ case SCSI_PROT_WRITE_INSERT:
+ case SCSI_PROT_READ_INSERT:
+ lpfc_cmd->cur_iocbq.iocb_flag |= LPFC_IO_DIF_INSERT;
+ break;
+ case SCSI_PROT_WRITE_PASS:
+ case SCSI_PROT_READ_PASS:
+ lpfc_cmd->cur_iocbq.iocb_flag |= LPFC_IO_DIF_PASS;
+ break;
+ }
+
fcpdl = lpfc_bg_scsi_adjust_dl(phba, lpfc_cmd);
fcp_cmnd->fcpDl = be32_to_cpu(fcpdl);
@@ -3236,7 +3251,6 @@ lpfc_bg_scsi_prep_dma_buf_s4(struct lpfc_hba *phba,
* we need to set word 4 of IOCB here
*/
iocb_cmd->un.fcpi.fcpi_parm = fcpdl;
- lpfc_cmd->cur_iocbq.iocb_flag |= LPFC_IO_DIF;
return 0;
err:
@@ -4914,6 +4928,9 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
if (!NLP_CHK_NODE_ACT(ndlp))
continue;
+ if (vport->phba->cfg_fcp2_no_tgt_reset &&
+ (ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE))
+ continue;
if (ndlp->nlp_state == NLP_STE_MAPPED_NODE &&
ndlp->nlp_sid == i &&
ndlp->rport) {
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index d7f3313ef88..624eab37039 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -8068,10 +8068,6 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
LPFC_WQE_LENLOC_WORD4);
bf_set(wqe_ebde_cnt, &wqe->fcp_iwrite.wqe_com, 0);
bf_set(wqe_pu, &wqe->fcp_iwrite.wqe_com, iocbq->iocb.ulpPU);
- if (iocbq->iocb_flag & LPFC_IO_DIF) {
- iocbq->iocb_flag &= ~LPFC_IO_DIF;
- bf_set(wqe_dif, &wqe->generic.wqe_com, 1);
- }
bf_set(wqe_dbde, &wqe->fcp_iwrite.wqe_com, 1);
break;
case CMD_FCP_IREAD64_CR:
@@ -8091,10 +8087,6 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
LPFC_WQE_LENLOC_WORD4);
bf_set(wqe_ebde_cnt, &wqe->fcp_iread.wqe_com, 0);
bf_set(wqe_pu, &wqe->fcp_iread.wqe_com, iocbq->iocb.ulpPU);
- if (iocbq->iocb_flag & LPFC_IO_DIF) {
- iocbq->iocb_flag &= ~LPFC_IO_DIF;
- bf_set(wqe_dif, &wqe->generic.wqe_com, 1);
- }
bf_set(wqe_dbde, &wqe->fcp_iread.wqe_com, 1);
break;
case CMD_FCP_ICMND64_CR:
@@ -8304,6 +8296,14 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
break;
}
+ if (iocbq->iocb_flag & LPFC_IO_DIF_PASS)
+ bf_set(wqe_dif, &wqe->generic.wqe_com, LPFC_WQE_DIF_PASSTHRU);
+ else if (iocbq->iocb_flag & LPFC_IO_DIF_STRIP)
+ bf_set(wqe_dif, &wqe->generic.wqe_com, LPFC_WQE_DIF_STRIP);
+ else if (iocbq->iocb_flag & LPFC_IO_DIF_INSERT)
+ bf_set(wqe_dif, &wqe->generic.wqe_com, LPFC_WQE_DIF_INSERT);
+ iocbq->iocb_flag &= ~(LPFC_IO_DIF_PASS | LPFC_IO_DIF_STRIP |
+ LPFC_IO_DIF_INSERT);
bf_set(wqe_xri_tag, &wqe->generic.wqe_com, xritag);
bf_set(wqe_reqtag, &wqe->generic.wqe_com, iocbq->iotag);
wqe->generic.wqe_com.abort_tag = abort_tag;
diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h
index 2f48d000a3b..9d2e0c6fe33 100644
--- a/drivers/scsi/lpfc/lpfc_sli.h
+++ b/drivers/scsi/lpfc/lpfc_sli.h
@@ -69,7 +69,9 @@ struct lpfc_iocbq {
#define LPFC_USE_FCPWQIDX 0x80 /* Submit to specified FCPWQ index */
#define DSS_SECURITY_OP 0x100 /* security IO */
#define LPFC_IO_ON_TXCMPLQ 0x200 /* The IO is still on the TXCMPLQ */
-#define LPFC_IO_DIF 0x400 /* T10 DIF IO */
+#define LPFC_IO_DIF_PASS 0x400 /* T10 DIF IO pass-thru prot */
+#define LPFC_IO_DIF_STRIP 0x800 /* T10 DIF IO strip prot */
+#define LPFC_IO_DIF_INSERT 0x1000 /* T10 DIF IO insert prot */
#define LPFC_FIP_ELS_ID_MASK 0xc000 /* ELS_ID range 0-3, non-shifted mask */
#define LPFC_FIP_ELS_ID_SHIFT 14
diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h
index f44a06a4c6e..44c427a45d6 100644
--- a/drivers/scsi/lpfc/lpfc_sli4.h
+++ b/drivers/scsi/lpfc/lpfc_sli4.h
@@ -82,6 +82,9 @@
#define LPFC_FW_RESET_MAXIMUM_WAIT_10MS_CNT 12000
+#define INT_FW_UPGRADE 0
+#define RUN_FW_UPGRADE 1
+
enum lpfc_sli4_queue_type {
LPFC_EQ,
LPFC_GCQ,
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index 0c2149189dd..ba596e854bb 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.35"
+#define LPFC_DRIVER_VERSION "8.3.36"
#define LPFC_DRIVER_NAME "lpfc"
/* Used for SLI 2/3 */
diff --git a/drivers/scsi/mpt3sas/Kconfig b/drivers/scsi/mpt3sas/Kconfig
new file mode 100644
index 00000000000..81471bf415d
--- /dev/null
+++ b/drivers/scsi/mpt3sas/Kconfig
@@ -0,0 +1,67 @@
+#
+# Kernel configuration file for the MPT3SAS
+#
+# This code is based on drivers/scsi/mpt3sas/Kconfig
+# Copyright (C) 2012 LSI Corporation
+# (mailto:DL-MPTFusionLinux@lsi.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.
+
+# NO WARRANTY
+# THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+# CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+# LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+# solely responsible for determining the appropriateness of using and
+# distributing the Program and assumes all risks associated with its
+# exercise of rights under this Agreement, including but not limited to
+# the risks and costs of program errors, damage to or loss of data,
+# programs or equipment, and unavailability or interruption of operations.
+
+# DISCLAIMER OF LIABILITY
+# NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+# USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+# HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+config SCSI_MPT3SAS
+ tristate "LSI MPT Fusion SAS 3.0 Device Driver"
+ depends on PCI && SCSI
+ select SCSI_SAS_ATTRS
+ select RAID_ATTRS
+ ---help---
+ This driver supports PCI-Express SAS 12Gb/s Host Adapters.
+
+config SCSI_MPT3SAS_MAX_SGE
+ int "LSI MPT Fusion Max number of SG Entries (16 - 256)"
+ depends on PCI && SCSI && SCSI_MPT3SAS
+ default "128"
+ range 16 256
+ ---help---
+ This option allows you to specify the maximum number of scatter-
+ gather entries per I/O. The driver default is 128, which matches
+ MAX_PHYS_SEGMENTS in most kernels. However in SuSE kernels this
+ can be 256. However, it may decreased down to 16. Decreasing this
+ parameter will reduce memory requirements on a per controller instance.
+
+config SCSI_MPT3SAS_LOGGING
+ bool "LSI MPT Fusion logging facility"
+ depends on PCI && SCSI && SCSI_MPT3SAS
+ ---help---
+ This turns on a logging facility.
diff --git a/drivers/scsi/mpt3sas/Makefile b/drivers/scsi/mpt3sas/Makefile
new file mode 100644
index 00000000000..4c1d2e7a117
--- /dev/null
+++ b/drivers/scsi/mpt3sas/Makefile
@@ -0,0 +1,8 @@
+# mpt3sas makefile
+obj-m += mpt3sas.o
+mpt3sas-y += mpt3sas_base.o \
+ mpt3sas_config.o \
+ mpt3sas_scsih.o \
+ mpt3sas_transport.o \
+ mpt3sas_ctl.o \
+ mpt3sas_trigger_diag.o
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2.h b/drivers/scsi/mpt3sas/mpi/mpi2.h
new file mode 100644
index 00000000000..03317ffea62
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpi/mpi2.h
@@ -0,0 +1,1164 @@
+/*
+ * Copyright (c) 2000-2012 LSI Corporation.
+ *
+ *
+ * Name: mpi2.h
+ * Title: MPI Message independent structures and definitions
+ * including System Interface Register Set and
+ * scatter/gather formats.
+ * Creation Date: June 21, 2006
+ *
+ * mpi2.h Version: 02.00.26
+ *
+ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25
+ * prefix are for use only on MPI v2.5 products, and must not be used
+ * with MPI v2.0 products. Unless otherwise noted, names beginning with
+ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products.
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 06-04-07 02.00.01 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 06-26-07 02.00.02 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 08-31-07 02.00.03 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Moved ReplyPostHostIndex register to offset 0x6C of the
+ * MPI2_SYSTEM_INTERFACE_REGS and modified the define for
+ * MPI2_REPLY_POST_HOST_INDEX_OFFSET.
+ * Added union of request descriptors.
+ * Added union of reply descriptors.
+ * 10-31-07 02.00.04 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added define for MPI2_VERSION_02_00.
+ * Fixed the size of the FunctionDependent5 field in the
+ * MPI2_DEFAULT_REPLY structure.
+ * 12-18-07 02.00.05 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Removed the MPI-defined Fault Codes and extended the
+ * product specific codes up to 0xEFFF.
+ * Added a sixth key value for the WriteSequence register
+ * and changed the flush value to 0x0.
+ * Added message function codes for Diagnostic Buffer Post
+ * and Diagnsotic Release.
+ * New IOCStatus define: MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED
+ * Moved MPI2_VERSION_UNION from mpi2_ioc.h.
+ * 02-29-08 02.00.06 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 03-03-08 02.00.07 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 05-21-08 02.00.08 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added #defines for marking a reply descriptor as unused.
+ * 06-27-08 02.00.09 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 10-02-08 02.00.10 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Moved LUN field defines from mpi2_init.h.
+ * 01-19-09 02.00.11 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 05-06-09 02.00.12 Bumped MPI2_HEADER_VERSION_UNIT.
+ * In all request and reply descriptors, replaced VF_ID
+ * field with MSIxIndex field.
+ * Removed DevHandle field from
+ * MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR and made those
+ * bytes reserved.
+ * Added RAID Accelerator functionality.
+ * 07-30-09 02.00.13 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 10-28-09 02.00.14 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added MSI-x index mask and shift for Reply Post Host
+ * Index register.
+ * Added function code for Host Based Discovery Action.
+ * 02-10-10 02.00.15 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added define for MPI2_FUNCTION_PWR_MGMT_CONTROL.
+ * Added defines for product-specific range of message
+ * function codes, 0xF0 to 0xFF.
+ * 05-12-10 02.00.16 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added alternative defines for the SGE Direction bit.
+ * 08-11-10 02.00.17 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 11-10-10 02.00.18 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added MPI2_IEEE_SGE_FLAGS_SYSTEMPLBCPI_ADDR define.
+ * 02-23-11 02.00.19 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added MPI2_FUNCTION_SEND_HOST_MESSAGE.
+ * 03-09-11 02.00.20 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 05-25-11 02.00.21 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 08-24-11 02.00.22 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 11-18-11 02.00.23 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Incorporating additions for MPI v2.5.
+ * 02-06-12 02.00.24 Bumped MPI2_HEADER_VERSION_UNIT.
+ * 03-29-12 02.00.25 Bumped MPI2_HEADER_VERSION_UNIT.
+ * Added Hard Reset delay timings.
+ * 07-10-12 02.00.26 Bumped MPI2_HEADER_VERSION_UNIT.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_H
+#define MPI2_H
+
+/*****************************************************************************
+*
+* MPI Version Definitions
+*
+*****************************************************************************/
+
+#define MPI2_VERSION_MAJOR_MASK (0xFF00)
+#define MPI2_VERSION_MAJOR_SHIFT (8)
+#define MPI2_VERSION_MINOR_MASK (0x00FF)
+#define MPI2_VERSION_MINOR_SHIFT (0)
+
+/*major version for all MPI v2.x */
+#define MPI2_VERSION_MAJOR (0x02)
+
+/*minor version for MPI v2.0 compatible products */
+#define MPI2_VERSION_MINOR (0x00)
+#define MPI2_VERSION ((MPI2_VERSION_MAJOR << MPI2_VERSION_MAJOR_SHIFT) | \
+ MPI2_VERSION_MINOR)
+#define MPI2_VERSION_02_00 (0x0200)
+
+/*minor version for MPI v2.5 compatible products */
+#define MPI25_VERSION_MINOR (0x05)
+#define MPI25_VERSION ((MPI2_VERSION_MAJOR << MPI2_VERSION_MAJOR_SHIFT) | \
+ MPI25_VERSION_MINOR)
+#define MPI2_VERSION_02_05 (0x0205)
+
+/*Unit and Dev versioning for this MPI header set */
+#define MPI2_HEADER_VERSION_UNIT (0x1A)
+#define MPI2_HEADER_VERSION_DEV (0x00)
+#define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00)
+#define MPI2_HEADER_VERSION_UNIT_SHIFT (8)
+#define MPI2_HEADER_VERSION_DEV_MASK (0x00FF)
+#define MPI2_HEADER_VERSION_DEV_SHIFT (0)
+#define MPI2_HEADER_VERSION ((MPI2_HEADER_VERSION_UNIT << 8) | \
+ MPI2_HEADER_VERSION_DEV)
+
+/*****************************************************************************
+*
+* IOC State Definitions
+*
+*****************************************************************************/
+
+#define MPI2_IOC_STATE_RESET (0x00000000)
+#define MPI2_IOC_STATE_READY (0x10000000)
+#define MPI2_IOC_STATE_OPERATIONAL (0x20000000)
+#define MPI2_IOC_STATE_FAULT (0x40000000)
+
+#define MPI2_IOC_STATE_MASK (0xF0000000)
+#define MPI2_IOC_STATE_SHIFT (28)
+
+/*Fault state range for prodcut specific codes */
+#define MPI2_FAULT_PRODUCT_SPECIFIC_MIN (0x0000)
+#define MPI2_FAULT_PRODUCT_SPECIFIC_MAX (0xEFFF)
+
+/*****************************************************************************
+*
+* System Interface Register Definitions
+*
+*****************************************************************************/
+
+typedef volatile struct _MPI2_SYSTEM_INTERFACE_REGS {
+ U32 Doorbell; /*0x00 */
+ U32 WriteSequence; /*0x04 */
+ U32 HostDiagnostic; /*0x08 */
+ U32 Reserved1; /*0x0C */
+ U32 DiagRWData; /*0x10 */
+ U32 DiagRWAddressLow; /*0x14 */
+ U32 DiagRWAddressHigh; /*0x18 */
+ U32 Reserved2[5]; /*0x1C */
+ U32 HostInterruptStatus; /*0x30 */
+ U32 HostInterruptMask; /*0x34 */
+ U32 DCRData; /*0x38 */
+ U32 DCRAddress; /*0x3C */
+ U32 Reserved3[2]; /*0x40 */
+ U32 ReplyFreeHostIndex; /*0x48 */
+ U32 Reserved4[8]; /*0x4C */
+ U32 ReplyPostHostIndex; /*0x6C */
+ U32 Reserved5; /*0x70 */
+ U32 HCBSize; /*0x74 */
+ U32 HCBAddressLow; /*0x78 */
+ U32 HCBAddressHigh; /*0x7C */
+ U32 Reserved6[16]; /*0x80 */
+ U32 RequestDescriptorPostLow; /*0xC0 */
+ U32 RequestDescriptorPostHigh; /*0xC4 */
+ U32 Reserved7[14]; /*0xC8 */
+} MPI2_SYSTEM_INTERFACE_REGS,
+ *PTR_MPI2_SYSTEM_INTERFACE_REGS,
+ Mpi2SystemInterfaceRegs_t,
+ *pMpi2SystemInterfaceRegs_t;
+
+/*
+ *Defines for working with the Doorbell register.
+ */
+#define MPI2_DOORBELL_OFFSET (0x00000000)
+
+/*IOC --> System values */
+#define MPI2_DOORBELL_USED (0x08000000)
+#define MPI2_DOORBELL_WHO_INIT_MASK (0x07000000)
+#define MPI2_DOORBELL_WHO_INIT_SHIFT (24)
+#define MPI2_DOORBELL_FAULT_CODE_MASK (0x0000FFFF)
+#define MPI2_DOORBELL_DATA_MASK (0x0000FFFF)
+
+/*System --> IOC values */
+#define MPI2_DOORBELL_FUNCTION_MASK (0xFF000000)
+#define MPI2_DOORBELL_FUNCTION_SHIFT (24)
+#define MPI2_DOORBELL_ADD_DWORDS_MASK (0x00FF0000)
+#define MPI2_DOORBELL_ADD_DWORDS_SHIFT (16)
+
+/*
+ *Defines for the WriteSequence register
+ */
+#define MPI2_WRITE_SEQUENCE_OFFSET (0x00000004)
+#define MPI2_WRSEQ_KEY_VALUE_MASK (0x0000000F)
+#define MPI2_WRSEQ_FLUSH_KEY_VALUE (0x0)
+#define MPI2_WRSEQ_1ST_KEY_VALUE (0xF)
+#define MPI2_WRSEQ_2ND_KEY_VALUE (0x4)
+#define MPI2_WRSEQ_3RD_KEY_VALUE (0xB)
+#define MPI2_WRSEQ_4TH_KEY_VALUE (0x2)
+#define MPI2_WRSEQ_5TH_KEY_VALUE (0x7)
+#define MPI2_WRSEQ_6TH_KEY_VALUE (0xD)
+
+/*
+ *Defines for the HostDiagnostic register
+ */
+#define MPI2_HOST_DIAGNOSTIC_OFFSET (0x00000008)
+
+#define MPI2_DIAG_BOOT_DEVICE_SELECT_MASK (0x00001800)
+#define MPI2_DIAG_BOOT_DEVICE_SELECT_DEFAULT (0x00000000)
+#define MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW (0x00000800)
+
+#define MPI2_DIAG_CLEAR_FLASH_BAD_SIG (0x00000400)
+#define MPI2_DIAG_FORCE_HCB_ON_RESET (0x00000200)
+#define MPI2_DIAG_HCB_MODE (0x00000100)
+#define MPI2_DIAG_DIAG_WRITE_ENABLE (0x00000080)
+#define MPI2_DIAG_FLASH_BAD_SIG (0x00000040)
+#define MPI2_DIAG_RESET_HISTORY (0x00000020)
+#define MPI2_DIAG_DIAG_RW_ENABLE (0x00000010)
+#define MPI2_DIAG_RESET_ADAPTER (0x00000004)
+#define MPI2_DIAG_HOLD_IOC_RESET (0x00000002)
+
+/*
+ *Offsets for DiagRWData and address
+ */
+#define MPI2_DIAG_RW_DATA_OFFSET (0x00000010)
+#define MPI2_DIAG_RW_ADDRESS_LOW_OFFSET (0x00000014)
+#define MPI2_DIAG_RW_ADDRESS_HIGH_OFFSET (0x00000018)
+
+/*
+ *Defines for the HostInterruptStatus register
+ */
+#define MPI2_HOST_INTERRUPT_STATUS_OFFSET (0x00000030)
+#define MPI2_HIS_SYS2IOC_DB_STATUS (0x80000000)
+#define MPI2_HIS_IOP_DOORBELL_STATUS MPI2_HIS_SYS2IOC_DB_STATUS
+#define MPI2_HIS_RESET_IRQ_STATUS (0x40000000)
+#define MPI2_HIS_REPLY_DESCRIPTOR_INTERRUPT (0x00000008)
+#define MPI2_HIS_IOC2SYS_DB_STATUS (0x00000001)
+#define MPI2_HIS_DOORBELL_INTERRUPT MPI2_HIS_IOC2SYS_DB_STATUS
+
+/*
+ *Defines for the HostInterruptMask register
+ */
+#define MPI2_HOST_INTERRUPT_MASK_OFFSET (0x00000034)
+#define MPI2_HIM_RESET_IRQ_MASK (0x40000000)
+#define MPI2_HIM_REPLY_INT_MASK (0x00000008)
+#define MPI2_HIM_RIM MPI2_HIM_REPLY_INT_MASK
+#define MPI2_HIM_IOC2SYS_DB_MASK (0x00000001)
+#define MPI2_HIM_DIM MPI2_HIM_IOC2SYS_DB_MASK
+
+/*
+ *Offsets for DCRData and address
+ */
+#define MPI2_DCR_DATA_OFFSET (0x00000038)
+#define MPI2_DCR_ADDRESS_OFFSET (0x0000003C)
+
+/*
+ *Offset for the Reply Free Queue
+ */
+#define MPI2_REPLY_FREE_HOST_INDEX_OFFSET (0x00000048)
+
+/*
+ *Defines for the Reply Descriptor Post Queue
+ */
+#define MPI2_REPLY_POST_HOST_INDEX_OFFSET (0x0000006C)
+#define MPI2_REPLY_POST_HOST_INDEX_MASK (0x00FFFFFF)
+#define MPI2_RPHI_MSIX_INDEX_MASK (0xFF000000)
+#define MPI2_RPHI_MSIX_INDEX_SHIFT (24)
+
+/*
+ *Defines for the HCBSize and address
+ */
+#define MPI2_HCB_SIZE_OFFSET (0x00000074)
+#define MPI2_HCB_SIZE_SIZE_MASK (0xFFFFF000)
+#define MPI2_HCB_SIZE_HCB_ENABLE (0x00000001)
+
+#define MPI2_HCB_ADDRESS_LOW_OFFSET (0x00000078)
+#define MPI2_HCB_ADDRESS_HIGH_OFFSET (0x0000007C)
+
+/*
+ *Offsets for the Request Queue
+ */
+#define MPI2_REQUEST_DESCRIPTOR_POST_LOW_OFFSET (0x000000C0)
+#define MPI2_REQUEST_DESCRIPTOR_POST_HIGH_OFFSET (0x000000C4)
+
+/*Hard Reset delay timings */
+#define MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC (50000)
+#define MPI2_HARD_RESET_PCIE_RESET_READ_WINDOW_MICRO_SEC (255000)
+#define MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC (256000)
+
+/*****************************************************************************
+*
+* Message Descriptors
+*
+*****************************************************************************/
+
+/*Request Descriptors */
+
+/*Default Request Descriptor */
+typedef struct _MPI2_DEFAULT_REQUEST_DESCRIPTOR {
+ U8 RequestFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 SMID; /*0x02 */
+ U16 LMID; /*0x04 */
+ U16 DescriptorTypeDependent; /*0x06 */
+} MPI2_DEFAULT_REQUEST_DESCRIPTOR,
+ *PTR_MPI2_DEFAULT_REQUEST_DESCRIPTOR,
+ Mpi2DefaultRequestDescriptor_t,
+ *pMpi2DefaultRequestDescriptor_t;
+
+/*defines for the RequestFlags field */
+#define MPI2_REQ_DESCRIPT_FLAGS_TYPE_MASK (0x0E)
+#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO (0x00)
+#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET (0x02)
+#define MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY (0x06)
+#define MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE (0x08)
+#define MPI2_REQ_DESCRIPT_FLAGS_RAID_ACCELERATOR (0x0A)
+#define MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO (0x0C)
+
+#define MPI2_REQ_DESCRIPT_FLAGS_IOC_FIFO_MARKER (0x01)
+
+/*High Priority Request Descriptor */
+typedef struct _MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR {
+ U8 RequestFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 SMID; /*0x02 */
+ U16 LMID; /*0x04 */
+ U16 Reserved1; /*0x06 */
+} MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR,
+ *PTR_MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR,
+ Mpi2HighPriorityRequestDescriptor_t,
+ *pMpi2HighPriorityRequestDescriptor_t;
+
+/*SCSI IO Request Descriptor */
+typedef struct _MPI2_SCSI_IO_REQUEST_DESCRIPTOR {
+ U8 RequestFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 SMID; /*0x02 */
+ U16 LMID; /*0x04 */
+ U16 DevHandle; /*0x06 */
+} MPI2_SCSI_IO_REQUEST_DESCRIPTOR,
+ *PTR_MPI2_SCSI_IO_REQUEST_DESCRIPTOR,
+ Mpi2SCSIIORequestDescriptor_t,
+ *pMpi2SCSIIORequestDescriptor_t;
+
+/*SCSI Target Request Descriptor */
+typedef struct _MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR {
+ U8 RequestFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 SMID; /*0x02 */
+ U16 LMID; /*0x04 */
+ U16 IoIndex; /*0x06 */
+} MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR,
+ *PTR_MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR,
+ Mpi2SCSITargetRequestDescriptor_t,
+ *pMpi2SCSITargetRequestDescriptor_t;
+
+/*RAID Accelerator Request Descriptor */
+typedef struct _MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR {
+ U8 RequestFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 SMID; /*0x02 */
+ U16 LMID; /*0x04 */
+ U16 Reserved; /*0x06 */
+} MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR,
+ *PTR_MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR,
+ Mpi2RAIDAcceleratorRequestDescriptor_t,
+ *pMpi2RAIDAcceleratorRequestDescriptor_t;
+
+/*Fast Path SCSI IO Request Descriptor */
+typedef MPI2_SCSI_IO_REQUEST_DESCRIPTOR
+ MPI25_FP_SCSI_IO_REQUEST_DESCRIPTOR,
+ *PTR_MPI25_FP_SCSI_IO_REQUEST_DESCRIPTOR,
+ Mpi25FastPathSCSIIORequestDescriptor_t,
+ *pMpi25FastPathSCSIIORequestDescriptor_t;
+
+/*union of Request Descriptors */
+typedef union _MPI2_REQUEST_DESCRIPTOR_UNION {
+ MPI2_DEFAULT_REQUEST_DESCRIPTOR Default;
+ MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR HighPriority;
+ MPI2_SCSI_IO_REQUEST_DESCRIPTOR SCSIIO;
+ MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR SCSITarget;
+ MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR RAIDAccelerator;
+ MPI25_FP_SCSI_IO_REQUEST_DESCRIPTOR FastPathSCSIIO;
+ U64 Words;
+} MPI2_REQUEST_DESCRIPTOR_UNION,
+ *PTR_MPI2_REQUEST_DESCRIPTOR_UNION,
+ Mpi2RequestDescriptorUnion_t,
+ *pMpi2RequestDescriptorUnion_t;
+
+/*Reply Descriptors */
+
+/*Default Reply Descriptor */
+typedef struct _MPI2_DEFAULT_REPLY_DESCRIPTOR {
+ U8 ReplyFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 DescriptorTypeDependent1; /*0x02 */
+ U32 DescriptorTypeDependent2; /*0x04 */
+} MPI2_DEFAULT_REPLY_DESCRIPTOR,
+ *PTR_MPI2_DEFAULT_REPLY_DESCRIPTOR,
+ Mpi2DefaultReplyDescriptor_t,
+ *pMpi2DefaultReplyDescriptor_t;
+
+/*defines for the ReplyFlags field */
+#define MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK (0x0F)
+#define MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS (0x00)
+#define MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY (0x01)
+#define MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS (0x02)
+#define MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER (0x03)
+#define MPI2_RPY_DESCRIPT_FLAGS_RAID_ACCELERATOR_SUCCESS (0x05)
+#define MPI25_RPY_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO_SUCCESS (0x06)
+#define MPI2_RPY_DESCRIPT_FLAGS_UNUSED (0x0F)
+
+/*values for marking a reply descriptor as unused */
+#define MPI2_RPY_DESCRIPT_UNUSED_WORD0_MARK (0xFFFFFFFF)
+#define MPI2_RPY_DESCRIPT_UNUSED_WORD1_MARK (0xFFFFFFFF)
+
+/*Address Reply Descriptor */
+typedef struct _MPI2_ADDRESS_REPLY_DESCRIPTOR {
+ U8 ReplyFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 SMID; /*0x02 */
+ U32 ReplyFrameAddress; /*0x04 */
+} MPI2_ADDRESS_REPLY_DESCRIPTOR,
+ *PTR_MPI2_ADDRESS_REPLY_DESCRIPTOR,
+ Mpi2AddressReplyDescriptor_t,
+ *pMpi2AddressReplyDescriptor_t;
+
+#define MPI2_ADDRESS_REPLY_SMID_INVALID (0x00)
+
+/*SCSI IO Success Reply Descriptor */
+typedef struct _MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR {
+ U8 ReplyFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 SMID; /*0x02 */
+ U16 TaskTag; /*0x04 */
+ U16 Reserved1; /*0x06 */
+} MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR,
+ *PTR_MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR,
+ Mpi2SCSIIOSuccessReplyDescriptor_t,
+ *pMpi2SCSIIOSuccessReplyDescriptor_t;
+
+/*TargetAssist Success Reply Descriptor */
+typedef struct _MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR {
+ U8 ReplyFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 SMID; /*0x02 */
+ U8 SequenceNumber; /*0x04 */
+ U8 Reserved1; /*0x05 */
+ U16 IoIndex; /*0x06 */
+} MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR,
+ *PTR_MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR,
+ Mpi2TargetAssistSuccessReplyDescriptor_t,
+ *pMpi2TargetAssistSuccessReplyDescriptor_t;
+
+/*Target Command Buffer Reply Descriptor */
+typedef struct _MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR {
+ U8 ReplyFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U8 VP_ID; /*0x02 */
+ U8 Flags; /*0x03 */
+ U16 InitiatorDevHandle; /*0x04 */
+ U16 IoIndex; /*0x06 */
+} MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR,
+ *PTR_MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR,
+ Mpi2TargetCommandBufferReplyDescriptor_t,
+ *pMpi2TargetCommandBufferReplyDescriptor_t;
+
+/*defines for Flags field */
+#define MPI2_RPY_DESCRIPT_TCB_FLAGS_PHYNUM_MASK (0x3F)
+
+/*RAID Accelerator Success Reply Descriptor */
+typedef struct _MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR {
+ U8 ReplyFlags; /*0x00 */
+ U8 MSIxIndex; /*0x01 */
+ U16 SMID; /*0x02 */
+ U32 Reserved; /*0x04 */
+} MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR,
+ *PTR_MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR,
+ Mpi2RAIDAcceleratorSuccessReplyDescriptor_t,
+ *pMpi2RAIDAcceleratorSuccessReplyDescriptor_t;
+
+/*Fast Path SCSI IO Success Reply Descriptor */
+typedef MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR
+ MPI25_FP_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR,
+ *PTR_MPI25_FP_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR,
+ Mpi25FastPathSCSIIOSuccessReplyDescriptor_t,
+ *pMpi25FastPathSCSIIOSuccessReplyDescriptor_t;
+
+/*union of Reply Descriptors */
+typedef union _MPI2_REPLY_DESCRIPTORS_UNION {
+ MPI2_DEFAULT_REPLY_DESCRIPTOR Default;
+ MPI2_ADDRESS_REPLY_DESCRIPTOR AddressReply;
+ MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR SCSIIOSuccess;
+ MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR TargetAssistSuccess;
+ MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR TargetCommandBuffer;
+ MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR RAIDAcceleratorSuccess;
+ MPI25_FP_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR FastPathSCSIIOSuccess;
+ U64 Words;
+} MPI2_REPLY_DESCRIPTORS_UNION,
+ *PTR_MPI2_REPLY_DESCRIPTORS_UNION,
+ Mpi2ReplyDescriptorsUnion_t,
+ *pMpi2ReplyDescriptorsUnion_t;
+
+/*****************************************************************************
+*
+* Message Functions
+*
+*****************************************************************************/
+
+#define MPI2_FUNCTION_SCSI_IO_REQUEST (0x00)
+#define MPI2_FUNCTION_SCSI_TASK_MGMT (0x01)
+#define MPI2_FUNCTION_IOC_INIT (0x02)
+#define MPI2_FUNCTION_IOC_FACTS (0x03)
+#define MPI2_FUNCTION_CONFIG (0x04)
+#define MPI2_FUNCTION_PORT_FACTS (0x05)
+#define MPI2_FUNCTION_PORT_ENABLE (0x06)
+#define MPI2_FUNCTION_EVENT_NOTIFICATION (0x07)
+#define MPI2_FUNCTION_EVENT_ACK (0x08)
+#define MPI2_FUNCTION_FW_DOWNLOAD (0x09)
+#define MPI2_FUNCTION_TARGET_ASSIST (0x0B)
+#define MPI2_FUNCTION_TARGET_STATUS_SEND (0x0C)
+#define MPI2_FUNCTION_TARGET_MODE_ABORT (0x0D)
+#define MPI2_FUNCTION_FW_UPLOAD (0x12)
+#define MPI2_FUNCTION_RAID_ACTION (0x15)
+#define MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH (0x16)
+#define MPI2_FUNCTION_TOOLBOX (0x17)
+#define MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR (0x18)
+#define MPI2_FUNCTION_SMP_PASSTHROUGH (0x1A)
+#define MPI2_FUNCTION_SAS_IO_UNIT_CONTROL (0x1B)
+#define MPI2_FUNCTION_SATA_PASSTHROUGH (0x1C)
+#define MPI2_FUNCTION_DIAG_BUFFER_POST (0x1D)
+#define MPI2_FUNCTION_DIAG_RELEASE (0x1E)
+#define MPI2_FUNCTION_TARGET_CMD_BUF_BASE_POST (0x24)
+#define MPI2_FUNCTION_TARGET_CMD_BUF_LIST_POST (0x25)
+#define MPI2_FUNCTION_RAID_ACCELERATOR (0x2C)
+#define MPI2_FUNCTION_HOST_BASED_DISCOVERY_ACTION (0x2F)
+#define MPI2_FUNCTION_PWR_MGMT_CONTROL (0x30)
+#define MPI2_FUNCTION_SEND_HOST_MESSAGE (0x31)
+#define MPI2_FUNCTION_MIN_PRODUCT_SPECIFIC (0xF0)
+#define MPI2_FUNCTION_MAX_PRODUCT_SPECIFIC (0xFF)
+
+/*Doorbell functions */
+#define MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET (0x40)
+#define MPI2_FUNCTION_HANDSHAKE (0x42)
+
+/*****************************************************************************
+*
+* IOC Status Values
+*
+*****************************************************************************/
+
+/*mask for IOCStatus status value */
+#define MPI2_IOCSTATUS_MASK (0x7FFF)
+
+/****************************************************************************
+* Common IOCStatus values for all replies
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_SUCCESS (0x0000)
+#define MPI2_IOCSTATUS_INVALID_FUNCTION (0x0001)
+#define MPI2_IOCSTATUS_BUSY (0x0002)
+#define MPI2_IOCSTATUS_INVALID_SGL (0x0003)
+#define MPI2_IOCSTATUS_INTERNAL_ERROR (0x0004)
+#define MPI2_IOCSTATUS_INVALID_VPID (0x0005)
+#define MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES (0x0006)
+#define MPI2_IOCSTATUS_INVALID_FIELD (0x0007)
+#define MPI2_IOCSTATUS_INVALID_STATE (0x0008)
+#define MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED (0x0009)
+
+/****************************************************************************
+* Config IOCStatus values
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_CONFIG_INVALID_ACTION (0x0020)
+#define MPI2_IOCSTATUS_CONFIG_INVALID_TYPE (0x0021)
+#define MPI2_IOCSTATUS_CONFIG_INVALID_PAGE (0x0022)
+#define MPI2_IOCSTATUS_CONFIG_INVALID_DATA (0x0023)
+#define MPI2_IOCSTATUS_CONFIG_NO_DEFAULTS (0x0024)
+#define MPI2_IOCSTATUS_CONFIG_CANT_COMMIT (0x0025)
+
+/****************************************************************************
+* SCSI IO Reply
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR (0x0040)
+#define MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE (0x0042)
+#define MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE (0x0043)
+#define MPI2_IOCSTATUS_SCSI_DATA_OVERRUN (0x0044)
+#define MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN (0x0045)
+#define MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR (0x0046)
+#define MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR (0x0047)
+#define MPI2_IOCSTATUS_SCSI_TASK_TERMINATED (0x0048)
+#define MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH (0x0049)
+#define MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED (0x004A)
+#define MPI2_IOCSTATUS_SCSI_IOC_TERMINATED (0x004B)
+#define MPI2_IOCSTATUS_SCSI_EXT_TERMINATED (0x004C)
+
+/****************************************************************************
+* For use by SCSI Initiator and SCSI Target end-to-end data protection
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_EEDP_GUARD_ERROR (0x004D)
+#define MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR (0x004E)
+#define MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR (0x004F)
+
+/****************************************************************************
+* SCSI Target values
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_TARGET_INVALID_IO_INDEX (0x0062)
+#define MPI2_IOCSTATUS_TARGET_ABORTED (0x0063)
+#define MPI2_IOCSTATUS_TARGET_NO_CONN_RETRYABLE (0x0064)
+#define MPI2_IOCSTATUS_TARGET_NO_CONNECTION (0x0065)
+#define MPI2_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH (0x006A)
+#define MPI2_IOCSTATUS_TARGET_DATA_OFFSET_ERROR (0x006D)
+#define MPI2_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA (0x006E)
+#define MPI2_IOCSTATUS_TARGET_IU_TOO_SHORT (0x006F)
+#define MPI2_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT (0x0070)
+#define MPI2_IOCSTATUS_TARGET_NAK_RECEIVED (0x0071)
+
+/****************************************************************************
+* Serial Attached SCSI values
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_SAS_SMP_REQUEST_FAILED (0x0090)
+#define MPI2_IOCSTATUS_SAS_SMP_DATA_OVERRUN (0x0091)
+
+/****************************************************************************
+* Diagnostic Buffer Post / Diagnostic Release values
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED (0x00A0)
+
+/****************************************************************************
+* RAID Accelerator values
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_RAID_ACCEL_ERROR (0x00B0)
+
+/****************************************************************************
+* IOCStatus flag to indicate that log info is available
+****************************************************************************/
+
+#define MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE (0x8000)
+
+/****************************************************************************
+* IOCLogInfo Types
+****************************************************************************/
+
+#define MPI2_IOCLOGINFO_TYPE_MASK (0xF0000000)
+#define MPI2_IOCLOGINFO_TYPE_SHIFT (28)
+#define MPI2_IOCLOGINFO_TYPE_NONE (0x0)
+#define MPI2_IOCLOGINFO_TYPE_SCSI (0x1)
+#define MPI2_IOCLOGINFO_TYPE_FC (0x2)
+#define MPI2_IOCLOGINFO_TYPE_SAS (0x3)
+#define MPI2_IOCLOGINFO_TYPE_ISCSI (0x4)
+#define MPI2_IOCLOGINFO_LOG_DATA_MASK (0x0FFFFFFF)
+
+/*****************************************************************************
+*
+* Standard Message Structures
+*
+*****************************************************************************/
+
+/****************************************************************************
+*Request Message Header for all request messages
+****************************************************************************/
+
+typedef struct _MPI2_REQUEST_HEADER {
+ U16 FunctionDependent1; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 FunctionDependent2; /*0x04 */
+ U8 FunctionDependent3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved1; /*0x0A */
+} MPI2_REQUEST_HEADER, *PTR_MPI2_REQUEST_HEADER,
+ MPI2RequestHeader_t, *pMPI2RequestHeader_t;
+
+/****************************************************************************
+* Default Reply
+****************************************************************************/
+
+typedef struct _MPI2_DEFAULT_REPLY {
+ U16 FunctionDependent1; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 FunctionDependent2; /*0x04 */
+ U8 FunctionDependent3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U16 FunctionDependent5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_DEFAULT_REPLY, *PTR_MPI2_DEFAULT_REPLY,
+ MPI2DefaultReply_t, *pMPI2DefaultReply_t;
+
+/*common version structure/union used in messages and configuration pages */
+
+typedef struct _MPI2_VERSION_STRUCT {
+ U8 Dev; /*0x00 */
+ U8 Unit; /*0x01 */
+ U8 Minor; /*0x02 */
+ U8 Major; /*0x03 */
+} MPI2_VERSION_STRUCT;
+
+typedef union _MPI2_VERSION_UNION {
+ MPI2_VERSION_STRUCT Struct;
+ U32 Word;
+} MPI2_VERSION_UNION;
+
+/*LUN field defines, common to many structures */
+#define MPI2_LUN_FIRST_LEVEL_ADDRESSING (0x0000FFFF)
+#define MPI2_LUN_SECOND_LEVEL_ADDRESSING (0xFFFF0000)
+#define MPI2_LUN_THIRD_LEVEL_ADDRESSING (0x0000FFFF)
+#define MPI2_LUN_FOURTH_LEVEL_ADDRESSING (0xFFFF0000)
+#define MPI2_LUN_LEVEL_1_WORD (0xFF00)
+#define MPI2_LUN_LEVEL_1_DWORD (0x0000FF00)
+
+/*****************************************************************************
+*
+* Fusion-MPT MPI Scatter Gather Elements
+*
+*****************************************************************************/
+
+/****************************************************************************
+* MPI Simple Element structures
+****************************************************************************/
+
+typedef struct _MPI2_SGE_SIMPLE32 {
+ U32 FlagsLength;
+ U32 Address;
+} MPI2_SGE_SIMPLE32, *PTR_MPI2_SGE_SIMPLE32,
+ Mpi2SGESimple32_t, *pMpi2SGESimple32_t;
+
+typedef struct _MPI2_SGE_SIMPLE64 {
+ U32 FlagsLength;
+ U64 Address;
+} MPI2_SGE_SIMPLE64, *PTR_MPI2_SGE_SIMPLE64,
+ Mpi2SGESimple64_t, *pMpi2SGESimple64_t;
+
+typedef struct _MPI2_SGE_SIMPLE_UNION {
+ U32 FlagsLength;
+ union {
+ U32 Address32;
+ U64 Address64;
+ } u;
+} MPI2_SGE_SIMPLE_UNION,
+ *PTR_MPI2_SGE_SIMPLE_UNION,
+ Mpi2SGESimpleUnion_t,
+ *pMpi2SGESimpleUnion_t;
+
+/****************************************************************************
+* MPI Chain Element structures - for MPI v2.0 products only
+****************************************************************************/
+
+typedef struct _MPI2_SGE_CHAIN32 {
+ U16 Length;
+ U8 NextChainOffset;
+ U8 Flags;
+ U32 Address;
+} MPI2_SGE_CHAIN32, *PTR_MPI2_SGE_CHAIN32,
+ Mpi2SGEChain32_t, *pMpi2SGEChain32_t;
+
+typedef struct _MPI2_SGE_CHAIN64 {
+ U16 Length;
+ U8 NextChainOffset;
+ U8 Flags;
+ U64 Address;
+} MPI2_SGE_CHAIN64, *PTR_MPI2_SGE_CHAIN64,
+ Mpi2SGEChain64_t, *pMpi2SGEChain64_t;
+
+typedef struct _MPI2_SGE_CHAIN_UNION {
+ U16 Length;
+ U8 NextChainOffset;
+ U8 Flags;
+ union {
+ U32 Address32;
+ U64 Address64;
+ } u;
+} MPI2_SGE_CHAIN_UNION,
+ *PTR_MPI2_SGE_CHAIN_UNION,
+ Mpi2SGEChainUnion_t,
+ *pMpi2SGEChainUnion_t;
+
+/****************************************************************************
+* MPI Transaction Context Element structures - for MPI v2.0 products only
+****************************************************************************/
+
+typedef struct _MPI2_SGE_TRANSACTION32 {
+ U8 Reserved;
+ U8 ContextSize;
+ U8 DetailsLength;
+ U8 Flags;
+ U32 TransactionContext[1];
+ U32 TransactionDetails[1];
+} MPI2_SGE_TRANSACTION32,
+ *PTR_MPI2_SGE_TRANSACTION32,
+ Mpi2SGETransaction32_t,
+ *pMpi2SGETransaction32_t;
+
+typedef struct _MPI2_SGE_TRANSACTION64 {
+ U8 Reserved;
+ U8 ContextSize;
+ U8 DetailsLength;
+ U8 Flags;
+ U32 TransactionContext[2];
+ U32 TransactionDetails[1];
+} MPI2_SGE_TRANSACTION64,
+ *PTR_MPI2_SGE_TRANSACTION64,
+ Mpi2SGETransaction64_t,
+ *pMpi2SGETransaction64_t;
+
+typedef struct _MPI2_SGE_TRANSACTION96 {
+ U8 Reserved;
+ U8 ContextSize;
+ U8 DetailsLength;
+ U8 Flags;
+ U32 TransactionContext[3];
+ U32 TransactionDetails[1];
+} MPI2_SGE_TRANSACTION96, *PTR_MPI2_SGE_TRANSACTION96,
+ Mpi2SGETransaction96_t, *pMpi2SGETransaction96_t;
+
+typedef struct _MPI2_SGE_TRANSACTION128 {
+ U8 Reserved;
+ U8 ContextSize;
+ U8 DetailsLength;
+ U8 Flags;
+ U32 TransactionContext[4];
+ U32 TransactionDetails[1];
+} MPI2_SGE_TRANSACTION128, *PTR_MPI2_SGE_TRANSACTION128,
+ Mpi2SGETransaction_t128, *pMpi2SGETransaction_t128;
+
+typedef struct _MPI2_SGE_TRANSACTION_UNION {
+ U8 Reserved;
+ U8 ContextSize;
+ U8 DetailsLength;
+ U8 Flags;
+ union {
+ U32 TransactionContext32[1];
+ U32 TransactionContext64[2];
+ U32 TransactionContext96[3];
+ U32 TransactionContext128[4];
+ } u;
+ U32 TransactionDetails[1];
+} MPI2_SGE_TRANSACTION_UNION,
+ *PTR_MPI2_SGE_TRANSACTION_UNION,
+ Mpi2SGETransactionUnion_t,
+ *pMpi2SGETransactionUnion_t;
+
+/****************************************************************************
+* MPI SGE union for IO SGL's - for MPI v2.0 products only
+****************************************************************************/
+
+typedef struct _MPI2_MPI_SGE_IO_UNION {
+ union {
+ MPI2_SGE_SIMPLE_UNION Simple;
+ MPI2_SGE_CHAIN_UNION Chain;
+ } u;
+} MPI2_MPI_SGE_IO_UNION, *PTR_MPI2_MPI_SGE_IO_UNION,
+ Mpi2MpiSGEIOUnion_t, *pMpi2MpiSGEIOUnion_t;
+
+/****************************************************************************
+* MPI SGE union for SGL's with Simple and Transaction elements - for MPI v2.0 products only
+****************************************************************************/
+
+typedef struct _MPI2_SGE_TRANS_SIMPLE_UNION {
+ union {
+ MPI2_SGE_SIMPLE_UNION Simple;
+ MPI2_SGE_TRANSACTION_UNION Transaction;
+ } u;
+} MPI2_SGE_TRANS_SIMPLE_UNION,
+ *PTR_MPI2_SGE_TRANS_SIMPLE_UNION,
+ Mpi2SGETransSimpleUnion_t,
+ *pMpi2SGETransSimpleUnion_t;
+
+/****************************************************************************
+* All MPI SGE types union
+****************************************************************************/
+
+typedef struct _MPI2_MPI_SGE_UNION {
+ union {
+ MPI2_SGE_SIMPLE_UNION Simple;
+ MPI2_SGE_CHAIN_UNION Chain;
+ MPI2_SGE_TRANSACTION_UNION Transaction;
+ } u;
+} MPI2_MPI_SGE_UNION, *PTR_MPI2_MPI_SGE_UNION,
+ Mpi2MpiSgeUnion_t, *pMpi2MpiSgeUnion_t;
+
+/****************************************************************************
+* MPI SGE field definition and masks
+****************************************************************************/
+
+/*Flags field bit definitions */
+
+#define MPI2_SGE_FLAGS_LAST_ELEMENT (0x80)
+#define MPI2_SGE_FLAGS_END_OF_BUFFER (0x40)
+#define MPI2_SGE_FLAGS_ELEMENT_TYPE_MASK (0x30)
+#define MPI2_SGE_FLAGS_LOCAL_ADDRESS (0x08)
+#define MPI2_SGE_FLAGS_DIRECTION (0x04)
+#define MPI2_SGE_FLAGS_ADDRESS_SIZE (0x02)
+#define MPI2_SGE_FLAGS_END_OF_LIST (0x01)
+
+#define MPI2_SGE_FLAGS_SHIFT (24)
+
+#define MPI2_SGE_LENGTH_MASK (0x00FFFFFF)
+#define MPI2_SGE_CHAIN_LENGTH_MASK (0x0000FFFF)
+
+/*Element Type */
+
+#define MPI2_SGE_FLAGS_TRANSACTION_ELEMENT (0x00)
+#define MPI2_SGE_FLAGS_SIMPLE_ELEMENT (0x10)
+#define MPI2_SGE_FLAGS_CHAIN_ELEMENT (0x30)
+#define MPI2_SGE_FLAGS_ELEMENT_MASK (0x30)
+
+/*Address location */
+
+#define MPI2_SGE_FLAGS_SYSTEM_ADDRESS (0x00)
+
+/*Direction */
+
+#define MPI2_SGE_FLAGS_IOC_TO_HOST (0x00)
+#define MPI2_SGE_FLAGS_HOST_TO_IOC (0x04)
+
+#define MPI2_SGE_FLAGS_DEST (MPI2_SGE_FLAGS_IOC_TO_HOST)
+#define MPI2_SGE_FLAGS_SOURCE (MPI2_SGE_FLAGS_HOST_TO_IOC)
+
+/*Address Size */
+
+#define MPI2_SGE_FLAGS_32_BIT_ADDRESSING (0x00)
+#define MPI2_SGE_FLAGS_64_BIT_ADDRESSING (0x02)
+
+/*Context Size */
+
+#define MPI2_SGE_FLAGS_32_BIT_CONTEXT (0x00)
+#define MPI2_SGE_FLAGS_64_BIT_CONTEXT (0x02)
+#define MPI2_SGE_FLAGS_96_BIT_CONTEXT (0x04)
+#define MPI2_SGE_FLAGS_128_BIT_CONTEXT (0x06)
+
+#define MPI2_SGE_CHAIN_OFFSET_MASK (0x00FF0000)
+#define MPI2_SGE_CHAIN_OFFSET_SHIFT (16)
+
+/****************************************************************************
+* MPI SGE operation Macros
+****************************************************************************/
+
+/*SIMPLE FlagsLength manipulations... */
+#define MPI2_SGE_SET_FLAGS(f) ((U32)(f) << MPI2_SGE_FLAGS_SHIFT)
+#define MPI2_SGE_GET_FLAGS(f) (((f) & ~MPI2_SGE_LENGTH_MASK) >> \
+ MPI2_SGE_FLAGS_SHIFT)
+#define MPI2_SGE_LENGTH(f) ((f) & MPI2_SGE_LENGTH_MASK)
+#define MPI2_SGE_CHAIN_LENGTH(f) ((f) & MPI2_SGE_CHAIN_LENGTH_MASK)
+
+#define MPI2_SGE_SET_FLAGS_LENGTH(f, l) (MPI2_SGE_SET_FLAGS(f) | \
+ MPI2_SGE_LENGTH(l))
+
+#define MPI2_pSGE_GET_FLAGS(psg) MPI2_SGE_GET_FLAGS((psg)->FlagsLength)
+#define MPI2_pSGE_GET_LENGTH(psg) MPI2_SGE_LENGTH((psg)->FlagsLength)
+#define MPI2_pSGE_SET_FLAGS_LENGTH(psg, f, l) ((psg)->FlagsLength = \
+ MPI2_SGE_SET_FLAGS_LENGTH(f, l))
+
+/*CAUTION - The following are READ-MODIFY-WRITE! */
+#define MPI2_pSGE_SET_FLAGS(psg, f) ((psg)->FlagsLength |= \
+ MPI2_SGE_SET_FLAGS(f))
+#define MPI2_pSGE_SET_LENGTH(psg, l) ((psg)->FlagsLength |= \
+ MPI2_SGE_LENGTH(l))
+
+#define MPI2_GET_CHAIN_OFFSET(x) ((x & MPI2_SGE_CHAIN_OFFSET_MASK) >> \
+ MPI2_SGE_CHAIN_OFFSET_SHIFT)
+
+/*****************************************************************************
+*
+* Fusion-MPT IEEE Scatter Gather Elements
+*
+*****************************************************************************/
+
+/****************************************************************************
+* IEEE Simple Element structures
+****************************************************************************/
+
+/*MPI2_IEEE_SGE_SIMPLE32 is for MPI v2.0 products only */
+typedef struct _MPI2_IEEE_SGE_SIMPLE32 {
+ U32 Address;
+ U32 FlagsLength;
+} MPI2_IEEE_SGE_SIMPLE32, *PTR_MPI2_IEEE_SGE_SIMPLE32,
+ Mpi2IeeeSgeSimple32_t, *pMpi2IeeeSgeSimple32_t;
+
+typedef struct _MPI2_IEEE_SGE_SIMPLE64 {
+ U64 Address;
+ U32 Length;
+ U16 Reserved1;
+ U8 Reserved2;
+ U8 Flags;
+} MPI2_IEEE_SGE_SIMPLE64, *PTR_MPI2_IEEE_SGE_SIMPLE64,
+ Mpi2IeeeSgeSimple64_t, *pMpi2IeeeSgeSimple64_t;
+
+typedef union _MPI2_IEEE_SGE_SIMPLE_UNION {
+ MPI2_IEEE_SGE_SIMPLE32 Simple32;
+ MPI2_IEEE_SGE_SIMPLE64 Simple64;
+} MPI2_IEEE_SGE_SIMPLE_UNION,
+ *PTR_MPI2_IEEE_SGE_SIMPLE_UNION,
+ Mpi2IeeeSgeSimpleUnion_t,
+ *pMpi2IeeeSgeSimpleUnion_t;
+
+/****************************************************************************
+* IEEE Chain Element structures
+****************************************************************************/
+
+/*MPI2_IEEE_SGE_CHAIN32 is for MPI v2.0 products only */
+typedef MPI2_IEEE_SGE_SIMPLE32 MPI2_IEEE_SGE_CHAIN32;
+
+/*MPI2_IEEE_SGE_CHAIN64 is for MPI v2.0 products only */
+typedef MPI2_IEEE_SGE_SIMPLE64 MPI2_IEEE_SGE_CHAIN64;
+
+typedef union _MPI2_IEEE_SGE_CHAIN_UNION {
+ MPI2_IEEE_SGE_CHAIN32 Chain32;
+ MPI2_IEEE_SGE_CHAIN64 Chain64;
+} MPI2_IEEE_SGE_CHAIN_UNION,
+ *PTR_MPI2_IEEE_SGE_CHAIN_UNION,
+ Mpi2IeeeSgeChainUnion_t,
+ *pMpi2IeeeSgeChainUnion_t;
+
+/*MPI25_IEEE_SGE_CHAIN64 is for MPI v2.5 products only */
+typedef struct _MPI25_IEEE_SGE_CHAIN64 {
+ U64 Address;
+ U32 Length;
+ U16 Reserved1;
+ U8 NextChainOffset;
+ U8 Flags;
+} MPI25_IEEE_SGE_CHAIN64,
+ *PTR_MPI25_IEEE_SGE_CHAIN64,
+ Mpi25IeeeSgeChain64_t,
+ *pMpi25IeeeSgeChain64_t;
+
+/****************************************************************************
+* All IEEE SGE types union
+****************************************************************************/
+
+/*MPI2_IEEE_SGE_UNION is for MPI v2.0 products only */
+typedef struct _MPI2_IEEE_SGE_UNION {
+ union {
+ MPI2_IEEE_SGE_SIMPLE_UNION Simple;
+ MPI2_IEEE_SGE_CHAIN_UNION Chain;
+ } u;
+} MPI2_IEEE_SGE_UNION, *PTR_MPI2_IEEE_SGE_UNION,
+ Mpi2IeeeSgeUnion_t, *pMpi2IeeeSgeUnion_t;
+
+/****************************************************************************
+* IEEE SGE union for IO SGL's
+****************************************************************************/
+
+typedef union _MPI25_SGE_IO_UNION {
+ MPI2_IEEE_SGE_SIMPLE64 IeeeSimple;
+ MPI25_IEEE_SGE_CHAIN64 IeeeChain;
+} MPI25_SGE_IO_UNION, *PTR_MPI25_SGE_IO_UNION,
+ Mpi25SGEIOUnion_t, *pMpi25SGEIOUnion_t;
+
+/****************************************************************************
+* IEEE SGE field definitions and masks
+****************************************************************************/
+
+/*Flags field bit definitions */
+
+#define MPI2_IEEE_SGE_FLAGS_ELEMENT_TYPE_MASK (0x80)
+#define MPI25_IEEE_SGE_FLAGS_END_OF_LIST (0x40)
+
+#define MPI2_IEEE32_SGE_FLAGS_SHIFT (24)
+
+#define MPI2_IEEE32_SGE_LENGTH_MASK (0x00FFFFFF)
+
+/*Element Type */
+
+#define MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT (0x00)
+#define MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT (0x80)
+
+/*Data Location Address Space */
+
+#define MPI2_IEEE_SGE_FLAGS_ADDR_MASK (0x03)
+#define MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR (0x00)
+#define MPI2_IEEE_SGE_FLAGS_IOCDDR_ADDR (0x01)
+#define MPI2_IEEE_SGE_FLAGS_IOCPLB_ADDR (0x02)
+#define MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR (0x03)
+#define MPI2_IEEE_SGE_FLAGS_SYSTEMPLBPCI_ADDR (0x03)
+#define MPI2_IEEE_SGE_FLAGS_SYSTEMPLBCPI_ADDR \
+ (MPI2_IEEE_SGE_FLAGS_SYSTEMPLBPCI_ADDR)
+
+/****************************************************************************
+* IEEE SGE operation Macros
+****************************************************************************/
+
+/*SIMPLE FlagsLength manipulations... */
+#define MPI2_IEEE32_SGE_SET_FLAGS(f) ((U32)(f) << MPI2_IEEE32_SGE_FLAGS_SHIFT)
+#define MPI2_IEEE32_SGE_GET_FLAGS(f) (((f) & ~MPI2_IEEE32_SGE_LENGTH_MASK) \
+ >> MPI2_IEEE32_SGE_FLAGS_SHIFT)
+#define MPI2_IEEE32_SGE_LENGTH(f) ((f) & MPI2_IEEE32_SGE_LENGTH_MASK)
+
+#define MPI2_IEEE32_SGE_SET_FLAGS_LENGTH(f, l) (MPI2_IEEE32_SGE_SET_FLAGS(f) |\
+ MPI2_IEEE32_SGE_LENGTH(l))
+
+#define MPI2_IEEE32_pSGE_GET_FLAGS(psg) \
+ MPI2_IEEE32_SGE_GET_FLAGS((psg)->FlagsLength)
+#define MPI2_IEEE32_pSGE_GET_LENGTH(psg) \
+ MPI2_IEEE32_SGE_LENGTH((psg)->FlagsLength)
+#define MPI2_IEEE32_pSGE_SET_FLAGS_LENGTH(psg, f, l) ((psg)->FlagsLength = \
+ MPI2_IEEE32_SGE_SET_FLAGS_LENGTH(f, l))
+
+/*CAUTION - The following are READ-MODIFY-WRITE! */
+#define MPI2_IEEE32_pSGE_SET_FLAGS(psg, f) ((psg)->FlagsLength |= \
+ MPI2_IEEE32_SGE_SET_FLAGS(f))
+#define MPI2_IEEE32_pSGE_SET_LENGTH(psg, l) ((psg)->FlagsLength |= \
+ MPI2_IEEE32_SGE_LENGTH(l))
+
+/*****************************************************************************
+*
+* Fusion-MPT MPI/IEEE Scatter Gather Unions
+*
+*****************************************************************************/
+
+typedef union _MPI2_SIMPLE_SGE_UNION {
+ MPI2_SGE_SIMPLE_UNION MpiSimple;
+ MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple;
+} MPI2_SIMPLE_SGE_UNION, *PTR_MPI2_SIMPLE_SGE_UNION,
+ Mpi2SimpleSgeUntion_t, *pMpi2SimpleSgeUntion_t;
+
+typedef union _MPI2_SGE_IO_UNION {
+ MPI2_SGE_SIMPLE_UNION MpiSimple;
+ MPI2_SGE_CHAIN_UNION MpiChain;
+ MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple;
+ MPI2_IEEE_SGE_CHAIN_UNION IeeeChain;
+} MPI2_SGE_IO_UNION, *PTR_MPI2_SGE_IO_UNION,
+ Mpi2SGEIOUnion_t, *pMpi2SGEIOUnion_t;
+
+/****************************************************************************
+*
+* Values for SGLFlags field, used in many request messages with an SGL
+*
+****************************************************************************/
+
+/*values for MPI SGL Data Location Address Space subfield */
+#define MPI2_SGLFLAGS_ADDRESS_SPACE_MASK (0x0C)
+#define MPI2_SGLFLAGS_SYSTEM_ADDRESS_SPACE (0x00)
+#define MPI2_SGLFLAGS_IOCDDR_ADDRESS_SPACE (0x04)
+#define MPI2_SGLFLAGS_IOCPLB_ADDRESS_SPACE (0x08)
+#define MPI2_SGLFLAGS_IOCPLBNTA_ADDRESS_SPACE (0x0C)
+/*values for SGL Type subfield */
+#define MPI2_SGLFLAGS_SGL_TYPE_MASK (0x03)
+#define MPI2_SGLFLAGS_SGL_TYPE_MPI (0x00)
+#define MPI2_SGLFLAGS_SGL_TYPE_IEEE32 (0x01)
+#define MPI2_SGLFLAGS_SGL_TYPE_IEEE64 (0x02)
+
+#endif
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h
new file mode 100644
index 00000000000..d8b2c3eedb5
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h
@@ -0,0 +1,3323 @@
+/*
+ * Copyright (c) 2000-2011 LSI Corporation.
+ *
+ *
+ * Name: mpi2_cnfg.h
+ * Title: MPI Configuration messages and pages
+ * Creation Date: November 10, 2006
+ *
+ * mpi2_cnfg.h Version: 02.00.22
+ *
+ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25
+ * prefix are for use only on MPI v2.5 products, and must not be used
+ * with MPI v2.0 products. Unless otherwise noted, names beginning with
+ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products.
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 06-04-07 02.00.01 Added defines for SAS IO Unit Page 2 PhyFlags.
+ * Added Manufacturing Page 11.
+ * Added MPI2_SAS_EXPANDER0_FLAGS_CONNECTOR_END_DEVICE
+ * define.
+ * 06-26-07 02.00.02 Adding generic structure for product-specific
+ * Manufacturing pages: MPI2_CONFIG_PAGE_MANUFACTURING_PS.
+ * Rework of BIOS Page 2 configuration page.
+ * Fixed MPI2_BIOSPAGE2_BOOT_DEVICE to be a union of the
+ * forms.
+ * Added configuration pages IOC Page 8 and Driver
+ * Persistent Mapping Page 0.
+ * 08-31-07 02.00.03 Modified configuration pages dealing with Integrated
+ * RAID (Manufacturing Page 4, RAID Volume Pages 0 and 1,
+ * RAID Physical Disk Pages 0 and 1, RAID Configuration
+ * Page 0).
+ * Added new value for AccessStatus field of SAS Device
+ * Page 0 (_SATA_NEEDS_INITIALIZATION).
+ * 10-31-07 02.00.04 Added missing SEPDevHandle field to
+ * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0.
+ * 12-18-07 02.00.05 Modified IO Unit Page 0 to use 32-bit version fields for
+ * NVDATA.
+ * Modified IOC Page 7 to use masks and added field for
+ * SASBroadcastPrimitiveMasks.
+ * Added MPI2_CONFIG_PAGE_BIOS_4.
+ * Added MPI2_CONFIG_PAGE_LOG_0.
+ * 02-29-08 02.00.06 Modified various names to make them 32-character unique.
+ * Added SAS Device IDs.
+ * Updated Integrated RAID configuration pages including
+ * Manufacturing Page 4, IOC Page 6, and RAID Configuration
+ * Page 0.
+ * 05-21-08 02.00.07 Added define MPI2_MANPAGE4_MIX_SSD_SAS_SATA.
+ * Added define MPI2_MANPAGE4_PHYSDISK_128MB_COERCION.
+ * Fixed define MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING.
+ * Added missing MaxNumRoutedSasAddresses field to
+ * MPI2_CONFIG_PAGE_EXPANDER_0.
+ * Added SAS Port Page 0.
+ * Modified structure layout for
+ * MPI2_CONFIG_PAGE_DRIVER_MAPPING_0.
+ * 06-27-08 02.00.08 Changed MPI2_CONFIG_PAGE_RD_PDISK_1 to use
+ * MPI2_RAID_PHYS_DISK1_PATH_MAX to size the array.
+ * 10-02-08 02.00.09 Changed MPI2_RAID_PGAD_CONFIGNUM_MASK from 0x0000FFFF
+ * to 0x000000FF.
+ * Added two new values for the Physical Disk Coercion Size
+ * bits in the Flags field of Manufacturing Page 4.
+ * Added product-specific Manufacturing pages 16 to 31.
+ * Modified Flags bits for controlling write cache on SATA
+ * drives in IO Unit Page 1.
+ * Added new bit to AdditionalControlFlags of SAS IO Unit
+ * Page 1 to control Invalid Topology Correction.
+ * Added additional defines for RAID Volume Page 0
+ * VolumeStatusFlags field.
+ * Modified meaning of RAID Volume Page 0 VolumeSettings
+ * define for auto-configure of hot-swap drives.
+ * Added SupportedPhysDisks field to RAID Volume Page 1 and
+ * added related defines.
+ * Added PhysDiskAttributes field (and related defines) to
+ * RAID Physical Disk Page 0.
+ * Added MPI2_SAS_PHYINFO_PHY_VACANT define.
+ * Added three new DiscoveryStatus bits for SAS IO Unit
+ * Page 0 and SAS Expander Page 0.
+ * Removed multiplexing information from SAS IO Unit pages.
+ * Added BootDeviceWaitTime field to SAS IO Unit Page 4.
+ * Removed Zone Address Resolved bit from PhyInfo and from
+ * Expander Page 0 Flags field.
+ * Added two new AccessStatus values to SAS Device Page 0
+ * for indicating routing problems. Added 3 reserved words
+ * to this page.
+ * 01-19-09 02.00.10 Fixed defines for GPIOVal field of IO Unit Page 3.
+ * Inserted missing reserved field into structure for IOC
+ * Page 6.
+ * Added more pending task bits to RAID Volume Page 0
+ * VolumeStatusFlags defines.
+ * Added MPI2_PHYSDISK0_STATUS_FLAG_NOT_CERTIFIED define.
+ * Added a new DiscoveryStatus bit for SAS IO Unit Page 0
+ * and SAS Expander Page 0 to flag a downstream initiator
+ * when in simplified routing mode.
+ * Removed SATA Init Failure defines for DiscoveryStatus
+ * fields of SAS IO Unit Page 0 and SAS Expander Page 0.
+ * Added MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED define.
+ * Added PortGroups, DmaGroup, and ControlGroup fields to
+ * SAS Device Page 0.
+ * 05-06-09 02.00.11 Added structures and defines for IO Unit Page 5 and IO
+ * Unit Page 6.
+ * Added expander reduced functionality data to SAS
+ * Expander Page 0.
+ * Added SAS PHY Page 2 and SAS PHY Page 3.
+ * 07-30-09 02.00.12 Added IO Unit Page 7.
+ * Added new device ids.
+ * Added SAS IO Unit Page 5.
+ * Added partial and slumber power management capable flags
+ * to SAS Device Page 0 Flags field.
+ * Added PhyInfo defines for power condition.
+ * Added Ethernet configuration pages.
+ * 10-28-09 02.00.13 Added MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY.
+ * Added SAS PHY Page 4 structure and defines.
+ * 02-10-10 02.00.14 Modified the comments for the configuration page
+ * structures that contain an array of data. The host
+ * should use the "count" field in the page data (e.g. the
+ * NumPhys field) to determine the number of valid elements
+ * in the array.
+ * Added/modified some MPI2_MFGPAGE_DEVID_SAS defines.
+ * Added PowerManagementCapabilities to IO Unit Page 7.
+ * Added PortWidthModGroup field to
+ * MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS.
+ * Added MPI2_CONFIG_PAGE_SASIOUNIT_6 and related defines.
+ * Added MPI2_CONFIG_PAGE_SASIOUNIT_7 and related defines.
+ * Added MPI2_CONFIG_PAGE_SASIOUNIT_8 and related defines.
+ * 05-12-10 02.00.15 Added MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT
+ * define.
+ * Added MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE define.
+ * Added MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY define.
+ * 08-11-10 02.00.16 Removed IO Unit Page 1 device path (multi-pathing)
+ * defines.
+ * 11-10-10 02.00.17 Added ReceptacleID field (replacing Reserved1) to
+ * MPI2_MANPAGE7_CONNECTOR_INFO and reworked defines for
+ * the Pinout field.
+ * Added BoardTemperature and BoardTemperatureUnits fields
+ * to MPI2_CONFIG_PAGE_IO_UNIT_7.
+ * Added MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING define
+ * and MPI2_CONFIG_PAGE_EXT_MAN_PS structure.
+ * 02-23-11 02.00.18 Added ProxyVF_ID field to MPI2_CONFIG_REQUEST.
+ * Added IO Unit Page 8, IO Unit Page 9,
+ * and IO Unit Page 10.
+ * Added SASNotifyPrimitiveMasks field to
+ * MPI2_CONFIG_PAGE_IOC_7.
+ * 03-09-11 02.00.19 Fixed IO Unit Page 10 (to match the spec).
+ * 05-25-11 02.00.20 Cleaned up a few comments.
+ * 08-24-11 02.00.21 Marked the IO Unit Page 7 PowerManagementCapabilities
+ * for PCIe link as obsolete.
+ * Added SpinupFlags field containing a Disable Spin-up bit
+ * to the MPI2_SAS_IOUNIT4_SPINUP_GROUP fields of SAS IO
+ * Unit Page 4.
+ * 11-18-11 02.00.22 Added define MPI2_IOCPAGE6_CAP_FLAGS_4K_SECTORS_SUPPORT.
+ * Added UEFIVersion field to BIOS Page 1 and defined new
+ * BiosOptions bits.
+ * Incorporating additions for MPI v2.5.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_CNFG_H
+#define MPI2_CNFG_H
+
+/*****************************************************************************
+* Configuration Page Header and defines
+*****************************************************************************/
+
+/*Config Page Header */
+typedef struct _MPI2_CONFIG_PAGE_HEADER {
+ U8 PageVersion; /*0x00 */
+ U8 PageLength; /*0x01 */
+ U8 PageNumber; /*0x02 */
+ U8 PageType; /*0x03 */
+} MPI2_CONFIG_PAGE_HEADER, *PTR_MPI2_CONFIG_PAGE_HEADER,
+ Mpi2ConfigPageHeader_t, *pMpi2ConfigPageHeader_t;
+
+typedef union _MPI2_CONFIG_PAGE_HEADER_UNION {
+ MPI2_CONFIG_PAGE_HEADER Struct;
+ U8 Bytes[4];
+ U16 Word16[2];
+ U32 Word32;
+} MPI2_CONFIG_PAGE_HEADER_UNION, *PTR_MPI2_CONFIG_PAGE_HEADER_UNION,
+ Mpi2ConfigPageHeaderUnion, *pMpi2ConfigPageHeaderUnion;
+
+/*Extended Config Page Header */
+typedef struct _MPI2_CONFIG_EXTENDED_PAGE_HEADER {
+ U8 PageVersion; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 PageNumber; /*0x02 */
+ U8 PageType; /*0x03 */
+ U16 ExtPageLength; /*0x04 */
+ U8 ExtPageType; /*0x06 */
+ U8 Reserved2; /*0x07 */
+} MPI2_CONFIG_EXTENDED_PAGE_HEADER,
+ *PTR_MPI2_CONFIG_EXTENDED_PAGE_HEADER,
+ Mpi2ConfigExtendedPageHeader_t,
+ *pMpi2ConfigExtendedPageHeader_t;
+
+typedef union _MPI2_CONFIG_EXT_PAGE_HEADER_UNION {
+ MPI2_CONFIG_PAGE_HEADER Struct;
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Ext;
+ U8 Bytes[8];
+ U16 Word16[4];
+ U32 Word32[2];
+} MPI2_CONFIG_EXT_PAGE_HEADER_UNION,
+ *PTR_MPI2_CONFIG_EXT_PAGE_HEADER_UNION,
+ Mpi2ConfigPageExtendedHeaderUnion,
+ *pMpi2ConfigPageExtendedHeaderUnion;
+
+
+/*PageType field values */
+#define MPI2_CONFIG_PAGEATTR_READ_ONLY (0x00)
+#define MPI2_CONFIG_PAGEATTR_CHANGEABLE (0x10)
+#define MPI2_CONFIG_PAGEATTR_PERSISTENT (0x20)
+#define MPI2_CONFIG_PAGEATTR_MASK (0xF0)
+
+#define MPI2_CONFIG_PAGETYPE_IO_UNIT (0x00)
+#define MPI2_CONFIG_PAGETYPE_IOC (0x01)
+#define MPI2_CONFIG_PAGETYPE_BIOS (0x02)
+#define MPI2_CONFIG_PAGETYPE_RAID_VOLUME (0x08)
+#define MPI2_CONFIG_PAGETYPE_MANUFACTURING (0x09)
+#define MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK (0x0A)
+#define MPI2_CONFIG_PAGETYPE_EXTENDED (0x0F)
+#define MPI2_CONFIG_PAGETYPE_MASK (0x0F)
+
+#define MPI2_CONFIG_TYPENUM_MASK (0x0FFF)
+
+
+/*ExtPageType field values */
+#define MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT (0x10)
+#define MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER (0x11)
+#define MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE (0x12)
+#define MPI2_CONFIG_EXTPAGETYPE_SAS_PHY (0x13)
+#define MPI2_CONFIG_EXTPAGETYPE_LOG (0x14)
+#define MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE (0x15)
+#define MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG (0x16)
+#define MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING (0x17)
+#define MPI2_CONFIG_EXTPAGETYPE_SAS_PORT (0x18)
+#define MPI2_CONFIG_EXTPAGETYPE_ETHERNET (0x19)
+#define MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING (0x1A)
+
+
+/*****************************************************************************
+* PageAddress defines
+*****************************************************************************/
+
+/*RAID Volume PageAddress format */
+#define MPI2_RAID_VOLUME_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
+#define MPI2_RAID_VOLUME_PGAD_FORM_HANDLE (0x10000000)
+
+#define MPI2_RAID_VOLUME_PGAD_HANDLE_MASK (0x0000FFFF)
+
+
+/*RAID Physical Disk PageAddress format */
+#define MPI2_PHYSDISK_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM (0x00000000)
+#define MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM (0x10000000)
+#define MPI2_PHYSDISK_PGAD_FORM_DEVHANDLE (0x20000000)
+
+#define MPI2_PHYSDISK_PGAD_PHYSDISKNUM_MASK (0x000000FF)
+#define MPI2_PHYSDISK_PGAD_DEVHANDLE_MASK (0x0000FFFF)
+
+
+/*SAS Expander PageAddress format */
+#define MPI2_SAS_EXPAND_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL (0x00000000)
+#define MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM (0x10000000)
+#define MPI2_SAS_EXPAND_PGAD_FORM_HNDL (0x20000000)
+
+#define MPI2_SAS_EXPAND_PGAD_HANDLE_MASK (0x0000FFFF)
+#define MPI2_SAS_EXPAND_PGAD_PHYNUM_MASK (0x00FF0000)
+#define MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT (16)
+
+
+/*SAS Device PageAddress format */
+#define MPI2_SAS_DEVICE_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
+#define MPI2_SAS_DEVICE_PGAD_FORM_HANDLE (0x20000000)
+
+#define MPI2_SAS_DEVICE_PGAD_HANDLE_MASK (0x0000FFFF)
+
+
+/*SAS PHY PageAddress format */
+#define MPI2_SAS_PHY_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER (0x00000000)
+#define MPI2_SAS_PHY_PGAD_FORM_PHY_TBL_INDEX (0x10000000)
+
+#define MPI2_SAS_PHY_PGAD_PHY_NUMBER_MASK (0x000000FF)
+#define MPI2_SAS_PHY_PGAD_PHY_TBL_INDEX_MASK (0x0000FFFF)
+
+
+/*SAS Port PageAddress format */
+#define MPI2_SASPORT_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_SASPORT_PGAD_FORM_GET_NEXT_PORT (0x00000000)
+#define MPI2_SASPORT_PGAD_FORM_PORT_NUM (0x10000000)
+
+#define MPI2_SASPORT_PGAD_PORTNUMBER_MASK (0x00000FFF)
+
+
+/*SAS Enclosure PageAddress format */
+#define MPI2_SAS_ENCLOS_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
+#define MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE (0x10000000)
+
+#define MPI2_SAS_ENCLOS_PGAD_HANDLE_MASK (0x0000FFFF)
+
+
+/*RAID Configuration PageAddress format */
+#define MPI2_RAID_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_RAID_PGAD_FORM_GET_NEXT_CONFIGNUM (0x00000000)
+#define MPI2_RAID_PGAD_FORM_CONFIGNUM (0x10000000)
+#define MPI2_RAID_PGAD_FORM_ACTIVE_CONFIG (0x20000000)
+
+#define MPI2_RAID_PGAD_CONFIGNUM_MASK (0x000000FF)
+
+
+/*Driver Persistent Mapping PageAddress format */
+#define MPI2_DPM_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_DPM_PGAD_FORM_ENTRY_RANGE (0x00000000)
+
+#define MPI2_DPM_PGAD_ENTRY_COUNT_MASK (0x0FFF0000)
+#define MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT (16)
+#define MPI2_DPM_PGAD_START_ENTRY_MASK (0x0000FFFF)
+
+
+/*Ethernet PageAddress format */
+#define MPI2_ETHERNET_PGAD_FORM_MASK (0xF0000000)
+#define MPI2_ETHERNET_PGAD_FORM_IF_NUM (0x00000000)
+
+#define MPI2_ETHERNET_PGAD_IF_NUMBER_MASK (0x000000FF)
+
+
+
+/****************************************************************************
+* Configuration messages
+****************************************************************************/
+
+/*Configuration Request Message */
+typedef struct _MPI2_CONFIG_REQUEST {
+ U8 Action; /*0x00 */
+ U8 SGLFlags; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 ExtPageLength; /*0x04 */
+ U8 ExtPageType; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U8 Reserved2; /*0x0C */
+ U8 ProxyVF_ID; /*0x0D */
+ U16 Reserved4; /*0x0E */
+ U32 Reserved3; /*0x10 */
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x14 */
+ U32 PageAddress; /*0x18 */
+ MPI2_SGE_IO_UNION PageBufferSGE; /*0x1C */
+} MPI2_CONFIG_REQUEST, *PTR_MPI2_CONFIG_REQUEST,
+ Mpi2ConfigRequest_t, *pMpi2ConfigRequest_t;
+
+/*values for the Action field */
+#define MPI2_CONFIG_ACTION_PAGE_HEADER (0x00)
+#define MPI2_CONFIG_ACTION_PAGE_READ_CURRENT (0x01)
+#define MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT (0x02)
+#define MPI2_CONFIG_ACTION_PAGE_DEFAULT (0x03)
+#define MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM (0x04)
+#define MPI2_CONFIG_ACTION_PAGE_READ_DEFAULT (0x05)
+#define MPI2_CONFIG_ACTION_PAGE_READ_NVRAM (0x06)
+#define MPI2_CONFIG_ACTION_PAGE_GET_CHANGEABLE (0x07)
+
+/*use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
+
+
+/*Config Reply Message */
+typedef struct _MPI2_CONFIG_REPLY {
+ U8 Action; /*0x00 */
+ U8 SGLFlags; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 ExtPageLength; /*0x04 */
+ U8 ExtPageType; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U16 Reserved2; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x14 */
+} MPI2_CONFIG_REPLY, *PTR_MPI2_CONFIG_REPLY,
+ Mpi2ConfigReply_t, *pMpi2ConfigReply_t;
+
+
+
+/*****************************************************************************
+*
+* C o n f i g u r a t i o n P a g e s
+*
+*****************************************************************************/
+
+/****************************************************************************
+* Manufacturing Config pages
+****************************************************************************/
+
+#define MPI2_MFGPAGE_VENDORID_LSI (0x1000)
+
+/*MPI v2.0 SAS products */
+#define MPI2_MFGPAGE_DEVID_SAS2004 (0x0070)
+#define MPI2_MFGPAGE_DEVID_SAS2008 (0x0072)
+#define MPI2_MFGPAGE_DEVID_SAS2108_1 (0x0074)
+#define MPI2_MFGPAGE_DEVID_SAS2108_2 (0x0076)
+#define MPI2_MFGPAGE_DEVID_SAS2108_3 (0x0077)
+#define MPI2_MFGPAGE_DEVID_SAS2116_1 (0x0064)
+#define MPI2_MFGPAGE_DEVID_SAS2116_2 (0x0065)
+
+#define MPI2_MFGPAGE_DEVID_SSS6200 (0x007E)
+
+#define MPI2_MFGPAGE_DEVID_SAS2208_1 (0x0080)
+#define MPI2_MFGPAGE_DEVID_SAS2208_2 (0x0081)
+#define MPI2_MFGPAGE_DEVID_SAS2208_3 (0x0082)
+#define MPI2_MFGPAGE_DEVID_SAS2208_4 (0x0083)
+#define MPI2_MFGPAGE_DEVID_SAS2208_5 (0x0084)
+#define MPI2_MFGPAGE_DEVID_SAS2208_6 (0x0085)
+#define MPI2_MFGPAGE_DEVID_SAS2308_1 (0x0086)
+#define MPI2_MFGPAGE_DEVID_SAS2308_2 (0x0087)
+#define MPI2_MFGPAGE_DEVID_SAS2308_3 (0x006E)
+
+/*MPI v2.5 SAS products */
+#define MPI25_MFGPAGE_DEVID_SAS3004 (0x0096)
+#define MPI25_MFGPAGE_DEVID_SAS3008 (0x0097)
+#define MPI25_MFGPAGE_DEVID_SAS3108_1 (0x0090)
+#define MPI25_MFGPAGE_DEVID_SAS3108_2 (0x0091)
+#define MPI25_MFGPAGE_DEVID_SAS3108_5 (0x0094)
+#define MPI25_MFGPAGE_DEVID_SAS3108_6 (0x0095)
+
+
+
+
+/*Manufacturing Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_0 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U8 ChipName[16]; /*0x04 */
+ U8 ChipRevision[8]; /*0x14 */
+ U8 BoardName[16]; /*0x1C */
+ U8 BoardAssembly[16]; /*0x2C */
+ U8 BoardTracerNumber[16]; /*0x3C */
+} MPI2_CONFIG_PAGE_MAN_0,
+ *PTR_MPI2_CONFIG_PAGE_MAN_0,
+ Mpi2ManufacturingPage0_t,
+ *pMpi2ManufacturingPage0_t;
+
+#define MPI2_MANUFACTURING0_PAGEVERSION (0x00)
+
+
+/*Manufacturing Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_1 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U8 VPD[256]; /*0x04 */
+} MPI2_CONFIG_PAGE_MAN_1,
+ *PTR_MPI2_CONFIG_PAGE_MAN_1,
+ Mpi2ManufacturingPage1_t,
+ *pMpi2ManufacturingPage1_t;
+
+#define MPI2_MANUFACTURING1_PAGEVERSION (0x00)
+
+
+typedef struct _MPI2_CHIP_REVISION_ID {
+ U16 DeviceID; /*0x00 */
+ U8 PCIRevisionID; /*0x02 */
+ U8 Reserved; /*0x03 */
+} MPI2_CHIP_REVISION_ID, *PTR_MPI2_CHIP_REVISION_ID,
+ Mpi2ChipRevisionId_t, *pMpi2ChipRevisionId_t;
+
+
+/*Manufacturing Page 2 */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check Header.PageLength at runtime.
+ */
+#ifndef MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS
+#define MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_2 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ MPI2_CHIP_REVISION_ID ChipId; /*0x04 */
+ U32
+ HwSettings[MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS];/*0x08 */
+} MPI2_CONFIG_PAGE_MAN_2,
+ *PTR_MPI2_CONFIG_PAGE_MAN_2,
+ Mpi2ManufacturingPage2_t,
+ *pMpi2ManufacturingPage2_t;
+
+#define MPI2_MANUFACTURING2_PAGEVERSION (0x00)
+
+
+/*Manufacturing Page 3 */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check Header.PageLength at runtime.
+ */
+#ifndef MPI2_MAN_PAGE_3_INFO_WORDS
+#define MPI2_MAN_PAGE_3_INFO_WORDS (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_3 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ MPI2_CHIP_REVISION_ID ChipId; /*0x04 */
+ U32
+ Info[MPI2_MAN_PAGE_3_INFO_WORDS];/*0x08 */
+} MPI2_CONFIG_PAGE_MAN_3,
+ *PTR_MPI2_CONFIG_PAGE_MAN_3,
+ Mpi2ManufacturingPage3_t,
+ *pMpi2ManufacturingPage3_t;
+
+#define MPI2_MANUFACTURING3_PAGEVERSION (0x00)
+
+
+/*Manufacturing Page 4 */
+
+typedef struct _MPI2_MANPAGE4_PWR_SAVE_SETTINGS {
+ U8 PowerSaveFlags; /*0x00 */
+ U8 InternalOperationsSleepTime; /*0x01 */
+ U8 InternalOperationsRunTime; /*0x02 */
+ U8 HostIdleTime; /*0x03 */
+} MPI2_MANPAGE4_PWR_SAVE_SETTINGS,
+ *PTR_MPI2_MANPAGE4_PWR_SAVE_SETTINGS,
+ Mpi2ManPage4PwrSaveSettings_t,
+ *pMpi2ManPage4PwrSaveSettings_t;
+
+/*defines for the PowerSaveFlags field */
+#define MPI2_MANPAGE4_MASK_POWERSAVE_MODE (0x03)
+#define MPI2_MANPAGE4_POWERSAVE_MODE_DISABLED (0x00)
+#define MPI2_MANPAGE4_CUSTOM_POWERSAVE_MODE (0x01)
+#define MPI2_MANPAGE4_FULL_POWERSAVE_MODE (0x02)
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_4 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x04 */
+ U32 Flags; /*0x08 */
+ U8 InquirySize; /*0x0C */
+ U8 Reserved2; /*0x0D */
+ U16 Reserved3; /*0x0E */
+ U8 InquiryData[56]; /*0x10 */
+ U32 RAID0VolumeSettings; /*0x48 */
+ U32 RAID1EVolumeSettings; /*0x4C */
+ U32 RAID1VolumeSettings; /*0x50 */
+ U32 RAID10VolumeSettings; /*0x54 */
+ U32 Reserved4; /*0x58 */
+ U32 Reserved5; /*0x5C */
+ MPI2_MANPAGE4_PWR_SAVE_SETTINGS PowerSaveSettings; /*0x60 */
+ U8 MaxOCEDisks; /*0x64 */
+ U8 ResyncRate; /*0x65 */
+ U16 DataScrubDuration; /*0x66 */
+ U8 MaxHotSpares; /*0x68 */
+ U8 MaxPhysDisksPerVol; /*0x69 */
+ U8 MaxPhysDisks; /*0x6A */
+ U8 MaxVolumes; /*0x6B */
+} MPI2_CONFIG_PAGE_MAN_4,
+ *PTR_MPI2_CONFIG_PAGE_MAN_4,
+ Mpi2ManufacturingPage4_t,
+ *pMpi2ManufacturingPage4_t;
+
+#define MPI2_MANUFACTURING4_PAGEVERSION (0x0A)
+
+/*Manufacturing Page 4 Flags field */
+#define MPI2_MANPAGE4_METADATA_SIZE_MASK (0x00030000)
+#define MPI2_MANPAGE4_METADATA_512MB (0x00000000)
+
+#define MPI2_MANPAGE4_MIX_SSD_SAS_SATA (0x00008000)
+#define MPI2_MANPAGE4_MIX_SSD_AND_NON_SSD (0x00004000)
+#define MPI2_MANPAGE4_HIDE_PHYSDISK_NON_IR (0x00002000)
+
+#define MPI2_MANPAGE4_MASK_PHYSDISK_COERCION (0x00001C00)
+#define MPI2_MANPAGE4_PHYSDISK_COERCION_1GB (0x00000000)
+#define MPI2_MANPAGE4_PHYSDISK_128MB_COERCION (0x00000400)
+#define MPI2_MANPAGE4_PHYSDISK_ADAPTIVE_COERCION (0x00000800)
+#define MPI2_MANPAGE4_PHYSDISK_ZERO_COERCION (0x00000C00)
+
+#define MPI2_MANPAGE4_MASK_BAD_BLOCK_MARKING (0x00000300)
+#define MPI2_MANPAGE4_DEFAULT_BAD_BLOCK_MARKING (0x00000000)
+#define MPI2_MANPAGE4_TABLE_BAD_BLOCK_MARKING (0x00000100)
+#define MPI2_MANPAGE4_WRITE_LONG_BAD_BLOCK_MARKING (0x00000200)
+
+#define MPI2_MANPAGE4_FORCE_OFFLINE_FAILOVER (0x00000080)
+#define MPI2_MANPAGE4_RAID10_DISABLE (0x00000040)
+#define MPI2_MANPAGE4_RAID1E_DISABLE (0x00000020)
+#define MPI2_MANPAGE4_RAID1_DISABLE (0x00000010)
+#define MPI2_MANPAGE4_RAID0_DISABLE (0x00000008)
+#define MPI2_MANPAGE4_IR_MODEPAGE8_DISABLE (0x00000004)
+#define MPI2_MANPAGE4_IM_RESYNC_CACHE_ENABLE (0x00000002)
+#define MPI2_MANPAGE4_IR_NO_MIX_SAS_SATA (0x00000001)
+
+
+/*Manufacturing Page 5 */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumPhys at runtime.
+ */
+#ifndef MPI2_MAN_PAGE_5_PHY_ENTRIES
+#define MPI2_MAN_PAGE_5_PHY_ENTRIES (1)
+#endif
+
+typedef struct _MPI2_MANUFACTURING5_ENTRY {
+ U64 WWID; /*0x00 */
+ U64 DeviceName; /*0x08 */
+} MPI2_MANUFACTURING5_ENTRY,
+ *PTR_MPI2_MANUFACTURING5_ENTRY,
+ Mpi2Manufacturing5Entry_t,
+ *pMpi2Manufacturing5Entry_t;
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_5 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U8 NumPhys; /*0x04 */
+ U8 Reserved1; /*0x05 */
+ U16 Reserved2; /*0x06 */
+ U32 Reserved3; /*0x08 */
+ U32 Reserved4; /*0x0C */
+ MPI2_MANUFACTURING5_ENTRY
+ Phy[MPI2_MAN_PAGE_5_PHY_ENTRIES];/*0x08 */
+} MPI2_CONFIG_PAGE_MAN_5,
+ *PTR_MPI2_CONFIG_PAGE_MAN_5,
+ Mpi2ManufacturingPage5_t,
+ *pMpi2ManufacturingPage5_t;
+
+#define MPI2_MANUFACTURING5_PAGEVERSION (0x03)
+
+
+/*Manufacturing Page 6 */
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_6 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 ProductSpecificInfo;/*0x04 */
+} MPI2_CONFIG_PAGE_MAN_6,
+ *PTR_MPI2_CONFIG_PAGE_MAN_6,
+ Mpi2ManufacturingPage6_t,
+ *pMpi2ManufacturingPage6_t;
+
+#define MPI2_MANUFACTURING6_PAGEVERSION (0x00)
+
+
+/*Manufacturing Page 7 */
+
+typedef struct _MPI2_MANPAGE7_CONNECTOR_INFO {
+ U32 Pinout; /*0x00 */
+ U8 Connector[16]; /*0x04 */
+ U8 Location; /*0x14 */
+ U8 ReceptacleID; /*0x15 */
+ U16 Slot; /*0x16 */
+ U32 Reserved2; /*0x18 */
+} MPI2_MANPAGE7_CONNECTOR_INFO,
+ *PTR_MPI2_MANPAGE7_CONNECTOR_INFO,
+ Mpi2ManPage7ConnectorInfo_t,
+ *pMpi2ManPage7ConnectorInfo_t;
+
+/*defines for the Pinout field */
+#define MPI2_MANPAGE7_PINOUT_LANE_MASK (0x0000FF00)
+#define MPI2_MANPAGE7_PINOUT_LANE_SHIFT (8)
+
+#define MPI2_MANPAGE7_PINOUT_TYPE_MASK (0x000000FF)
+#define MPI2_MANPAGE7_PINOUT_TYPE_UNKNOWN (0x00)
+#define MPI2_MANPAGE7_PINOUT_SATA_SINGLE (0x01)
+#define MPI2_MANPAGE7_PINOUT_SFF_8482 (0x02)
+#define MPI2_MANPAGE7_PINOUT_SFF_8486 (0x03)
+#define MPI2_MANPAGE7_PINOUT_SFF_8484 (0x04)
+#define MPI2_MANPAGE7_PINOUT_SFF_8087 (0x05)
+#define MPI2_MANPAGE7_PINOUT_SFF_8643_4I (0x06)
+#define MPI2_MANPAGE7_PINOUT_SFF_8643_8I (0x07)
+#define MPI2_MANPAGE7_PINOUT_SFF_8470 (0x08)
+#define MPI2_MANPAGE7_PINOUT_SFF_8088 (0x09)
+#define MPI2_MANPAGE7_PINOUT_SFF_8644_4X (0x0A)
+#define MPI2_MANPAGE7_PINOUT_SFF_8644_8X (0x0B)
+#define MPI2_MANPAGE7_PINOUT_SFF_8644_16X (0x0C)
+#define MPI2_MANPAGE7_PINOUT_SFF_8436 (0x0D)
+
+/*defines for the Location field */
+#define MPI2_MANPAGE7_LOCATION_UNKNOWN (0x01)
+#define MPI2_MANPAGE7_LOCATION_INTERNAL (0x02)
+#define MPI2_MANPAGE7_LOCATION_EXTERNAL (0x04)
+#define MPI2_MANPAGE7_LOCATION_SWITCHABLE (0x08)
+#define MPI2_MANPAGE7_LOCATION_AUTO (0x10)
+#define MPI2_MANPAGE7_LOCATION_NOT_PRESENT (0x20)
+#define MPI2_MANPAGE7_LOCATION_NOT_CONNECTED (0x80)
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumPhys at runtime.
+ */
+#ifndef MPI2_MANPAGE7_CONNECTOR_INFO_MAX
+#define MPI2_MANPAGE7_CONNECTOR_INFO_MAX (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_7 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x04 */
+ U32 Reserved2; /*0x08 */
+ U32 Flags; /*0x0C */
+ U8 EnclosureName[16]; /*0x10 */
+ U8 NumPhys; /*0x20 */
+ U8 Reserved3; /*0x21 */
+ U16 Reserved4; /*0x22 */
+ MPI2_MANPAGE7_CONNECTOR_INFO
+ ConnectorInfo[MPI2_MANPAGE7_CONNECTOR_INFO_MAX]; /*0x24 */
+} MPI2_CONFIG_PAGE_MAN_7,
+ *PTR_MPI2_CONFIG_PAGE_MAN_7,
+ Mpi2ManufacturingPage7_t,
+ *pMpi2ManufacturingPage7_t;
+
+#define MPI2_MANUFACTURING7_PAGEVERSION (0x01)
+
+/*defines for the Flags field */
+#define MPI2_MANPAGE7_FLAG_USE_SLOT_INFO (0x00000001)
+
+
+/*
+ *Generic structure to use for product-specific manufacturing pages
+ *(currently Manufacturing Page 8 through Manufacturing Page 31).
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_MAN_PS {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 ProductSpecificInfo;/*0x04 */
+} MPI2_CONFIG_PAGE_MAN_PS,
+ *PTR_MPI2_CONFIG_PAGE_MAN_PS,
+ Mpi2ManufacturingPagePS_t,
+ *pMpi2ManufacturingPagePS_t;
+
+#define MPI2_MANUFACTURING8_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING9_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING10_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING11_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING12_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING13_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING14_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING15_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING16_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING17_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING18_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING19_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING20_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING21_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING22_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING23_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING24_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING25_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING26_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING27_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING28_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING29_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING30_PAGEVERSION (0x00)
+#define MPI2_MANUFACTURING31_PAGEVERSION (0x00)
+
+
+/****************************************************************************
+* IO Unit Config Pages
+****************************************************************************/
+
+/*IO Unit Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_0 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U64 UniqueValue; /*0x04 */
+ MPI2_VERSION_UNION NvdataVersionDefault; /*0x08 */
+ MPI2_VERSION_UNION NvdataVersionPersistent; /*0x0A */
+} MPI2_CONFIG_PAGE_IO_UNIT_0,
+ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_0,
+ Mpi2IOUnitPage0_t, *pMpi2IOUnitPage0_t;
+
+#define MPI2_IOUNITPAGE0_PAGEVERSION (0x02)
+
+
+/*IO Unit Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_1 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 Flags; /*0x04 */
+} MPI2_CONFIG_PAGE_IO_UNIT_1,
+ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_1,
+ Mpi2IOUnitPage1_t, *pMpi2IOUnitPage1_t;
+
+#define MPI2_IOUNITPAGE1_PAGEVERSION (0x04)
+
+/*IO Unit Page 1 Flags defines */
+#define MPI25_IOUNITPAGE1_NEW_DEVICE_FAST_PATH_DISABLE (0x00002000)
+#define MPI25_IOUNITPAGE1_DISABLE_FAST_PATH (0x00001000)
+#define MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY (0x00000800)
+#define MPI2_IOUNITPAGE1_MASK_SATA_WRITE_CACHE (0x00000600)
+#define MPI2_IOUNITPAGE1_SATA_WRITE_CACHE_SHIFT (9)
+#define MPI2_IOUNITPAGE1_ENABLE_SATA_WRITE_CACHE (0x00000000)
+#define MPI2_IOUNITPAGE1_DISABLE_SATA_WRITE_CACHE (0x00000200)
+#define MPI2_IOUNITPAGE1_UNCHANGED_SATA_WRITE_CACHE (0x00000400)
+#define MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE (0x00000100)
+#define MPI2_IOUNITPAGE1_DISABLE_IR (0x00000040)
+#define MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING (0x00000020)
+#define MPI2_IOUNITPAGE1_IR_USE_STATIC_VOLUME_ID (0x00000004)
+
+
+/*IO Unit Page 3 */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for GPIOCount at runtime.
+ */
+#ifndef MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX
+#define MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_3 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U8 GPIOCount; /*0x04 */
+ U8 Reserved1; /*0x05 */
+ U16 Reserved2; /*0x06 */
+ U16
+ GPIOVal[MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX];/*0x08 */
+} MPI2_CONFIG_PAGE_IO_UNIT_3,
+ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_3,
+ Mpi2IOUnitPage3_t, *pMpi2IOUnitPage3_t;
+
+#define MPI2_IOUNITPAGE3_PAGEVERSION (0x01)
+
+/*defines for IO Unit Page 3 GPIOVal field */
+#define MPI2_IOUNITPAGE3_GPIO_FUNCTION_MASK (0xFFFC)
+#define MPI2_IOUNITPAGE3_GPIO_FUNCTION_SHIFT (2)
+#define MPI2_IOUNITPAGE3_GPIO_SETTING_OFF (0x0000)
+#define MPI2_IOUNITPAGE3_GPIO_SETTING_ON (0x0001)
+
+
+/*IO Unit Page 5 */
+
+/*
+ *Upper layer code (drivers, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumDmaEngines at runtime.
+ */
+#ifndef MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES
+#define MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_5 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U64
+ RaidAcceleratorBufferBaseAddress; /*0x04 */
+ U64
+ RaidAcceleratorBufferSize; /*0x0C */
+ U64
+ RaidAcceleratorControlBaseAddress; /*0x14 */
+ U8 RAControlSize; /*0x1C */
+ U8 NumDmaEngines; /*0x1D */
+ U8 RAMinControlSize; /*0x1E */
+ U8 RAMaxControlSize; /*0x1F */
+ U32 Reserved1; /*0x20 */
+ U32 Reserved2; /*0x24 */
+ U32 Reserved3; /*0x28 */
+ U32
+ DmaEngineCapabilities[MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES]; /*0x2C */
+} MPI2_CONFIG_PAGE_IO_UNIT_5,
+ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_5,
+ Mpi2IOUnitPage5_t, *pMpi2IOUnitPage5_t;
+
+#define MPI2_IOUNITPAGE5_PAGEVERSION (0x00)
+
+/*defines for IO Unit Page 5 DmaEngineCapabilities field */
+#define MPI2_IOUNITPAGE5_DMA_CAP_MASK_MAX_REQUESTS (0xFF00)
+#define MPI2_IOUNITPAGE5_DMA_CAP_SHIFT_MAX_REQUESTS (16)
+
+#define MPI2_IOUNITPAGE5_DMA_CAP_EEDP (0x0008)
+#define MPI2_IOUNITPAGE5_DMA_CAP_PARITY_GENERATION (0x0004)
+#define MPI2_IOUNITPAGE5_DMA_CAP_HASHING (0x0002)
+#define MPI2_IOUNITPAGE5_DMA_CAP_ENCRYPTION (0x0001)
+
+
+/*IO Unit Page 6 */
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_6 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U16 Flags; /*0x04 */
+ U8 RAHostControlSize; /*0x06 */
+ U8 Reserved0; /*0x07 */
+ U64
+ RaidAcceleratorHostControlBaseAddress; /*0x08 */
+ U32 Reserved1; /*0x10 */
+ U32 Reserved2; /*0x14 */
+ U32 Reserved3; /*0x18 */
+} MPI2_CONFIG_PAGE_IO_UNIT_6,
+ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_6,
+ Mpi2IOUnitPage6_t, *pMpi2IOUnitPage6_t;
+
+#define MPI2_IOUNITPAGE6_PAGEVERSION (0x00)
+
+/*defines for IO Unit Page 6 Flags field */
+#define MPI2_IOUNITPAGE6_FLAGS_ENABLE_RAID_ACCELERATOR (0x0001)
+
+
+/*IO Unit Page 7 */
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_7 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U8 CurrentPowerMode; /*0x04 */
+ U8 PreviousPowerMode; /*0x05 */
+ U8 PCIeWidth; /*0x06 */
+ U8 PCIeSpeed; /*0x07 */
+ U32 ProcessorState; /*0x08 */
+ U32
+ PowerManagementCapabilities; /*0x0C */
+ U16 IOCTemperature; /*0x10 */
+ U8
+ IOCTemperatureUnits; /*0x12 */
+ U8 IOCSpeed; /*0x13 */
+ U16 BoardTemperature; /*0x14 */
+ U8
+ BoardTemperatureUnits; /*0x16 */
+ U8 Reserved3; /*0x17 */
+} MPI2_CONFIG_PAGE_IO_UNIT_7,
+ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_7,
+ Mpi2IOUnitPage7_t, *pMpi2IOUnitPage7_t;
+
+#define MPI2_IOUNITPAGE7_PAGEVERSION (0x02)
+
+/*defines for IO Unit Page 7 CurrentPowerMode and PreviousPowerMode fields */
+#define MPI25_IOUNITPAGE7_PM_INIT_MASK (0xC0)
+#define MPI25_IOUNITPAGE7_PM_INIT_UNAVAILABLE (0x00)
+#define MPI25_IOUNITPAGE7_PM_INIT_HOST (0x40)
+#define MPI25_IOUNITPAGE7_PM_INIT_IO_UNIT (0x80)
+#define MPI25_IOUNITPAGE7_PM_INIT_PCIE_DPA (0xC0)
+
+#define MPI25_IOUNITPAGE7_PM_MODE_MASK (0x07)
+#define MPI25_IOUNITPAGE7_PM_MODE_UNAVAILABLE (0x00)
+#define MPI25_IOUNITPAGE7_PM_MODE_UNKNOWN (0x01)
+#define MPI25_IOUNITPAGE7_PM_MODE_FULL_POWER (0x04)
+#define MPI25_IOUNITPAGE7_PM_MODE_REDUCED_POWER (0x05)
+#define MPI25_IOUNITPAGE7_PM_MODE_STANDBY (0x06)
+
+
+/*defines for IO Unit Page 7 PCIeWidth field */
+#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X1 (0x01)
+#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X2 (0x02)
+#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X4 (0x04)
+#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X8 (0x08)
+
+/*defines for IO Unit Page 7 PCIeSpeed field */
+#define MPI2_IOUNITPAGE7_PCIE_SPEED_2_5_GBPS (0x00)
+#define MPI2_IOUNITPAGE7_PCIE_SPEED_5_0_GBPS (0x01)
+#define MPI2_IOUNITPAGE7_PCIE_SPEED_8_0_GBPS (0x02)
+
+/*defines for IO Unit Page 7 ProcessorState field */
+#define MPI2_IOUNITPAGE7_PSTATE_MASK_SECOND (0x0000000F)
+#define MPI2_IOUNITPAGE7_PSTATE_SHIFT_SECOND (0)
+
+#define MPI2_IOUNITPAGE7_PSTATE_NOT_PRESENT (0x00)
+#define MPI2_IOUNITPAGE7_PSTATE_DISABLED (0x01)
+#define MPI2_IOUNITPAGE7_PSTATE_ENABLED (0x02)
+
+/*defines for IO Unit Page 7 PowerManagementCapabilities field */
+#define MPI25_IOUNITPAGE7_PMCAP_DPA_FULL_PWR_MODE (0x00400000)
+#define MPI25_IOUNITPAGE7_PMCAP_DPA_REDUCED_PWR_MODE (0x00200000)
+#define MPI25_IOUNITPAGE7_PMCAP_DPA_STANDBY_MODE (0x00100000)
+#define MPI25_IOUNITPAGE7_PMCAP_HOST_FULL_PWR_MODE (0x00040000)
+#define MPI25_IOUNITPAGE7_PMCAP_HOST_REDUCED_PWR_MODE (0x00020000)
+#define MPI25_IOUNITPAGE7_PMCAP_HOST_STANDBY_MODE (0x00010000)
+#define MPI25_IOUNITPAGE7_PMCAP_IO_FULL_PWR_MODE (0x00004000)
+#define MPI25_IOUNITPAGE7_PMCAP_IO_REDUCED_PWR_MODE (0x00002000)
+#define MPI25_IOUNITPAGE7_PMCAP_IO_STANDBY_MODE (0x00001000)
+#define MPI2_IOUNITPAGE7_PMCAP_HOST_12_5_PCT_IOCSPEED (0x00000400)
+#define MPI2_IOUNITPAGE7_PMCAP_HOST_25_0_PCT_IOCSPEED (0x00000200)
+#define MPI2_IOUNITPAGE7_PMCAP_HOST_50_0_PCT_IOCSPEED (0x00000100)
+#define MPI25_IOUNITPAGE7_PMCAP_IO_12_5_PCT_IOCSPEED (0x00000040)
+#define MPI25_IOUNITPAGE7_PMCAP_IO_25_0_PCT_IOCSPEED (0x00000020)
+#define MPI25_IOUNITPAGE7_PMCAP_IO_50_0_PCT_IOCSPEED (0x00000010)
+#define MPI2_IOUNITPAGE7_PMCAP_HOST_WIDTH_CHANGE_PCIE (0x00000008)
+#define MPI2_IOUNITPAGE7_PMCAP_HOST_SPEED_CHANGE_PCIE (0x00000004)
+#define MPI25_IOUNITPAGE7_PMCAP_IO_WIDTH_CHANGE_PCIE (0x00000002)
+#define MPI25_IOUNITPAGE7_PMCAP_IO_SPEED_CHANGE_PCIE (0x00000001)
+
+/*obsolete names for the PowerManagementCapabilities bits (above) */
+#define MPI2_IOUNITPAGE7_PMCAP_12_5_PCT_IOCSPEED (0x00000400)
+#define MPI2_IOUNITPAGE7_PMCAP_25_0_PCT_IOCSPEED (0x00000200)
+#define MPI2_IOUNITPAGE7_PMCAP_50_0_PCT_IOCSPEED (0x00000100)
+#define MPI2_IOUNITPAGE7_PMCAP_PCIE_WIDTH_CHANGE (0x00000008) /*obsolete */
+#define MPI2_IOUNITPAGE7_PMCAP_PCIE_SPEED_CHANGE (0x00000004) /*obsolete */
+
+
+/*defines for IO Unit Page 7 IOCTemperatureUnits field */
+#define MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT (0x00)
+#define MPI2_IOUNITPAGE7_IOC_TEMP_FAHRENHEIT (0x01)
+#define MPI2_IOUNITPAGE7_IOC_TEMP_CELSIUS (0x02)
+
+/*defines for IO Unit Page 7 IOCSpeed field */
+#define MPI2_IOUNITPAGE7_IOC_SPEED_FULL (0x01)
+#define MPI2_IOUNITPAGE7_IOC_SPEED_HALF (0x02)
+#define MPI2_IOUNITPAGE7_IOC_SPEED_QUARTER (0x04)
+#define MPI2_IOUNITPAGE7_IOC_SPEED_EIGHTH (0x08)
+
+/*defines for IO Unit Page 7 BoardTemperatureUnits field */
+#define MPI2_IOUNITPAGE7_BOARD_TEMP_NOT_PRESENT (0x00)
+#define MPI2_IOUNITPAGE7_BOARD_TEMP_FAHRENHEIT (0x01)
+#define MPI2_IOUNITPAGE7_BOARD_TEMP_CELSIUS (0x02)
+
+
+/*IO Unit Page 8 */
+
+#define MPI2_IOUNIT8_NUM_THRESHOLDS (4)
+
+typedef struct _MPI2_IOUNIT8_SENSOR {
+ U16 Flags; /*0x00 */
+ U16 Reserved1; /*0x02 */
+ U16
+ Threshold[MPI2_IOUNIT8_NUM_THRESHOLDS]; /*0x04 */
+ U32 Reserved2; /*0x0C */
+ U32 Reserved3; /*0x10 */
+ U32 Reserved4; /*0x14 */
+} MPI2_IOUNIT8_SENSOR, *PTR_MPI2_IOUNIT8_SENSOR,
+ Mpi2IOUnit8Sensor_t, *pMpi2IOUnit8Sensor_t;
+
+/*defines for IO Unit Page 8 Sensor Flags field */
+#define MPI2_IOUNIT8_SENSOR_FLAGS_T3_ENABLE (0x0008)
+#define MPI2_IOUNIT8_SENSOR_FLAGS_T2_ENABLE (0x0004)
+#define MPI2_IOUNIT8_SENSOR_FLAGS_T1_ENABLE (0x0002)
+#define MPI2_IOUNIT8_SENSOR_FLAGS_T0_ENABLE (0x0001)
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumSensors at runtime.
+ */
+#ifndef MPI2_IOUNITPAGE8_SENSOR_ENTRIES
+#define MPI2_IOUNITPAGE8_SENSOR_ENTRIES (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_8 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x04 */
+ U32 Reserved2; /*0x08 */
+ U8 NumSensors; /*0x0C */
+ U8 PollingInterval; /*0x0D */
+ U16 Reserved3; /*0x0E */
+ MPI2_IOUNIT8_SENSOR
+ Sensor[MPI2_IOUNITPAGE8_SENSOR_ENTRIES];/*0x10 */
+} MPI2_CONFIG_PAGE_IO_UNIT_8,
+ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_8,
+ Mpi2IOUnitPage8_t, *pMpi2IOUnitPage8_t;
+
+#define MPI2_IOUNITPAGE8_PAGEVERSION (0x00)
+
+
+/*IO Unit Page 9 */
+
+typedef struct _MPI2_IOUNIT9_SENSOR {
+ U16 CurrentTemperature; /*0x00 */
+ U16 Reserved1; /*0x02 */
+ U8 Flags; /*0x04 */
+ U8 Reserved2; /*0x05 */
+ U16 Reserved3; /*0x06 */
+ U32 Reserved4; /*0x08 */
+ U32 Reserved5; /*0x0C */
+} MPI2_IOUNIT9_SENSOR, *PTR_MPI2_IOUNIT9_SENSOR,
+ Mpi2IOUnit9Sensor_t, *pMpi2IOUnit9Sensor_t;
+
+/*defines for IO Unit Page 9 Sensor Flags field */
+#define MPI2_IOUNIT9_SENSOR_FLAGS_TEMP_VALID (0x01)
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumSensors at runtime.
+ */
+#ifndef MPI2_IOUNITPAGE9_SENSOR_ENTRIES
+#define MPI2_IOUNITPAGE9_SENSOR_ENTRIES (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_9 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x04 */
+ U32 Reserved2; /*0x08 */
+ U8 NumSensors; /*0x0C */
+ U8 Reserved4; /*0x0D */
+ U16 Reserved3; /*0x0E */
+ MPI2_IOUNIT9_SENSOR
+ Sensor[MPI2_IOUNITPAGE9_SENSOR_ENTRIES];/*0x10 */
+} MPI2_CONFIG_PAGE_IO_UNIT_9,
+ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_9,
+ Mpi2IOUnitPage9_t, *pMpi2IOUnitPage9_t;
+
+#define MPI2_IOUNITPAGE9_PAGEVERSION (0x00)
+
+
+/*IO Unit Page 10 */
+
+typedef struct _MPI2_IOUNIT10_FUNCTION {
+ U8 CreditPercent; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+} MPI2_IOUNIT10_FUNCTION,
+ *PTR_MPI2_IOUNIT10_FUNCTION,
+ Mpi2IOUnit10Function_t,
+ *pMpi2IOUnit10Function_t;
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumFunctions at runtime.
+ */
+#ifndef MPI2_IOUNITPAGE10_FUNCTION_ENTRIES
+#define MPI2_IOUNITPAGE10_FUNCTION_ENTRIES (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_10 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U8 NumFunctions; /*0x04 */
+ U8 Reserved1; /*0x05 */
+ U16 Reserved2; /*0x06 */
+ U32 Reserved3; /*0x08 */
+ U32 Reserved4; /*0x0C */
+ MPI2_IOUNIT10_FUNCTION
+ Function[MPI2_IOUNITPAGE10_FUNCTION_ENTRIES];/*0x10 */
+} MPI2_CONFIG_PAGE_IO_UNIT_10,
+ *PTR_MPI2_CONFIG_PAGE_IO_UNIT_10,
+ Mpi2IOUnitPage10_t, *pMpi2IOUnitPage10_t;
+
+#define MPI2_IOUNITPAGE10_PAGEVERSION (0x01)
+
+
+
+/****************************************************************************
+* IOC Config Pages
+****************************************************************************/
+
+/*IOC Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_IOC_0 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x04 */
+ U32 Reserved2; /*0x08 */
+ U16 VendorID; /*0x0C */
+ U16 DeviceID; /*0x0E */
+ U8 RevisionID; /*0x10 */
+ U8 Reserved3; /*0x11 */
+ U16 Reserved4; /*0x12 */
+ U32 ClassCode; /*0x14 */
+ U16 SubsystemVendorID; /*0x18 */
+ U16 SubsystemID; /*0x1A */
+} MPI2_CONFIG_PAGE_IOC_0,
+ *PTR_MPI2_CONFIG_PAGE_IOC_0,
+ Mpi2IOCPage0_t, *pMpi2IOCPage0_t;
+
+#define MPI2_IOCPAGE0_PAGEVERSION (0x02)
+
+
+/*IOC Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_IOC_1 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 Flags; /*0x04 */
+ U32 CoalescingTimeout; /*0x08 */
+ U8 CoalescingDepth; /*0x0C */
+ U8 PCISlotNum; /*0x0D */
+ U8 PCIBusNum; /*0x0E */
+ U8 PCIDomainSegment; /*0x0F */
+ U32 Reserved1; /*0x10 */
+ U32 Reserved2; /*0x14 */
+} MPI2_CONFIG_PAGE_IOC_1,
+ *PTR_MPI2_CONFIG_PAGE_IOC_1,
+ Mpi2IOCPage1_t, *pMpi2IOCPage1_t;
+
+#define MPI2_IOCPAGE1_PAGEVERSION (0x05)
+
+/*defines for IOC Page 1 Flags field */
+#define MPI2_IOCPAGE1_REPLY_COALESCING (0x00000001)
+
+#define MPI2_IOCPAGE1_PCISLOTNUM_UNKNOWN (0xFF)
+#define MPI2_IOCPAGE1_PCIBUSNUM_UNKNOWN (0xFF)
+#define MPI2_IOCPAGE1_PCIDOMAIN_UNKNOWN (0xFF)
+
+/*IOC Page 6 */
+
+typedef struct _MPI2_CONFIG_PAGE_IOC_6 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32
+ CapabilitiesFlags; /*0x04 */
+ U8 MaxDrivesRAID0; /*0x08 */
+ U8 MaxDrivesRAID1; /*0x09 */
+ U8
+ MaxDrivesRAID1E; /*0x0A */
+ U8
+ MaxDrivesRAID10; /*0x0B */
+ U8 MinDrivesRAID0; /*0x0C */
+ U8 MinDrivesRAID1; /*0x0D */
+ U8
+ MinDrivesRAID1E; /*0x0E */
+ U8
+ MinDrivesRAID10; /*0x0F */
+ U32 Reserved1; /*0x10 */
+ U8
+ MaxGlobalHotSpares; /*0x14 */
+ U8 MaxPhysDisks; /*0x15 */
+ U8 MaxVolumes; /*0x16 */
+ U8 MaxConfigs; /*0x17 */
+ U8 MaxOCEDisks; /*0x18 */
+ U8 Reserved2; /*0x19 */
+ U16 Reserved3; /*0x1A */
+ U32
+ SupportedStripeSizeMapRAID0; /*0x1C */
+ U32
+ SupportedStripeSizeMapRAID1E; /*0x20 */
+ U32
+ SupportedStripeSizeMapRAID10; /*0x24 */
+ U32 Reserved4; /*0x28 */
+ U32 Reserved5; /*0x2C */
+ U16
+ DefaultMetadataSize; /*0x30 */
+ U16 Reserved6; /*0x32 */
+ U16
+ MaxBadBlockTableEntries; /*0x34 */
+ U16 Reserved7; /*0x36 */
+ U32
+ IRNvsramVersion; /*0x38 */
+} MPI2_CONFIG_PAGE_IOC_6,
+ *PTR_MPI2_CONFIG_PAGE_IOC_6,
+ Mpi2IOCPage6_t, *pMpi2IOCPage6_t;
+
+#define MPI2_IOCPAGE6_PAGEVERSION (0x05)
+
+/*defines for IOC Page 6 CapabilitiesFlags */
+#define MPI2_IOCPAGE6_CAP_FLAGS_4K_SECTORS_SUPPORT (0x00000020)
+#define MPI2_IOCPAGE6_CAP_FLAGS_RAID10_SUPPORT (0x00000010)
+#define MPI2_IOCPAGE6_CAP_FLAGS_RAID1_SUPPORT (0x00000008)
+#define MPI2_IOCPAGE6_CAP_FLAGS_RAID1E_SUPPORT (0x00000004)
+#define MPI2_IOCPAGE6_CAP_FLAGS_RAID0_SUPPORT (0x00000002)
+#define MPI2_IOCPAGE6_CAP_FLAGS_GLOBAL_HOT_SPARE (0x00000001)
+
+
+/*IOC Page 7 */
+
+#define MPI2_IOCPAGE7_EVENTMASK_WORDS (4)
+
+typedef struct _MPI2_CONFIG_PAGE_IOC_7 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x04 */
+ U32
+ EventMasks[MPI2_IOCPAGE7_EVENTMASK_WORDS];/*0x08 */
+ U16 SASBroadcastPrimitiveMasks; /*0x18 */
+ U16 SASNotifyPrimitiveMasks; /*0x1A */
+ U32 Reserved3; /*0x1C */
+} MPI2_CONFIG_PAGE_IOC_7,
+ *PTR_MPI2_CONFIG_PAGE_IOC_7,
+ Mpi2IOCPage7_t, *pMpi2IOCPage7_t;
+
+#define MPI2_IOCPAGE7_PAGEVERSION (0x02)
+
+
+/*IOC Page 8 */
+
+typedef struct _MPI2_CONFIG_PAGE_IOC_8 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U8 NumDevsPerEnclosure; /*0x04 */
+ U8 Reserved1; /*0x05 */
+ U16 Reserved2; /*0x06 */
+ U16 MaxPersistentEntries; /*0x08 */
+ U16 MaxNumPhysicalMappedIDs; /*0x0A */
+ U16 Flags; /*0x0C */
+ U16 Reserved3; /*0x0E */
+ U16 IRVolumeMappingFlags; /*0x10 */
+ U16 Reserved4; /*0x12 */
+ U32 Reserved5; /*0x14 */
+} MPI2_CONFIG_PAGE_IOC_8,
+ *PTR_MPI2_CONFIG_PAGE_IOC_8,
+ Mpi2IOCPage8_t, *pMpi2IOCPage8_t;
+
+#define MPI2_IOCPAGE8_PAGEVERSION (0x00)
+
+/*defines for IOC Page 8 Flags field */
+#define MPI2_IOCPAGE8_FLAGS_DA_START_SLOT_1 (0x00000020)
+#define MPI2_IOCPAGE8_FLAGS_RESERVED_TARGETID_0 (0x00000010)
+
+#define MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE (0x0000000E)
+#define MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING (0x00000000)
+#define MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING (0x00000002)
+
+#define MPI2_IOCPAGE8_FLAGS_DISABLE_PERSISTENT_MAPPING (0x00000001)
+#define MPI2_IOCPAGE8_FLAGS_ENABLE_PERSISTENT_MAPPING (0x00000000)
+
+/*defines for IOC Page 8 IRVolumeMappingFlags */
+#define MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE (0x00000003)
+#define MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING (0x00000000)
+#define MPI2_IOCPAGE8_IRFLAGS_HIGH_VOLUME_MAPPING (0x00000001)
+
+
+/****************************************************************************
+* BIOS Config Pages
+****************************************************************************/
+
+/*BIOS Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_BIOS_1 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 BiosOptions; /*0x04 */
+ U32 IOCSettings; /*0x08 */
+ U32 Reserved1; /*0x0C */
+ U32 DeviceSettings; /*0x10 */
+ U16 NumberOfDevices; /*0x14 */
+ U16 UEFIVersion; /*0x16 */
+ U16 IOTimeoutBlockDevicesNonRM; /*0x18 */
+ U16 IOTimeoutSequential; /*0x1A */
+ U16 IOTimeoutOther; /*0x1C */
+ U16 IOTimeoutBlockDevicesRM; /*0x1E */
+} MPI2_CONFIG_PAGE_BIOS_1,
+ *PTR_MPI2_CONFIG_PAGE_BIOS_1,
+ Mpi2BiosPage1_t, *pMpi2BiosPage1_t;
+
+#define MPI2_BIOSPAGE1_PAGEVERSION (0x05)
+
+/*values for BIOS Page 1 BiosOptions field */
+#define MPI2_BIOSPAGE1_OPTIONS_MASK_UEFI_HII_REGISTRATION (0x00000006)
+#define MPI2_BIOSPAGE1_OPTIONS_ENABLE_UEFI_HII (0x00000000)
+#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_UEFI_HII (0x00000002)
+#define MPI2_BIOSPAGE1_OPTIONS_VERSION_CHECK_UEFI_HII (0x00000004)
+
+#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_BIOS (0x00000001)
+
+/*values for BIOS Page 1 IOCSettings field */
+#define MPI2_BIOSPAGE1_IOCSET_MASK_BOOT_PREFERENCE (0x00030000)
+#define MPI2_BIOSPAGE1_IOCSET_ENCLOSURE_SLOT_BOOT (0x00000000)
+#define MPI2_BIOSPAGE1_IOCSET_SAS_ADDRESS_BOOT (0x00010000)
+
+#define MPI2_BIOSPAGE1_IOCSET_MASK_RM_SETTING (0x000000C0)
+#define MPI2_BIOSPAGE1_IOCSET_NONE_RM_SETTING (0x00000000)
+#define MPI2_BIOSPAGE1_IOCSET_BOOT_RM_SETTING (0x00000040)
+#define MPI2_BIOSPAGE1_IOCSET_MEDIA_RM_SETTING (0x00000080)
+
+#define MPI2_BIOSPAGE1_IOCSET_MASK_ADAPTER_SUPPORT (0x00000030)
+#define MPI2_BIOSPAGE1_IOCSET_NO_SUPPORT (0x00000000)
+#define MPI2_BIOSPAGE1_IOCSET_BIOS_SUPPORT (0x00000010)
+#define MPI2_BIOSPAGE1_IOCSET_OS_SUPPORT (0x00000020)
+#define MPI2_BIOSPAGE1_IOCSET_ALL_SUPPORT (0x00000030)
+
+#define MPI2_BIOSPAGE1_IOCSET_ALTERNATE_CHS (0x00000008)
+
+/*values for BIOS Page 1 DeviceSettings field */
+#define MPI2_BIOSPAGE1_DEVSET_DISABLE_SMART_POLLING (0x00000010)
+#define MPI2_BIOSPAGE1_DEVSET_DISABLE_SEQ_LUN (0x00000008)
+#define MPI2_BIOSPAGE1_DEVSET_DISABLE_RM_LUN (0x00000004)
+#define MPI2_BIOSPAGE1_DEVSET_DISABLE_NON_RM_LUN (0x00000002)
+#define MPI2_BIOSPAGE1_DEVSET_DISABLE_OTHER_LUN (0x00000001)
+
+/*defines for BIOS Page 1 UEFIVersion field */
+#define MPI2_BIOSPAGE1_UEFI_VER_MAJOR_MASK (0xFF00)
+#define MPI2_BIOSPAGE1_UEFI_VER_MAJOR_SHIFT (8)
+#define MPI2_BIOSPAGE1_UEFI_VER_MINOR_MASK (0x00FF)
+#define MPI2_BIOSPAGE1_UEFI_VER_MINOR_SHIFT (0)
+
+
+
+/*BIOS Page 2 */
+
+typedef struct _MPI2_BOOT_DEVICE_ADAPTER_ORDER {
+ U32 Reserved1; /*0x00 */
+ U32 Reserved2; /*0x04 */
+ U32 Reserved3; /*0x08 */
+ U32 Reserved4; /*0x0C */
+ U32 Reserved5; /*0x10 */
+ U32 Reserved6; /*0x14 */
+} MPI2_BOOT_DEVICE_ADAPTER_ORDER,
+ *PTR_MPI2_BOOT_DEVICE_ADAPTER_ORDER,
+ Mpi2BootDeviceAdapterOrder_t,
+ *pMpi2BootDeviceAdapterOrder_t;
+
+typedef struct _MPI2_BOOT_DEVICE_SAS_WWID {
+ U64 SASAddress; /*0x00 */
+ U8 LUN[8]; /*0x08 */
+ U32 Reserved1; /*0x10 */
+ U32 Reserved2; /*0x14 */
+} MPI2_BOOT_DEVICE_SAS_WWID,
+ *PTR_MPI2_BOOT_DEVICE_SAS_WWID,
+ Mpi2BootDeviceSasWwid_t,
+ *pMpi2BootDeviceSasWwid_t;
+
+typedef struct _MPI2_BOOT_DEVICE_ENCLOSURE_SLOT {
+ U64 EnclosureLogicalID; /*0x00 */
+ U32 Reserved1; /*0x08 */
+ U32 Reserved2; /*0x0C */
+ U16 SlotNumber; /*0x10 */
+ U16 Reserved3; /*0x12 */
+ U32 Reserved4; /*0x14 */
+} MPI2_BOOT_DEVICE_ENCLOSURE_SLOT,
+ *PTR_MPI2_BOOT_DEVICE_ENCLOSURE_SLOT,
+ Mpi2BootDeviceEnclosureSlot_t,
+ *pMpi2BootDeviceEnclosureSlot_t;
+
+typedef struct _MPI2_BOOT_DEVICE_DEVICE_NAME {
+ U64 DeviceName; /*0x00 */
+ U8 LUN[8]; /*0x08 */
+ U32 Reserved1; /*0x10 */
+ U32 Reserved2; /*0x14 */
+} MPI2_BOOT_DEVICE_DEVICE_NAME,
+ *PTR_MPI2_BOOT_DEVICE_DEVICE_NAME,
+ Mpi2BootDeviceDeviceName_t,
+ *pMpi2BootDeviceDeviceName_t;
+
+typedef union _MPI2_MPI2_BIOSPAGE2_BOOT_DEVICE {
+ MPI2_BOOT_DEVICE_ADAPTER_ORDER AdapterOrder;
+ MPI2_BOOT_DEVICE_SAS_WWID SasWwid;
+ MPI2_BOOT_DEVICE_ENCLOSURE_SLOT EnclosureSlot;
+ MPI2_BOOT_DEVICE_DEVICE_NAME DeviceName;
+} MPI2_BIOSPAGE2_BOOT_DEVICE,
+ *PTR_MPI2_BIOSPAGE2_BOOT_DEVICE,
+ Mpi2BiosPage2BootDevice_t,
+ *pMpi2BiosPage2BootDevice_t;
+
+typedef struct _MPI2_CONFIG_PAGE_BIOS_2 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x04 */
+ U32 Reserved2; /*0x08 */
+ U32 Reserved3; /*0x0C */
+ U32 Reserved4; /*0x10 */
+ U32 Reserved5; /*0x14 */
+ U32 Reserved6; /*0x18 */
+ U8 ReqBootDeviceForm; /*0x1C */
+ U8 Reserved7; /*0x1D */
+ U16 Reserved8; /*0x1E */
+ MPI2_BIOSPAGE2_BOOT_DEVICE RequestedBootDevice; /*0x20 */
+ U8 ReqAltBootDeviceForm; /*0x38 */
+ U8 Reserved9; /*0x39 */
+ U16 Reserved10; /*0x3A */
+ MPI2_BIOSPAGE2_BOOT_DEVICE RequestedAltBootDevice; /*0x3C */
+ U8 CurrentBootDeviceForm; /*0x58 */
+ U8 Reserved11; /*0x59 */
+ U16 Reserved12; /*0x5A */
+ MPI2_BIOSPAGE2_BOOT_DEVICE CurrentBootDevice; /*0x58 */
+} MPI2_CONFIG_PAGE_BIOS_2, *PTR_MPI2_CONFIG_PAGE_BIOS_2,
+ Mpi2BiosPage2_t, *pMpi2BiosPage2_t;
+
+#define MPI2_BIOSPAGE2_PAGEVERSION (0x04)
+
+/*values for BIOS Page 2 BootDeviceForm fields */
+#define MPI2_BIOSPAGE2_FORM_MASK (0x0F)
+#define MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED (0x00)
+#define MPI2_BIOSPAGE2_FORM_SAS_WWID (0x05)
+#define MPI2_BIOSPAGE2_FORM_ENCLOSURE_SLOT (0x06)
+#define MPI2_BIOSPAGE2_FORM_DEVICE_NAME (0x07)
+
+
+/*BIOS Page 3 */
+
+typedef struct _MPI2_ADAPTER_INFO {
+ U8 PciBusNumber; /*0x00 */
+ U8 PciDeviceAndFunctionNumber; /*0x01 */
+ U16 AdapterFlags; /*0x02 */
+} MPI2_ADAPTER_INFO, *PTR_MPI2_ADAPTER_INFO,
+ Mpi2AdapterInfo_t, *pMpi2AdapterInfo_t;
+
+#define MPI2_ADAPTER_INFO_FLAGS_EMBEDDED (0x0001)
+#define MPI2_ADAPTER_INFO_FLAGS_INIT_STATUS (0x0002)
+
+typedef struct _MPI2_CONFIG_PAGE_BIOS_3 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U32 GlobalFlags; /*0x04 */
+ U32 BiosVersion; /*0x08 */
+ MPI2_ADAPTER_INFO AdapterOrder[4]; /*0x0C */
+ U32 Reserved1; /*0x1C */
+} MPI2_CONFIG_PAGE_BIOS_3,
+ *PTR_MPI2_CONFIG_PAGE_BIOS_3,
+ Mpi2BiosPage3_t, *pMpi2BiosPage3_t;
+
+#define MPI2_BIOSPAGE3_PAGEVERSION (0x00)
+
+/*values for BIOS Page 3 GlobalFlags */
+#define MPI2_BIOSPAGE3_FLAGS_PAUSE_ON_ERROR (0x00000002)
+#define MPI2_BIOSPAGE3_FLAGS_VERBOSE_ENABLE (0x00000004)
+#define MPI2_BIOSPAGE3_FLAGS_HOOK_INT_40_DISABLE (0x00000010)
+
+#define MPI2_BIOSPAGE3_FLAGS_DEV_LIST_DISPLAY_MASK (0x000000E0)
+#define MPI2_BIOSPAGE3_FLAGS_INSTALLED_DEV_DISPLAY (0x00000000)
+#define MPI2_BIOSPAGE3_FLAGS_ADAPTER_DISPLAY (0x00000020)
+#define MPI2_BIOSPAGE3_FLAGS_ADAPTER_DEV_DISPLAY (0x00000040)
+
+
+/*BIOS Page 4 */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumPhys at runtime.
+ */
+#ifndef MPI2_BIOS_PAGE_4_PHY_ENTRIES
+#define MPI2_BIOS_PAGE_4_PHY_ENTRIES (1)
+#endif
+
+typedef struct _MPI2_BIOS4_ENTRY {
+ U64 ReassignmentWWID; /*0x00 */
+ U64 ReassignmentDeviceName; /*0x08 */
+} MPI2_BIOS4_ENTRY, *PTR_MPI2_BIOS4_ENTRY,
+ Mpi2MBios4Entry_t, *pMpi2Bios4Entry_t;
+
+typedef struct _MPI2_CONFIG_PAGE_BIOS_4 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U8 NumPhys; /*0x04 */
+ U8 Reserved1; /*0x05 */
+ U16 Reserved2; /*0x06 */
+ MPI2_BIOS4_ENTRY
+ Phy[MPI2_BIOS_PAGE_4_PHY_ENTRIES]; /*0x08 */
+} MPI2_CONFIG_PAGE_BIOS_4, *PTR_MPI2_CONFIG_PAGE_BIOS_4,
+ Mpi2BiosPage4_t, *pMpi2BiosPage4_t;
+
+#define MPI2_BIOSPAGE4_PAGEVERSION (0x01)
+
+
+/****************************************************************************
+* RAID Volume Config Pages
+****************************************************************************/
+
+/*RAID Volume Page 0 */
+
+typedef struct _MPI2_RAIDVOL0_PHYS_DISK {
+ U8 RAIDSetNum; /*0x00 */
+ U8 PhysDiskMap; /*0x01 */
+ U8 PhysDiskNum; /*0x02 */
+ U8 Reserved; /*0x03 */
+} MPI2_RAIDVOL0_PHYS_DISK, *PTR_MPI2_RAIDVOL0_PHYS_DISK,
+ Mpi2RaidVol0PhysDisk_t, *pMpi2RaidVol0PhysDisk_t;
+
+/*defines for the PhysDiskMap field */
+#define MPI2_RAIDVOL0_PHYSDISK_PRIMARY (0x01)
+#define MPI2_RAIDVOL0_PHYSDISK_SECONDARY (0x02)
+
+typedef struct _MPI2_RAIDVOL0_SETTINGS {
+ U16 Settings; /*0x00 */
+ U8 HotSparePool; /*0x01 */
+ U8 Reserved; /*0x02 */
+} MPI2_RAIDVOL0_SETTINGS, *PTR_MPI2_RAIDVOL0_SETTINGS,
+ Mpi2RaidVol0Settings_t,
+ *pMpi2RaidVol0Settings_t;
+
+/*RAID Volume Page 0 HotSparePool defines, also used in RAID Physical Disk */
+#define MPI2_RAID_HOT_SPARE_POOL_0 (0x01)
+#define MPI2_RAID_HOT_SPARE_POOL_1 (0x02)
+#define MPI2_RAID_HOT_SPARE_POOL_2 (0x04)
+#define MPI2_RAID_HOT_SPARE_POOL_3 (0x08)
+#define MPI2_RAID_HOT_SPARE_POOL_4 (0x10)
+#define MPI2_RAID_HOT_SPARE_POOL_5 (0x20)
+#define MPI2_RAID_HOT_SPARE_POOL_6 (0x40)
+#define MPI2_RAID_HOT_SPARE_POOL_7 (0x80)
+
+/*RAID Volume Page 0 VolumeSettings defines */
+#define MPI2_RAIDVOL0_SETTING_USE_PRODUCT_ID_SUFFIX (0x0008)
+#define MPI2_RAIDVOL0_SETTING_AUTO_CONFIG_HSWAP_DISABLE (0x0004)
+
+#define MPI2_RAIDVOL0_SETTING_MASK_WRITE_CACHING (0x0003)
+#define MPI2_RAIDVOL0_SETTING_UNCHANGED (0x0000)
+#define MPI2_RAIDVOL0_SETTING_DISABLE_WRITE_CACHING (0x0001)
+#define MPI2_RAIDVOL0_SETTING_ENABLE_WRITE_CACHING (0x0002)
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumPhysDisks at runtime.
+ */
+#ifndef MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX
+#define MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_0 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U16 DevHandle; /*0x04 */
+ U8 VolumeState; /*0x06 */
+ U8 VolumeType; /*0x07 */
+ U32 VolumeStatusFlags; /*0x08 */
+ MPI2_RAIDVOL0_SETTINGS VolumeSettings; /*0x0C */
+ U64 MaxLBA; /*0x10 */
+ U32 StripeSize; /*0x18 */
+ U16 BlockSize; /*0x1C */
+ U16 Reserved1; /*0x1E */
+ U8 SupportedPhysDisks;/*0x20 */
+ U8 ResyncRate; /*0x21 */
+ U16 DataScrubDuration; /*0x22 */
+ U8 NumPhysDisks; /*0x24 */
+ U8 Reserved2; /*0x25 */
+ U8 Reserved3; /*0x26 */
+ U8 InactiveStatus; /*0x27 */
+ MPI2_RAIDVOL0_PHYS_DISK
+ PhysDisk[MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX]; /*0x28 */
+} MPI2_CONFIG_PAGE_RAID_VOL_0,
+ *PTR_MPI2_CONFIG_PAGE_RAID_VOL_0,
+ Mpi2RaidVolPage0_t, *pMpi2RaidVolPage0_t;
+
+#define MPI2_RAIDVOLPAGE0_PAGEVERSION (0x0A)
+
+/*values for RAID VolumeState */
+#define MPI2_RAID_VOL_STATE_MISSING (0x00)
+#define MPI2_RAID_VOL_STATE_FAILED (0x01)
+#define MPI2_RAID_VOL_STATE_INITIALIZING (0x02)
+#define MPI2_RAID_VOL_STATE_ONLINE (0x03)
+#define MPI2_RAID_VOL_STATE_DEGRADED (0x04)
+#define MPI2_RAID_VOL_STATE_OPTIMAL (0x05)
+
+/*values for RAID VolumeType */
+#define MPI2_RAID_VOL_TYPE_RAID0 (0x00)
+#define MPI2_RAID_VOL_TYPE_RAID1E (0x01)
+#define MPI2_RAID_VOL_TYPE_RAID1 (0x02)
+#define MPI2_RAID_VOL_TYPE_RAID10 (0x05)
+#define MPI2_RAID_VOL_TYPE_UNKNOWN (0xFF)
+
+/*values for RAID Volume Page 0 VolumeStatusFlags field */
+#define MPI2_RAIDVOL0_STATUS_FLAG_PENDING_RESYNC (0x02000000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_BACKG_INIT_PENDING (0x01000000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_MDC_PENDING (0x00800000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_USER_CONSIST_PENDING (0x00400000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_MAKE_DATA_CONSISTENT (0x00200000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_DATA_SCRUB (0x00100000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_CONSISTENCY_CHECK (0x00080000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_CAPACITY_EXPANSION (0x00040000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_BACKGROUND_INIT (0x00020000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS (0x00010000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT (0x00000080)
+#define MPI2_RAIDVOL0_STATUS_FLAG_OCE_ALLOWED (0x00000040)
+#define MPI2_RAIDVOL0_STATUS_FLAG_BGI_COMPLETE (0x00000020)
+#define MPI2_RAIDVOL0_STATUS_FLAG_1E_OFFSET_MIRROR (0x00000000)
+#define MPI2_RAIDVOL0_STATUS_FLAG_1E_ADJACENT_MIRROR (0x00000010)
+#define MPI2_RAIDVOL0_STATUS_FLAG_BAD_BLOCK_TABLE_FULL (0x00000008)
+#define MPI2_RAIDVOL0_STATUS_FLAG_VOLUME_INACTIVE (0x00000004)
+#define MPI2_RAIDVOL0_STATUS_FLAG_QUIESCED (0x00000002)
+#define MPI2_RAIDVOL0_STATUS_FLAG_ENABLED (0x00000001)
+
+/*values for RAID Volume Page 0 SupportedPhysDisks field */
+#define MPI2_RAIDVOL0_SUPPORT_SOLID_STATE_DISKS (0x08)
+#define MPI2_RAIDVOL0_SUPPORT_HARD_DISKS (0x04)
+#define MPI2_RAIDVOL0_SUPPORT_SAS_PROTOCOL (0x02)
+#define MPI2_RAIDVOL0_SUPPORT_SATA_PROTOCOL (0x01)
+
+/*values for RAID Volume Page 0 InactiveStatus field */
+#define MPI2_RAIDVOLPAGE0_UNKNOWN_INACTIVE (0x00)
+#define MPI2_RAIDVOLPAGE0_STALE_METADATA_INACTIVE (0x01)
+#define MPI2_RAIDVOLPAGE0_FOREIGN_VOLUME_INACTIVE (0x02)
+#define MPI2_RAIDVOLPAGE0_INSUFFICIENT_RESOURCE_INACTIVE (0x03)
+#define MPI2_RAIDVOLPAGE0_CLONE_VOLUME_INACTIVE (0x04)
+#define MPI2_RAIDVOLPAGE0_INSUFFICIENT_METADATA_INACTIVE (0x05)
+#define MPI2_RAIDVOLPAGE0_PREVIOUSLY_DELETED (0x06)
+
+
+/*RAID Volume Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_1 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U16 DevHandle; /*0x04 */
+ U16 Reserved0; /*0x06 */
+ U8 GUID[24]; /*0x08 */
+ U8 Name[16]; /*0x20 */
+ U64 WWID; /*0x30 */
+ U32 Reserved1; /*0x38 */
+ U32 Reserved2; /*0x3C */
+} MPI2_CONFIG_PAGE_RAID_VOL_1,
+ *PTR_MPI2_CONFIG_PAGE_RAID_VOL_1,
+ Mpi2RaidVolPage1_t, *pMpi2RaidVolPage1_t;
+
+#define MPI2_RAIDVOLPAGE1_PAGEVERSION (0x03)
+
+
+/****************************************************************************
+* RAID Physical Disk Config Pages
+****************************************************************************/
+
+/*RAID Physical Disk Page 0 */
+
+typedef struct _MPI2_RAIDPHYSDISK0_SETTINGS {
+ U16 Reserved1; /*0x00 */
+ U8 HotSparePool; /*0x02 */
+ U8 Reserved2; /*0x03 */
+} MPI2_RAIDPHYSDISK0_SETTINGS,
+ *PTR_MPI2_RAIDPHYSDISK0_SETTINGS,
+ Mpi2RaidPhysDisk0Settings_t,
+ *pMpi2RaidPhysDisk0Settings_t;
+
+/*use MPI2_RAID_HOT_SPARE_POOL_ defines for the HotSparePool field */
+
+typedef struct _MPI2_RAIDPHYSDISK0_INQUIRY_DATA {
+ U8 VendorID[8]; /*0x00 */
+ U8 ProductID[16]; /*0x08 */
+ U8 ProductRevLevel[4]; /*0x18 */
+ U8 SerialNum[32]; /*0x1C */
+} MPI2_RAIDPHYSDISK0_INQUIRY_DATA,
+ *PTR_MPI2_RAIDPHYSDISK0_INQUIRY_DATA,
+ Mpi2RaidPhysDisk0InquiryData_t,
+ *pMpi2RaidPhysDisk0InquiryData_t;
+
+typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_0 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U16 DevHandle; /*0x04 */
+ U8 Reserved1; /*0x06 */
+ U8 PhysDiskNum; /*0x07 */
+ MPI2_RAIDPHYSDISK0_SETTINGS PhysDiskSettings; /*0x08 */
+ U32 Reserved2; /*0x0C */
+ MPI2_RAIDPHYSDISK0_INQUIRY_DATA InquiryData; /*0x10 */
+ U32 Reserved3; /*0x4C */
+ U8 PhysDiskState; /*0x50 */
+ U8 OfflineReason; /*0x51 */
+ U8 IncompatibleReason; /*0x52 */
+ U8 PhysDiskAttributes; /*0x53 */
+ U32 PhysDiskStatusFlags;/*0x54 */
+ U64 DeviceMaxLBA; /*0x58 */
+ U64 HostMaxLBA; /*0x60 */
+ U64 CoercedMaxLBA; /*0x68 */
+ U16 BlockSize; /*0x70 */
+ U16 Reserved5; /*0x72 */
+ U32 Reserved6; /*0x74 */
+} MPI2_CONFIG_PAGE_RD_PDISK_0,
+ *PTR_MPI2_CONFIG_PAGE_RD_PDISK_0,
+ Mpi2RaidPhysDiskPage0_t,
+ *pMpi2RaidPhysDiskPage0_t;
+
+#define MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION (0x05)
+
+/*PhysDiskState defines */
+#define MPI2_RAID_PD_STATE_NOT_CONFIGURED (0x00)
+#define MPI2_RAID_PD_STATE_NOT_COMPATIBLE (0x01)
+#define MPI2_RAID_PD_STATE_OFFLINE (0x02)
+#define MPI2_RAID_PD_STATE_ONLINE (0x03)
+#define MPI2_RAID_PD_STATE_HOT_SPARE (0x04)
+#define MPI2_RAID_PD_STATE_DEGRADED (0x05)
+#define MPI2_RAID_PD_STATE_REBUILDING (0x06)
+#define MPI2_RAID_PD_STATE_OPTIMAL (0x07)
+
+/*OfflineReason defines */
+#define MPI2_PHYSDISK0_ONLINE (0x00)
+#define MPI2_PHYSDISK0_OFFLINE_MISSING (0x01)
+#define MPI2_PHYSDISK0_OFFLINE_FAILED (0x03)
+#define MPI2_PHYSDISK0_OFFLINE_INITIALIZING (0x04)
+#define MPI2_PHYSDISK0_OFFLINE_REQUESTED (0x05)
+#define MPI2_PHYSDISK0_OFFLINE_FAILED_REQUESTED (0x06)
+#define MPI2_PHYSDISK0_OFFLINE_OTHER (0xFF)
+
+/*IncompatibleReason defines */
+#define MPI2_PHYSDISK0_COMPATIBLE (0x00)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_PROTOCOL (0x01)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_BLOCKSIZE (0x02)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_MAX_LBA (0x03)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_SATA_EXTENDED_CMD (0x04)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_REMOVEABLE_MEDIA (0x05)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE (0x06)
+#define MPI2_PHYSDISK0_INCOMPATIBLE_UNKNOWN (0xFF)
+
+/*PhysDiskAttributes defines */
+#define MPI2_PHYSDISK0_ATTRIB_MEDIA_MASK (0x0C)
+#define MPI2_PHYSDISK0_ATTRIB_SOLID_STATE_DRIVE (0x08)
+#define MPI2_PHYSDISK0_ATTRIB_HARD_DISK_DRIVE (0x04)
+
+#define MPI2_PHYSDISK0_ATTRIB_PROTOCOL_MASK (0x03)
+#define MPI2_PHYSDISK0_ATTRIB_SAS_PROTOCOL (0x02)
+#define MPI2_PHYSDISK0_ATTRIB_SATA_PROTOCOL (0x01)
+
+/*PhysDiskStatusFlags defines */
+#define MPI2_PHYSDISK0_STATUS_FLAG_NOT_CERTIFIED (0x00000040)
+#define MPI2_PHYSDISK0_STATUS_FLAG_OCE_TARGET (0x00000020)
+#define MPI2_PHYSDISK0_STATUS_FLAG_WRITE_CACHE_ENABLED (0x00000010)
+#define MPI2_PHYSDISK0_STATUS_FLAG_OPTIMAL_PREVIOUS (0x00000000)
+#define MPI2_PHYSDISK0_STATUS_FLAG_NOT_OPTIMAL_PREVIOUS (0x00000008)
+#define MPI2_PHYSDISK0_STATUS_FLAG_INACTIVE_VOLUME (0x00000004)
+#define MPI2_PHYSDISK0_STATUS_FLAG_QUIESCED (0x00000002)
+#define MPI2_PHYSDISK0_STATUS_FLAG_OUT_OF_SYNC (0x00000001)
+
+
+/*RAID Physical Disk Page 1 */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumPhysDiskPaths at runtime.
+ */
+#ifndef MPI2_RAID_PHYS_DISK1_PATH_MAX
+#define MPI2_RAID_PHYS_DISK1_PATH_MAX (1)
+#endif
+
+typedef struct _MPI2_RAIDPHYSDISK1_PATH {
+ U16 DevHandle; /*0x00 */
+ U16 Reserved1; /*0x02 */
+ U64 WWID; /*0x04 */
+ U64 OwnerWWID; /*0x0C */
+ U8 OwnerIdentifier; /*0x14 */
+ U8 Reserved2; /*0x15 */
+ U16 Flags; /*0x16 */
+} MPI2_RAIDPHYSDISK1_PATH, *PTR_MPI2_RAIDPHYSDISK1_PATH,
+ Mpi2RaidPhysDisk1Path_t,
+ *pMpi2RaidPhysDisk1Path_t;
+
+/*RAID Physical Disk Page 1 Physical Disk Path Flags field defines */
+#define MPI2_RAID_PHYSDISK1_FLAG_PRIMARY (0x0004)
+#define MPI2_RAID_PHYSDISK1_FLAG_BROKEN (0x0002)
+#define MPI2_RAID_PHYSDISK1_FLAG_INVALID (0x0001)
+
+typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_1 {
+ MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */
+ U8 NumPhysDiskPaths; /*0x04 */
+ U8 PhysDiskNum; /*0x05 */
+ U16 Reserved1; /*0x06 */
+ U32 Reserved2; /*0x08 */
+ MPI2_RAIDPHYSDISK1_PATH
+ PhysicalDiskPath[MPI2_RAID_PHYS_DISK1_PATH_MAX];/*0x0C */
+} MPI2_CONFIG_PAGE_RD_PDISK_1,
+ *PTR_MPI2_CONFIG_PAGE_RD_PDISK_1,
+ Mpi2RaidPhysDiskPage1_t,
+ *pMpi2RaidPhysDiskPage1_t;
+
+#define MPI2_RAIDPHYSDISKPAGE1_PAGEVERSION (0x02)
+
+
+/****************************************************************************
+* values for fields used by several types of SAS Config Pages
+****************************************************************************/
+
+/*values for NegotiatedLinkRates fields */
+#define MPI2_SAS_NEG_LINK_RATE_MASK_LOGICAL (0xF0)
+#define MPI2_SAS_NEG_LINK_RATE_SHIFT_LOGICAL (4)
+#define MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL (0x0F)
+/*link rates used for Negotiated Physical and Logical Link Rate */
+#define MPI2_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE (0x00)
+#define MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED (0x01)
+#define MPI2_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED (0x02)
+#define MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE (0x03)
+#define MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR (0x04)
+#define MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS (0x05)
+#define MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY (0x06)
+#define MPI2_SAS_NEG_LINK_RATE_1_5 (0x08)
+#define MPI2_SAS_NEG_LINK_RATE_3_0 (0x09)
+#define MPI2_SAS_NEG_LINK_RATE_6_0 (0x0A)
+#define MPI25_SAS_NEG_LINK_RATE_12_0 (0x0B)
+
+
+/*values for AttachedPhyInfo fields */
+#define MPI2_SAS_APHYINFO_INSIDE_ZPSDS_PERSISTENT (0x00000040)
+#define MPI2_SAS_APHYINFO_REQUESTED_INSIDE_ZPSDS (0x00000020)
+#define MPI2_SAS_APHYINFO_BREAK_REPLY_CAPABLE (0x00000010)
+
+#define MPI2_SAS_APHYINFO_REASON_MASK (0x0000000F)
+#define MPI2_SAS_APHYINFO_REASON_UNKNOWN (0x00000000)
+#define MPI2_SAS_APHYINFO_REASON_POWER_ON (0x00000001)
+#define MPI2_SAS_APHYINFO_REASON_HARD_RESET (0x00000002)
+#define MPI2_SAS_APHYINFO_REASON_SMP_PHY_CONTROL (0x00000003)
+#define MPI2_SAS_APHYINFO_REASON_LOSS_OF_SYNC (0x00000004)
+#define MPI2_SAS_APHYINFO_REASON_MULTIPLEXING_SEQ (0x00000005)
+#define MPI2_SAS_APHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00000006)
+#define MPI2_SAS_APHYINFO_REASON_BREAK_TIMEOUT (0x00000007)
+#define MPI2_SAS_APHYINFO_REASON_PHY_TEST_STOPPED (0x00000008)
+
+
+/*values for PhyInfo fields */
+#define MPI2_SAS_PHYINFO_PHY_VACANT (0x80000000)
+
+#define MPI2_SAS_PHYINFO_PHY_POWER_CONDITION_MASK (0x18000000)
+#define MPI2_SAS_PHYINFO_SHIFT_PHY_POWER_CONDITION (27)
+#define MPI2_SAS_PHYINFO_PHY_POWER_ACTIVE (0x00000000)
+#define MPI2_SAS_PHYINFO_PHY_POWER_PARTIAL (0x08000000)
+#define MPI2_SAS_PHYINFO_PHY_POWER_SLUMBER (0x10000000)
+
+#define MPI2_SAS_PHYINFO_CHANGED_REQ_INSIDE_ZPSDS (0x04000000)
+#define MPI2_SAS_PHYINFO_INSIDE_ZPSDS_PERSISTENT (0x02000000)
+#define MPI2_SAS_PHYINFO_REQ_INSIDE_ZPSDS (0x01000000)
+#define MPI2_SAS_PHYINFO_ZONE_GROUP_PERSISTENT (0x00400000)
+#define MPI2_SAS_PHYINFO_INSIDE_ZPSDS (0x00200000)
+#define MPI2_SAS_PHYINFO_ZONING_ENABLED (0x00100000)
+
+#define MPI2_SAS_PHYINFO_REASON_MASK (0x000F0000)
+#define MPI2_SAS_PHYINFO_REASON_UNKNOWN (0x00000000)
+#define MPI2_SAS_PHYINFO_REASON_POWER_ON (0x00010000)
+#define MPI2_SAS_PHYINFO_REASON_HARD_RESET (0x00020000)
+#define MPI2_SAS_PHYINFO_REASON_SMP_PHY_CONTROL (0x00030000)
+#define MPI2_SAS_PHYINFO_REASON_LOSS_OF_SYNC (0x00040000)
+#define MPI2_SAS_PHYINFO_REASON_MULTIPLEXING_SEQ (0x00050000)
+#define MPI2_SAS_PHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00060000)
+#define MPI2_SAS_PHYINFO_REASON_BREAK_TIMEOUT (0x00070000)
+#define MPI2_SAS_PHYINFO_REASON_PHY_TEST_STOPPED (0x00080000)
+
+#define MPI2_SAS_PHYINFO_MULTIPLEXING_SUPPORTED (0x00008000)
+#define MPI2_SAS_PHYINFO_SATA_PORT_ACTIVE (0x00004000)
+#define MPI2_SAS_PHYINFO_SATA_PORT_SELECTOR_PRESENT (0x00002000)
+#define MPI2_SAS_PHYINFO_VIRTUAL_PHY (0x00001000)
+
+#define MPI2_SAS_PHYINFO_MASK_PARTIAL_PATHWAY_TIME (0x00000F00)
+#define MPI2_SAS_PHYINFO_SHIFT_PARTIAL_PATHWAY_TIME (8)
+
+#define MPI2_SAS_PHYINFO_MASK_ROUTING_ATTRIBUTE (0x000000F0)
+#define MPI2_SAS_PHYINFO_DIRECT_ROUTING (0x00000000)
+#define MPI2_SAS_PHYINFO_SUBTRACTIVE_ROUTING (0x00000010)
+#define MPI2_SAS_PHYINFO_TABLE_ROUTING (0x00000020)
+
+
+/*values for SAS ProgrammedLinkRate fields */
+#define MPI2_SAS_PRATE_MAX_RATE_MASK (0xF0)
+#define MPI2_SAS_PRATE_MAX_RATE_NOT_PROGRAMMABLE (0x00)
+#define MPI2_SAS_PRATE_MAX_RATE_1_5 (0x80)
+#define MPI2_SAS_PRATE_MAX_RATE_3_0 (0x90)
+#define MPI2_SAS_PRATE_MAX_RATE_6_0 (0xA0)
+#define MPI2_SAS_PRATE_MIN_RATE_MASK (0x0F)
+#define MPI2_SAS_PRATE_MIN_RATE_NOT_PROGRAMMABLE (0x00)
+#define MPI2_SAS_PRATE_MIN_RATE_1_5 (0x08)
+#define MPI2_SAS_PRATE_MIN_RATE_3_0 (0x09)
+#define MPI2_SAS_PRATE_MIN_RATE_6_0 (0x0A)
+#define MPI25_SAS_PRATE_MIN_RATE_12_0 (0x0B)
+
+
+/*values for SAS HwLinkRate fields */
+#define MPI2_SAS_HWRATE_MAX_RATE_MASK (0xF0)
+#define MPI2_SAS_HWRATE_MAX_RATE_1_5 (0x80)
+#define MPI2_SAS_HWRATE_MAX_RATE_3_0 (0x90)
+#define MPI2_SAS_HWRATE_MAX_RATE_6_0 (0xA0)
+#define MPI2_SAS_HWRATE_MIN_RATE_MASK (0x0F)
+#define MPI2_SAS_HWRATE_MIN_RATE_1_5 (0x08)
+#define MPI2_SAS_HWRATE_MIN_RATE_3_0 (0x09)
+#define MPI2_SAS_HWRATE_MIN_RATE_6_0 (0x0A)
+#define MPI25_SAS_HWRATE_MIN_RATE_12_0 (0x0B)
+
+
+
+/****************************************************************************
+* SAS IO Unit Config Pages
+****************************************************************************/
+
+/*SAS IO Unit Page 0 */
+
+typedef struct _MPI2_SAS_IO_UNIT0_PHY_DATA {
+ U8 Port; /*0x00 */
+ U8 PortFlags; /*0x01 */
+ U8 PhyFlags; /*0x02 */
+ U8 NegotiatedLinkRate; /*0x03 */
+ U32 ControllerPhyDeviceInfo;/*0x04 */
+ U16 AttachedDevHandle; /*0x08 */
+ U16 ControllerDevHandle; /*0x0A */
+ U32 DiscoveryStatus; /*0x0C */
+ U32 Reserved; /*0x10 */
+} MPI2_SAS_IO_UNIT0_PHY_DATA,
+ *PTR_MPI2_SAS_IO_UNIT0_PHY_DATA,
+ Mpi2SasIOUnit0PhyData_t,
+ *pMpi2SasIOUnit0PhyData_t;
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumPhys at runtime.
+ */
+#ifndef MPI2_SAS_IOUNIT0_PHY_MAX
+#define MPI2_SAS_IOUNIT0_PHY_MAX (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1;/*0x08 */
+ U8 NumPhys; /*0x0C */
+ U8 Reserved2;/*0x0D */
+ U16 Reserved3;/*0x0E */
+ MPI2_SAS_IO_UNIT0_PHY_DATA
+ PhyData[MPI2_SAS_IOUNIT0_PHY_MAX]; /*0x10 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_0,
+ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_0,
+ Mpi2SasIOUnitPage0_t, *pMpi2SasIOUnitPage0_t;
+
+#define MPI2_SASIOUNITPAGE0_PAGEVERSION (0x05)
+
+/*values for SAS IO Unit Page 0 PortFlags */
+#define MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS (0x08)
+#define MPI2_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG (0x01)
+
+/*values for SAS IO Unit Page 0 PhyFlags */
+#define MPI2_SASIOUNIT0_PHYFLAGS_ZONING_ENABLED (0x10)
+#define MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED (0x08)
+
+/*use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */
+
+/*see mpi2_sas.h for values for
+ *SAS IO Unit Page 0 ControllerPhyDeviceInfo values */
+
+/*values for SAS IO Unit Page 0 DiscoveryStatus */
+#define MPI2_SASIOUNIT0_DS_MAX_ENCLOSURES_EXCEED (0x80000000)
+#define MPI2_SASIOUNIT0_DS_MAX_EXPANDERS_EXCEED (0x40000000)
+#define MPI2_SASIOUNIT0_DS_MAX_DEVICES_EXCEED (0x20000000)
+#define MPI2_SASIOUNIT0_DS_MAX_TOPO_PHYS_EXCEED (0x10000000)
+#define MPI2_SASIOUNIT0_DS_DOWNSTREAM_INITIATOR (0x08000000)
+#define MPI2_SASIOUNIT0_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000)
+#define MPI2_SASIOUNIT0_DS_EXP_MULTI_SUBTRACTIVE (0x00004000)
+#define MPI2_SASIOUNIT0_DS_MULTI_PORT_DOMAIN (0x00002000)
+#define MPI2_SASIOUNIT0_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000)
+#define MPI2_SASIOUNIT0_DS_UNSUPPORTED_DEVICE (0x00000800)
+#define MPI2_SASIOUNIT0_DS_TABLE_LINK (0x00000400)
+#define MPI2_SASIOUNIT0_DS_SUBTRACTIVE_LINK (0x00000200)
+#define MPI2_SASIOUNIT0_DS_SMP_CRC_ERROR (0x00000100)
+#define MPI2_SASIOUNIT0_DS_SMP_FUNCTION_FAILED (0x00000080)
+#define MPI2_SASIOUNIT0_DS_INDEX_NOT_EXIST (0x00000040)
+#define MPI2_SASIOUNIT0_DS_OUT_ROUTE_ENTRIES (0x00000020)
+#define MPI2_SASIOUNIT0_DS_SMP_TIMEOUT (0x00000010)
+#define MPI2_SASIOUNIT0_DS_MULTIPLE_PORTS (0x00000004)
+#define MPI2_SASIOUNIT0_DS_UNADDRESSABLE_DEVICE (0x00000002)
+#define MPI2_SASIOUNIT0_DS_LOOP_DETECTED (0x00000001)
+
+
+/*SAS IO Unit Page 1 */
+
+typedef struct _MPI2_SAS_IO_UNIT1_PHY_DATA {
+ U8 Port; /*0x00 */
+ U8 PortFlags; /*0x01 */
+ U8 PhyFlags; /*0x02 */
+ U8 MaxMinLinkRate; /*0x03 */
+ U32 ControllerPhyDeviceInfo; /*0x04 */
+ U16 MaxTargetPortConnectTime; /*0x08 */
+ U16 Reserved1; /*0x0A */
+} MPI2_SAS_IO_UNIT1_PHY_DATA,
+ *PTR_MPI2_SAS_IO_UNIT1_PHY_DATA,
+ Mpi2SasIOUnit1PhyData_t,
+ *pMpi2SasIOUnit1PhyData_t;
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumPhys at runtime.
+ */
+#ifndef MPI2_SAS_IOUNIT1_PHY_MAX
+#define MPI2_SAS_IOUNIT1_PHY_MAX (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_1 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U16
+ ControlFlags; /*0x08 */
+ U16
+ SASNarrowMaxQueueDepth; /*0x0A */
+ U16
+ AdditionalControlFlags; /*0x0C */
+ U16
+ SASWideMaxQueueDepth; /*0x0E */
+ U8
+ NumPhys; /*0x10 */
+ U8
+ SATAMaxQDepth; /*0x11 */
+ U8
+ ReportDeviceMissingDelay; /*0x12 */
+ U8
+ IODeviceMissingDelay; /*0x13 */
+ MPI2_SAS_IO_UNIT1_PHY_DATA
+ PhyData[MPI2_SAS_IOUNIT1_PHY_MAX]; /*0x14 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_1,
+ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_1,
+ Mpi2SasIOUnitPage1_t, *pMpi2SasIOUnitPage1_t;
+
+#define MPI2_SASIOUNITPAGE1_PAGEVERSION (0x09)
+
+/*values for SAS IO Unit Page 1 ControlFlags */
+#define MPI2_SASIOUNIT1_CONTROL_DEVICE_SELF_TEST (0x8000)
+#define MPI2_SASIOUNIT1_CONTROL_SATA_3_0_MAX (0x4000)
+#define MPI2_SASIOUNIT1_CONTROL_SATA_1_5_MAX (0x2000)
+#define MPI2_SASIOUNIT1_CONTROL_SATA_SW_PRESERVE (0x1000)
+
+#define MPI2_SASIOUNIT1_CONTROL_MASK_DEV_SUPPORT (0x0600)
+#define MPI2_SASIOUNIT1_CONTROL_SHIFT_DEV_SUPPORT (9)
+#define MPI2_SASIOUNIT1_CONTROL_DEV_SUPPORT_BOTH (0x0)
+#define MPI2_SASIOUNIT1_CONTROL_DEV_SAS_SUPPORT (0x1)
+#define MPI2_SASIOUNIT1_CONTROL_DEV_SATA_SUPPORT (0x2)
+
+#define MPI2_SASIOUNIT1_CONTROL_SATA_48BIT_LBA_REQUIRED (0x0080)
+#define MPI2_SASIOUNIT1_CONTROL_SATA_SMART_REQUIRED (0x0040)
+#define MPI2_SASIOUNIT1_CONTROL_SATA_NCQ_REQUIRED (0x0020)
+#define MPI2_SASIOUNIT1_CONTROL_SATA_FUA_REQUIRED (0x0010)
+#define MPI2_SASIOUNIT1_CONTROL_TABLE_SUBTRACTIVE_ILLEGAL (0x0008)
+#define MPI2_SASIOUNIT1_CONTROL_SUBTRACTIVE_ILLEGAL (0x0004)
+#define MPI2_SASIOUNIT1_CONTROL_FIRST_LVL_DISC_ONLY (0x0002)
+#define MPI2_SASIOUNIT1_CONTROL_CLEAR_AFFILIATION (0x0001)
+
+/*values for SAS IO Unit Page 1 AdditionalControlFlags */
+#define MPI2_SASIOUNIT1_ACONTROL_MULTI_PORT_DOMAIN_ILLEGAL (0x0080)
+#define MPI2_SASIOUNIT1_ACONTROL_SATA_ASYNCHROUNOUS_NOTIFICATION (0x0040)
+#define MPI2_SASIOUNIT1_ACONTROL_INVALID_TOPOLOGY_CORRECTION (0x0020)
+#define MPI2_SASIOUNIT1_ACONTROL_PORT_ENABLE_ONLY_SATA_LINK_RESET (0x0010)
+#define MPI2_SASIOUNIT1_ACONTROL_OTHER_AFFILIATION_SATA_LINK_RESET (0x0008)
+#define MPI2_SASIOUNIT1_ACONTROL_SELF_AFFILIATION_SATA_LINK_RESET (0x0004)
+#define MPI2_SASIOUNIT1_ACONTROL_NO_AFFILIATION_SATA_LINK_RESET (0x0002)
+#define MPI2_SASIOUNIT1_ACONTROL_ALLOW_TABLE_TO_TABLE (0x0001)
+
+/*defines for SAS IO Unit Page 1 ReportDeviceMissingDelay */
+#define MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK (0x7F)
+#define MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16 (0x80)
+
+/*values for SAS IO Unit Page 1 PortFlags */
+#define MPI2_SASIOUNIT1_PORT_FLAGS_AUTO_PORT_CONFIG (0x01)
+
+/*values for SAS IO Unit Page 1 PhyFlags */
+#define MPI2_SASIOUNIT1_PHYFLAGS_ZONING_ENABLE (0x10)
+#define MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE (0x08)
+
+/*values for SAS IO Unit Page 1 MaxMinLinkRate */
+#define MPI2_SASIOUNIT1_MAX_RATE_MASK (0xF0)
+#define MPI2_SASIOUNIT1_MAX_RATE_1_5 (0x80)
+#define MPI2_SASIOUNIT1_MAX_RATE_3_0 (0x90)
+#define MPI2_SASIOUNIT1_MAX_RATE_6_0 (0xA0)
+#define MPI25_SASIOUNIT1_MAX_RATE_12_0 (0xB0)
+#define MPI2_SASIOUNIT1_MIN_RATE_MASK (0x0F)
+#define MPI2_SASIOUNIT1_MIN_RATE_1_5 (0x08)
+#define MPI2_SASIOUNIT1_MIN_RATE_3_0 (0x09)
+#define MPI2_SASIOUNIT1_MIN_RATE_6_0 (0x0A)
+#define MPI25_SASIOUNIT1_MIN_RATE_12_0 (0x0B)
+
+/*see mpi2_sas.h for values for
+ *SAS IO Unit Page 1 ControllerPhyDeviceInfo values */
+
+
+/*SAS IO Unit Page 4 */
+
+typedef struct _MPI2_SAS_IOUNIT4_SPINUP_GROUP {
+ U8 MaxTargetSpinup; /*0x00 */
+ U8 SpinupDelay; /*0x01 */
+ U8 SpinupFlags; /*0x02 */
+ U8 Reserved1; /*0x03 */
+} MPI2_SAS_IOUNIT4_SPINUP_GROUP,
+ *PTR_MPI2_SAS_IOUNIT4_SPINUP_GROUP,
+ Mpi2SasIOUnit4SpinupGroup_t,
+ *pMpi2SasIOUnit4SpinupGroup_t;
+/*defines for SAS IO Unit Page 4 SpinupFlags */
+#define MPI2_SASIOUNIT4_SPINUP_DISABLE_FLAG (0x01)
+
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumPhys at runtime.
+ */
+#ifndef MPI2_SAS_IOUNIT4_PHY_MAX
+#define MPI2_SAS_IOUNIT4_PHY_MAX (4)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_4 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header;/*0x00 */
+ MPI2_SAS_IOUNIT4_SPINUP_GROUP
+ SpinupGroupParameters[4]; /*0x08 */
+ U32
+ Reserved1; /*0x18 */
+ U32
+ Reserved2; /*0x1C */
+ U32
+ Reserved3; /*0x20 */
+ U8
+ BootDeviceWaitTime; /*0x24 */
+ U8
+ Reserved4; /*0x25 */
+ U16
+ Reserved5; /*0x26 */
+ U8
+ NumPhys; /*0x28 */
+ U8
+ PEInitialSpinupDelay; /*0x29 */
+ U8
+ PEReplyDelay; /*0x2A */
+ U8
+ Flags; /*0x2B */
+ U8
+ PHY[MPI2_SAS_IOUNIT4_PHY_MAX]; /*0x2C */
+} MPI2_CONFIG_PAGE_SASIOUNIT_4,
+ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_4,
+ Mpi2SasIOUnitPage4_t, *pMpi2SasIOUnitPage4_t;
+
+#define MPI2_SASIOUNITPAGE4_PAGEVERSION (0x02)
+
+/*defines for Flags field */
+#define MPI2_SASIOUNIT4_FLAGS_AUTO_PORTENABLE (0x01)
+
+/*defines for PHY field */
+#define MPI2_SASIOUNIT4_PHY_SPINUP_GROUP_MASK (0x03)
+
+
+/*SAS IO Unit Page 5 */
+
+typedef struct _MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS {
+ U8 ControlFlags; /*0x00 */
+ U8 PortWidthModGroup; /*0x01 */
+ U16 InactivityTimerExponent; /*0x02 */
+ U8 SATAPartialTimeout; /*0x04 */
+ U8 Reserved2; /*0x05 */
+ U8 SATASlumberTimeout; /*0x06 */
+ U8 Reserved3; /*0x07 */
+ U8 SASPartialTimeout; /*0x08 */
+ U8 Reserved4; /*0x09 */
+ U8 SASSlumberTimeout; /*0x0A */
+ U8 Reserved5; /*0x0B */
+} MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS,
+ *PTR_MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS,
+ Mpi2SasIOUnit5PhyPmSettings_t,
+ *pMpi2SasIOUnit5PhyPmSettings_t;
+
+/*defines for ControlFlags field */
+#define MPI2_SASIOUNIT5_CONTROL_SAS_SLUMBER_ENABLE (0x08)
+#define MPI2_SASIOUNIT5_CONTROL_SAS_PARTIAL_ENABLE (0x04)
+#define MPI2_SASIOUNIT5_CONTROL_SATA_SLUMBER_ENABLE (0x02)
+#define MPI2_SASIOUNIT5_CONTROL_SATA_PARTIAL_ENABLE (0x01)
+
+/*defines for PortWidthModeGroup field */
+#define MPI2_SASIOUNIT5_PWMG_DISABLE (0xFF)
+
+/*defines for InactivityTimerExponent field */
+#define MPI2_SASIOUNIT5_ITE_MASK_SAS_SLUMBER (0x7000)
+#define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_SLUMBER (12)
+#define MPI2_SASIOUNIT5_ITE_MASK_SAS_PARTIAL (0x0700)
+#define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_PARTIAL (8)
+#define MPI2_SASIOUNIT5_ITE_MASK_SATA_SLUMBER (0x0070)
+#define MPI2_SASIOUNIT5_ITE_SHIFT_SATA_SLUMBER (4)
+#define MPI2_SASIOUNIT5_ITE_MASK_SATA_PARTIAL (0x0007)
+#define MPI2_SASIOUNIT5_ITE_SHIFT_SATA_PARTIAL (0)
+
+#define MPI2_SASIOUNIT5_ITE_TEN_SECONDS (7)
+#define MPI2_SASIOUNIT5_ITE_ONE_SECOND (6)
+#define MPI2_SASIOUNIT5_ITE_HUNDRED_MILLISECONDS (5)
+#define MPI2_SASIOUNIT5_ITE_TEN_MILLISECONDS (4)
+#define MPI2_SASIOUNIT5_ITE_ONE_MILLISECOND (3)
+#define MPI2_SASIOUNIT5_ITE_HUNDRED_MICROSECONDS (2)
+#define MPI2_SASIOUNIT5_ITE_TEN_MICROSECONDS (1)
+#define MPI2_SASIOUNIT5_ITE_ONE_MICROSECOND (0)
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumPhys at runtime.
+ */
+#ifndef MPI2_SAS_IOUNIT5_PHY_MAX
+#define MPI2_SAS_IOUNIT5_PHY_MAX (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_5 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U8 NumPhys; /*0x08 */
+ U8 Reserved1;/*0x09 */
+ U16 Reserved2;/*0x0A */
+ U32 Reserved3;/*0x0C */
+ MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS
+ SASPhyPowerManagementSettings[MPI2_SAS_IOUNIT5_PHY_MAX];/*0x10 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_5,
+ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_5,
+ Mpi2SasIOUnitPage5_t, *pMpi2SasIOUnitPage5_t;
+
+#define MPI2_SASIOUNITPAGE5_PAGEVERSION (0x01)
+
+
+/*SAS IO Unit Page 6 */
+
+typedef struct _MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS {
+ U8 CurrentStatus; /*0x00 */
+ U8 CurrentModulation; /*0x01 */
+ U8 CurrentUtilization; /*0x02 */
+ U8 Reserved1; /*0x03 */
+ U32 Reserved2; /*0x04 */
+} MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS,
+ *PTR_MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS,
+ Mpi2SasIOUnit6PortWidthModGroupStatus_t,
+ *pMpi2SasIOUnit6PortWidthModGroupStatus_t;
+
+/*defines for CurrentStatus field */
+#define MPI2_SASIOUNIT6_STATUS_UNAVAILABLE (0x00)
+#define MPI2_SASIOUNIT6_STATUS_UNCONFIGURED (0x01)
+#define MPI2_SASIOUNIT6_STATUS_INVALID_CONFIG (0x02)
+#define MPI2_SASIOUNIT6_STATUS_LINK_DOWN (0x03)
+#define MPI2_SASIOUNIT6_STATUS_OBSERVATION_ONLY (0x04)
+#define MPI2_SASIOUNIT6_STATUS_INACTIVE (0x05)
+#define MPI2_SASIOUNIT6_STATUS_ACTIVE_IOUNIT (0x06)
+#define MPI2_SASIOUNIT6_STATUS_ACTIVE_HOST (0x07)
+
+/*defines for CurrentModulation field */
+#define MPI2_SASIOUNIT6_MODULATION_25_PERCENT (0x00)
+#define MPI2_SASIOUNIT6_MODULATION_50_PERCENT (0x01)
+#define MPI2_SASIOUNIT6_MODULATION_75_PERCENT (0x02)
+#define MPI2_SASIOUNIT6_MODULATION_100_PERCENT (0x03)
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumGroups at runtime.
+ */
+#ifndef MPI2_SAS_IOUNIT6_GROUP_MAX
+#define MPI2_SAS_IOUNIT6_GROUP_MAX (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_6 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x08 */
+ U32 Reserved2; /*0x0C */
+ U8 NumGroups; /*0x10 */
+ U8 Reserved3; /*0x11 */
+ U16 Reserved4; /*0x12 */
+ MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS
+ PortWidthModulationGroupStatus[MPI2_SAS_IOUNIT6_GROUP_MAX]; /*0x14 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_6,
+ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_6,
+ Mpi2SasIOUnitPage6_t, *pMpi2SasIOUnitPage6_t;
+
+#define MPI2_SASIOUNITPAGE6_PAGEVERSION (0x00)
+
+
+/*SAS IO Unit Page 7 */
+
+typedef struct _MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS {
+ U8 Flags; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U8 Threshold75Pct; /*0x04 */
+ U8 Threshold50Pct; /*0x05 */
+ U8 Threshold25Pct; /*0x06 */
+ U8 Reserved3; /*0x07 */
+} MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS,
+ *PTR_MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS,
+ Mpi2SasIOUnit7PortWidthModGroupSettings_t,
+ *pMpi2SasIOUnit7PortWidthModGroupSettings_t;
+
+/*defines for Flags field */
+#define MPI2_SASIOUNIT7_FLAGS_ENABLE_PORT_WIDTH_MODULATION (0x01)
+
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumGroups at runtime.
+ */
+#ifndef MPI2_SAS_IOUNIT7_GROUP_MAX
+#define MPI2_SAS_IOUNIT7_GROUP_MAX (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_7 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U8 SamplingInterval; /*0x08 */
+ U8 WindowLength; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U32 Reserved2; /*0x0C */
+ U32 Reserved3; /*0x10 */
+ U8 NumGroups; /*0x14 */
+ U8 Reserved4; /*0x15 */
+ U16 Reserved5; /*0x16 */
+ MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS
+ PortWidthModulationGroupSettings[MPI2_SAS_IOUNIT7_GROUP_MAX];/*0x18 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_7,
+ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_7,
+ Mpi2SasIOUnitPage7_t, *pMpi2SasIOUnitPage7_t;
+
+#define MPI2_SASIOUNITPAGE7_PAGEVERSION (0x00)
+
+
+/*SAS IO Unit Page 8 */
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_8 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U32
+ Reserved1; /*0x08 */
+ U32
+ PowerManagementCapabilities; /*0x0C */
+ U8
+ TxRxSleepStatus; /*0x10 */
+ U8
+ Reserved2; /*0x11 */
+ U16
+ Reserved3; /*0x12 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_8,
+ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT_8,
+ Mpi2SasIOUnitPage8_t, *pMpi2SasIOUnitPage8_t;
+
+#define MPI2_SASIOUNITPAGE8_PAGEVERSION (0x00)
+
+/*defines for PowerManagementCapabilities field */
+#define MPI2_SASIOUNIT8_PM_HOST_PORT_WIDTH_MOD (0x00001000)
+#define MPI2_SASIOUNIT8_PM_HOST_SAS_SLUMBER_MODE (0x00000800)
+#define MPI2_SASIOUNIT8_PM_HOST_SAS_PARTIAL_MODE (0x00000400)
+#define MPI2_SASIOUNIT8_PM_HOST_SATA_SLUMBER_MODE (0x00000200)
+#define MPI2_SASIOUNIT8_PM_HOST_SATA_PARTIAL_MODE (0x00000100)
+#define MPI2_SASIOUNIT8_PM_IOUNIT_PORT_WIDTH_MOD (0x00000010)
+#define MPI2_SASIOUNIT8_PM_IOUNIT_SAS_SLUMBER_MODE (0x00000008)
+#define MPI2_SASIOUNIT8_PM_IOUNIT_SAS_PARTIAL_MODE (0x00000004)
+#define MPI2_SASIOUNIT8_PM_IOUNIT_SATA_SLUMBER_MODE (0x00000002)
+#define MPI2_SASIOUNIT8_PM_IOUNIT_SATA_PARTIAL_MODE (0x00000001)
+
+/*defines for TxRxSleepStatus field */
+#define MPI25_SASIOUNIT8_TXRXSLEEP_UNSUPPORTED (0x00)
+#define MPI25_SASIOUNIT8_TXRXSLEEP_DISENGAGED (0x01)
+#define MPI25_SASIOUNIT8_TXRXSLEEP_ACTIVE (0x02)
+#define MPI25_SASIOUNIT8_TXRXSLEEP_SHUTDOWN (0x03)
+
+
+
+/*SAS IO Unit Page 16 */
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT16 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U64
+ TimeStamp; /*0x08 */
+ U32
+ Reserved1; /*0x10 */
+ U32
+ Reserved2; /*0x14 */
+ U32
+ FastPathPendedRequests; /*0x18 */
+ U32
+ FastPathUnPendedRequests; /*0x1C */
+ U32
+ FastPathHostRequestStarts; /*0x20 */
+ U32
+ FastPathFirmwareRequestStarts; /*0x24 */
+ U32
+ FastPathHostCompletions; /*0x28 */
+ U32
+ FastPathFirmwareCompletions; /*0x2C */
+ U32
+ NonFastPathRequestStarts; /*0x30 */
+ U32
+ NonFastPathHostCompletions; /*0x30 */
+} MPI2_CONFIG_PAGE_SASIOUNIT16,
+ *PTR_MPI2_CONFIG_PAGE_SASIOUNIT16,
+ Mpi2SasIOUnitPage16_t, *pMpi2SasIOUnitPage16_t;
+
+#define MPI2_SASIOUNITPAGE16_PAGEVERSION (0x00)
+
+
+/****************************************************************************
+* SAS Expander Config Pages
+****************************************************************************/
+
+/*SAS Expander Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_EXPANDER_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U8
+ PhysicalPort; /*0x08 */
+ U8
+ ReportGenLength; /*0x09 */
+ U16
+ EnclosureHandle; /*0x0A */
+ U64
+ SASAddress; /*0x0C */
+ U32
+ DiscoveryStatus; /*0x14 */
+ U16
+ DevHandle; /*0x18 */
+ U16
+ ParentDevHandle; /*0x1A */
+ U16
+ ExpanderChangeCount; /*0x1C */
+ U16
+ ExpanderRouteIndexes; /*0x1E */
+ U8
+ NumPhys; /*0x20 */
+ U8
+ SASLevel; /*0x21 */
+ U16
+ Flags; /*0x22 */
+ U16
+ STPBusInactivityTimeLimit; /*0x24 */
+ U16
+ STPMaxConnectTimeLimit; /*0x26 */
+ U16
+ STP_SMP_NexusLossTime; /*0x28 */
+ U16
+ MaxNumRoutedSasAddresses; /*0x2A */
+ U64
+ ActiveZoneManagerSASAddress;/*0x2C */
+ U16
+ ZoneLockInactivityLimit; /*0x34 */
+ U16
+ Reserved1; /*0x36 */
+ U8
+ TimeToReducedFunc; /*0x38 */
+ U8
+ InitialTimeToReducedFunc; /*0x39 */
+ U8
+ MaxReducedFuncTime; /*0x3A */
+ U8
+ Reserved2; /*0x3B */
+} MPI2_CONFIG_PAGE_EXPANDER_0,
+ *PTR_MPI2_CONFIG_PAGE_EXPANDER_0,
+ Mpi2ExpanderPage0_t, *pMpi2ExpanderPage0_t;
+
+#define MPI2_SASEXPANDER0_PAGEVERSION (0x06)
+
+/*values for SAS Expander Page 0 DiscoveryStatus field */
+#define MPI2_SAS_EXPANDER0_DS_MAX_ENCLOSURES_EXCEED (0x80000000)
+#define MPI2_SAS_EXPANDER0_DS_MAX_EXPANDERS_EXCEED (0x40000000)
+#define MPI2_SAS_EXPANDER0_DS_MAX_DEVICES_EXCEED (0x20000000)
+#define MPI2_SAS_EXPANDER0_DS_MAX_TOPO_PHYS_EXCEED (0x10000000)
+#define MPI2_SAS_EXPANDER0_DS_DOWNSTREAM_INITIATOR (0x08000000)
+#define MPI2_SAS_EXPANDER0_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000)
+#define MPI2_SAS_EXPANDER0_DS_EXP_MULTI_SUBTRACTIVE (0x00004000)
+#define MPI2_SAS_EXPANDER0_DS_MULTI_PORT_DOMAIN (0x00002000)
+#define MPI2_SAS_EXPANDER0_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000)
+#define MPI2_SAS_EXPANDER0_DS_UNSUPPORTED_DEVICE (0x00000800)
+#define MPI2_SAS_EXPANDER0_DS_TABLE_LINK (0x00000400)
+#define MPI2_SAS_EXPANDER0_DS_SUBTRACTIVE_LINK (0x00000200)
+#define MPI2_SAS_EXPANDER0_DS_SMP_CRC_ERROR (0x00000100)
+#define MPI2_SAS_EXPANDER0_DS_SMP_FUNCTION_FAILED (0x00000080)
+#define MPI2_SAS_EXPANDER0_DS_INDEX_NOT_EXIST (0x00000040)
+#define MPI2_SAS_EXPANDER0_DS_OUT_ROUTE_ENTRIES (0x00000020)
+#define MPI2_SAS_EXPANDER0_DS_SMP_TIMEOUT (0x00000010)
+#define MPI2_SAS_EXPANDER0_DS_MULTIPLE_PORTS (0x00000004)
+#define MPI2_SAS_EXPANDER0_DS_UNADDRESSABLE_DEVICE (0x00000002)
+#define MPI2_SAS_EXPANDER0_DS_LOOP_DETECTED (0x00000001)
+
+/*values for SAS Expander Page 0 Flags field */
+#define MPI2_SAS_EXPANDER0_FLAGS_REDUCED_FUNCTIONALITY (0x2000)
+#define MPI2_SAS_EXPANDER0_FLAGS_ZONE_LOCKED (0x1000)
+#define MPI2_SAS_EXPANDER0_FLAGS_SUPPORTED_PHYSICAL_PRES (0x0800)
+#define MPI2_SAS_EXPANDER0_FLAGS_ASSERTED_PHYSICAL_PRES (0x0400)
+#define MPI2_SAS_EXPANDER0_FLAGS_ZONING_SUPPORT (0x0200)
+#define MPI2_SAS_EXPANDER0_FLAGS_ENABLED_ZONING (0x0100)
+#define MPI2_SAS_EXPANDER0_FLAGS_TABLE_TO_TABLE_SUPPORT (0x0080)
+#define MPI2_SAS_EXPANDER0_FLAGS_CONNECTOR_END_DEVICE (0x0010)
+#define MPI2_SAS_EXPANDER0_FLAGS_OTHERS_CONFIG (0x0004)
+#define MPI2_SAS_EXPANDER0_FLAGS_CONFIG_IN_PROGRESS (0x0002)
+#define MPI2_SAS_EXPANDER0_FLAGS_ROUTE_TABLE_CONFIG (0x0001)
+
+
+/*SAS Expander Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_EXPANDER_1 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U8
+ PhysicalPort; /*0x08 */
+ U8
+ Reserved1; /*0x09 */
+ U16
+ Reserved2; /*0x0A */
+ U8
+ NumPhys; /*0x0C */
+ U8
+ Phy; /*0x0D */
+ U16
+ NumTableEntriesProgrammed; /*0x0E */
+ U8
+ ProgrammedLinkRate; /*0x10 */
+ U8
+ HwLinkRate; /*0x11 */
+ U16
+ AttachedDevHandle; /*0x12 */
+ U32
+ PhyInfo; /*0x14 */
+ U32
+ AttachedDeviceInfo; /*0x18 */
+ U16
+ ExpanderDevHandle; /*0x1C */
+ U8
+ ChangeCount; /*0x1E */
+ U8
+ NegotiatedLinkRate; /*0x1F */
+ U8
+ PhyIdentifier; /*0x20 */
+ U8
+ AttachedPhyIdentifier; /*0x21 */
+ U8
+ Reserved3; /*0x22 */
+ U8
+ DiscoveryInfo; /*0x23 */
+ U32
+ AttachedPhyInfo; /*0x24 */
+ U8
+ ZoneGroup; /*0x28 */
+ U8
+ SelfConfigStatus; /*0x29 */
+ U16
+ Reserved4; /*0x2A */
+} MPI2_CONFIG_PAGE_EXPANDER_1,
+ *PTR_MPI2_CONFIG_PAGE_EXPANDER_1,
+ Mpi2ExpanderPage1_t, *pMpi2ExpanderPage1_t;
+
+#define MPI2_SASEXPANDER1_PAGEVERSION (0x02)
+
+/*use MPI2_SAS_PRATE_ defines for the ProgrammedLinkRate field */
+
+/*use MPI2_SAS_HWRATE_ defines for the HwLinkRate field */
+
+/*use MPI2_SAS_PHYINFO_ for the PhyInfo field */
+
+/*see mpi2_sas.h for the MPI2_SAS_DEVICE_INFO_ defines
+ *used for the AttachedDeviceInfo field */
+
+/*use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */
+
+/*values for SAS Expander Page 1 DiscoveryInfo field */
+#define MPI2_SAS_EXPANDER1_DISCINFO_BAD_PHY_DISABLED (0x04)
+#define MPI2_SAS_EXPANDER1_DISCINFO_LINK_STATUS_CHANGE (0x02)
+#define MPI2_SAS_EXPANDER1_DISCINFO_NO_ROUTING_ENTRIES (0x01)
+
+/*use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */
+
+
+/****************************************************************************
+* SAS Device Config Pages
+****************************************************************************/
+
+/*SAS Device Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U16
+ Slot; /*0x08 */
+ U16
+ EnclosureHandle; /*0x0A */
+ U64
+ SASAddress; /*0x0C */
+ U16
+ ParentDevHandle; /*0x14 */
+ U8
+ PhyNum; /*0x16 */
+ U8
+ AccessStatus; /*0x17 */
+ U16
+ DevHandle; /*0x18 */
+ U8
+ AttachedPhyIdentifier; /*0x1A */
+ U8
+ ZoneGroup; /*0x1B */
+ U32
+ DeviceInfo; /*0x1C */
+ U16
+ Flags; /*0x20 */
+ U8
+ PhysicalPort; /*0x22 */
+ U8
+ MaxPortConnections; /*0x23 */
+ U64
+ DeviceName; /*0x24 */
+ U8
+ PortGroups; /*0x2C */
+ U8
+ DmaGroup; /*0x2D */
+ U8
+ ControlGroup; /*0x2E */
+ U8
+ Reserved1; /*0x2F */
+ U32
+ Reserved2; /*0x30 */
+ U32
+ Reserved3; /*0x34 */
+} MPI2_CONFIG_PAGE_SAS_DEV_0,
+ *PTR_MPI2_CONFIG_PAGE_SAS_DEV_0,
+ Mpi2SasDevicePage0_t,
+ *pMpi2SasDevicePage0_t;
+
+#define MPI2_SASDEVICE0_PAGEVERSION (0x08)
+
+/*values for SAS Device Page 0 AccessStatus field */
+#define MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS (0x00)
+#define MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED (0x01)
+#define MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED (0x02)
+#define MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT (0x03)
+#define MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION (0x04)
+#define MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE (0x05)
+#define MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE (0x06)
+#define MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED (0x07)
+/*specific values for SATA Init failures */
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN (0x10)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT (0x11)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG (0x12)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION (0x13)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER (0x14)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN (0x15)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN (0x16)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN (0x17)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION (0x18)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE (0x19)
+#define MPI2_SAS_DEVICE0_ASTATUS_SIF_MAX (0x1F)
+
+/*see mpi2_sas.h for values for SAS Device Page 0 DeviceInfo values */
+
+/*values for SAS Device Page 0 Flags field */
+#define MPI2_SAS_DEVICE0_FLAGS_UNAUTHORIZED_DEVICE (0x8000)
+#define MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH (0x4000)
+#define MPI25_SAS_DEVICE0_FLAGS_FAST_PATH_CAPABLE (0x2000)
+#define MPI2_SAS_DEVICE0_FLAGS_SLUMBER_PM_CAPABLE (0x1000)
+#define MPI2_SAS_DEVICE0_FLAGS_PARTIAL_PM_CAPABLE (0x0800)
+#define MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY (0x0400)
+#define MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE (0x0200)
+#define MPI2_SAS_DEVICE0_FLAGS_UNSUPPORTED_DEVICE (0x0100)
+#define MPI2_SAS_DEVICE0_FLAGS_SATA_48BIT_LBA_SUPPORTED (0x0080)
+#define MPI2_SAS_DEVICE0_FLAGS_SATA_SMART_SUPPORTED (0x0040)
+#define MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED (0x0020)
+#define MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED (0x0010)
+#define MPI2_SAS_DEVICE0_FLAGS_PORT_SELECTOR_ATTACH (0x0008)
+#define MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT (0x0001)
+
+
+/*SAS Device Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_1 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U32
+ Reserved1; /*0x08 */
+ U64
+ SASAddress; /*0x0C */
+ U32
+ Reserved2; /*0x14 */
+ U16
+ DevHandle; /*0x18 */
+ U16
+ Reserved3; /*0x1A */
+ U8
+ InitialRegDeviceFIS[20];/*0x1C */
+} MPI2_CONFIG_PAGE_SAS_DEV_1,
+ *PTR_MPI2_CONFIG_PAGE_SAS_DEV_1,
+ Mpi2SasDevicePage1_t,
+ *pMpi2SasDevicePage1_t;
+
+#define MPI2_SASDEVICE1_PAGEVERSION (0x01)
+
+
+/****************************************************************************
+* SAS PHY Config Pages
+****************************************************************************/
+
+/*SAS PHY Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U16
+ OwnerDevHandle; /*0x08 */
+ U16
+ Reserved1; /*0x0A */
+ U16
+ AttachedDevHandle; /*0x0C */
+ U8
+ AttachedPhyIdentifier; /*0x0E */
+ U8
+ Reserved2; /*0x0F */
+ U32
+ AttachedPhyInfo; /*0x10 */
+ U8
+ ProgrammedLinkRate; /*0x14 */
+ U8
+ HwLinkRate; /*0x15 */
+ U8
+ ChangeCount; /*0x16 */
+ U8
+ Flags; /*0x17 */
+ U32
+ PhyInfo; /*0x18 */
+ U8
+ NegotiatedLinkRate; /*0x1C */
+ U8
+ Reserved3; /*0x1D */
+ U16
+ Reserved4; /*0x1E */
+} MPI2_CONFIG_PAGE_SAS_PHY_0,
+ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_0,
+ Mpi2SasPhyPage0_t, *pMpi2SasPhyPage0_t;
+
+#define MPI2_SASPHY0_PAGEVERSION (0x03)
+
+/*use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */
+
+/*use MPI2_SAS_PRATE_ defines for the ProgrammedLinkRate field */
+
+/*use MPI2_SAS_HWRATE_ defines for the HwLinkRate field */
+
+/*values for SAS PHY Page 0 Flags field */
+#define MPI2_SAS_PHY0_FLAGS_SGPIO_DIRECT_ATTACH_ENC (0x01)
+
+/*use MPI2_SAS_PHYINFO_ for the PhyInfo field */
+
+/*use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */
+
+
+/*SAS PHY Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_1 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U32
+ Reserved1; /*0x08 */
+ U32
+ InvalidDwordCount; /*0x0C */
+ U32
+ RunningDisparityErrorCount; /*0x10 */
+ U32
+ LossDwordSynchCount; /*0x14 */
+ U32
+ PhyResetProblemCount; /*0x18 */
+} MPI2_CONFIG_PAGE_SAS_PHY_1,
+ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_1,
+ Mpi2SasPhyPage1_t, *pMpi2SasPhyPage1_t;
+
+#define MPI2_SASPHY1_PAGEVERSION (0x01)
+
+
+/*SAS PHY Page 2 */
+
+typedef struct _MPI2_SASPHY2_PHY_EVENT {
+ U8 PhyEventCode; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U32 PhyEventInfo; /*0x04 */
+} MPI2_SASPHY2_PHY_EVENT, *PTR_MPI2_SASPHY2_PHY_EVENT,
+ Mpi2SasPhy2PhyEvent_t, *pMpi2SasPhy2PhyEvent_t;
+
+/*use MPI2_SASPHY3_EVENT_CODE_ for the PhyEventCode field */
+
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumPhyEvents at runtime.
+ */
+#ifndef MPI2_SASPHY2_PHY_EVENT_MAX
+#define MPI2_SASPHY2_PHY_EVENT_MAX (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_2 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U32
+ Reserved1; /*0x08 */
+ U8
+ NumPhyEvents; /*0x0C */
+ U8
+ Reserved2; /*0x0D */
+ U16
+ Reserved3; /*0x0E */
+ MPI2_SASPHY2_PHY_EVENT
+ PhyEvent[MPI2_SASPHY2_PHY_EVENT_MAX]; /*0x10 */
+} MPI2_CONFIG_PAGE_SAS_PHY_2,
+ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_2,
+ Mpi2SasPhyPage2_t,
+ *pMpi2SasPhyPage2_t;
+
+#define MPI2_SASPHY2_PAGEVERSION (0x00)
+
+
+/*SAS PHY Page 3 */
+
+typedef struct _MPI2_SASPHY3_PHY_EVENT_CONFIG {
+ U8 PhyEventCode; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U8 CounterType; /*0x04 */
+ U8 ThresholdWindow; /*0x05 */
+ U8 TimeUnits; /*0x06 */
+ U8 Reserved3; /*0x07 */
+ U32 EventThreshold; /*0x08 */
+ U16 ThresholdFlags; /*0x0C */
+ U16 Reserved4; /*0x0E */
+} MPI2_SASPHY3_PHY_EVENT_CONFIG,
+ *PTR_MPI2_SASPHY3_PHY_EVENT_CONFIG,
+ Mpi2SasPhy3PhyEventConfig_t,
+ *pMpi2SasPhy3PhyEventConfig_t;
+
+/*values for PhyEventCode field */
+#define MPI2_SASPHY3_EVENT_CODE_NO_EVENT (0x00)
+#define MPI2_SASPHY3_EVENT_CODE_INVALID_DWORD (0x01)
+#define MPI2_SASPHY3_EVENT_CODE_RUNNING_DISPARITY_ERROR (0x02)
+#define MPI2_SASPHY3_EVENT_CODE_LOSS_DWORD_SYNC (0x03)
+#define MPI2_SASPHY3_EVENT_CODE_PHY_RESET_PROBLEM (0x04)
+#define MPI2_SASPHY3_EVENT_CODE_ELASTICITY_BUF_OVERFLOW (0x05)
+#define MPI2_SASPHY3_EVENT_CODE_RX_ERROR (0x06)
+#define MPI2_SASPHY3_EVENT_CODE_RX_ADDR_FRAME_ERROR (0x20)
+#define MPI2_SASPHY3_EVENT_CODE_TX_AC_OPEN_REJECT (0x21)
+#define MPI2_SASPHY3_EVENT_CODE_RX_AC_OPEN_REJECT (0x22)
+#define MPI2_SASPHY3_EVENT_CODE_TX_RC_OPEN_REJECT (0x23)
+#define MPI2_SASPHY3_EVENT_CODE_RX_RC_OPEN_REJECT (0x24)
+#define MPI2_SASPHY3_EVENT_CODE_RX_AIP_PARTIAL_WAITING_ON (0x25)
+#define MPI2_SASPHY3_EVENT_CODE_RX_AIP_CONNECT_WAITING_ON (0x26)
+#define MPI2_SASPHY3_EVENT_CODE_TX_BREAK (0x27)
+#define MPI2_SASPHY3_EVENT_CODE_RX_BREAK (0x28)
+#define MPI2_SASPHY3_EVENT_CODE_BREAK_TIMEOUT (0x29)
+#define MPI2_SASPHY3_EVENT_CODE_CONNECTION (0x2A)
+#define MPI2_SASPHY3_EVENT_CODE_PEAKTX_PATHWAY_BLOCKED (0x2B)
+#define MPI2_SASPHY3_EVENT_CODE_PEAKTX_ARB_WAIT_TIME (0x2C)
+#define MPI2_SASPHY3_EVENT_CODE_PEAK_ARB_WAIT_TIME (0x2D)
+#define MPI2_SASPHY3_EVENT_CODE_PEAK_CONNECT_TIME (0x2E)
+#define MPI2_SASPHY3_EVENT_CODE_TX_SSP_FRAMES (0x40)
+#define MPI2_SASPHY3_EVENT_CODE_RX_SSP_FRAMES (0x41)
+#define MPI2_SASPHY3_EVENT_CODE_TX_SSP_ERROR_FRAMES (0x42)
+#define MPI2_SASPHY3_EVENT_CODE_RX_SSP_ERROR_FRAMES (0x43)
+#define MPI2_SASPHY3_EVENT_CODE_TX_CREDIT_BLOCKED (0x44)
+#define MPI2_SASPHY3_EVENT_CODE_RX_CREDIT_BLOCKED (0x45)
+#define MPI2_SASPHY3_EVENT_CODE_TX_SATA_FRAMES (0x50)
+#define MPI2_SASPHY3_EVENT_CODE_RX_SATA_FRAMES (0x51)
+#define MPI2_SASPHY3_EVENT_CODE_SATA_OVERFLOW (0x52)
+#define MPI2_SASPHY3_EVENT_CODE_TX_SMP_FRAMES (0x60)
+#define MPI2_SASPHY3_EVENT_CODE_RX_SMP_FRAMES (0x61)
+#define MPI2_SASPHY3_EVENT_CODE_RX_SMP_ERROR_FRAMES (0x63)
+#define MPI2_SASPHY3_EVENT_CODE_HOTPLUG_TIMEOUT (0xD0)
+#define MPI2_SASPHY3_EVENT_CODE_MISALIGNED_MUX_PRIMITIVE (0xD1)
+#define MPI2_SASPHY3_EVENT_CODE_RX_AIP (0xD2)
+
+/*values for the CounterType field */
+#define MPI2_SASPHY3_COUNTER_TYPE_WRAPPING (0x00)
+#define MPI2_SASPHY3_COUNTER_TYPE_SATURATING (0x01)
+#define MPI2_SASPHY3_COUNTER_TYPE_PEAK_VALUE (0x02)
+
+/*values for the TimeUnits field */
+#define MPI2_SASPHY3_TIME_UNITS_10_MICROSECONDS (0x00)
+#define MPI2_SASPHY3_TIME_UNITS_100_MICROSECONDS (0x01)
+#define MPI2_SASPHY3_TIME_UNITS_1_MILLISECOND (0x02)
+#define MPI2_SASPHY3_TIME_UNITS_10_MILLISECONDS (0x03)
+
+/*values for the ThresholdFlags field */
+#define MPI2_SASPHY3_TFLAGS_PHY_RESET (0x0002)
+#define MPI2_SASPHY3_TFLAGS_EVENT_NOTIFY (0x0001)
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumPhyEvents at runtime.
+ */
+#ifndef MPI2_SASPHY3_PHY_EVENT_MAX
+#define MPI2_SASPHY3_PHY_EVENT_MAX (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_3 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U32
+ Reserved1; /*0x08 */
+ U8
+ NumPhyEvents; /*0x0C */
+ U8
+ Reserved2; /*0x0D */
+ U16
+ Reserved3; /*0x0E */
+ MPI2_SASPHY3_PHY_EVENT_CONFIG
+ PhyEventConfig[MPI2_SASPHY3_PHY_EVENT_MAX]; /*0x10 */
+} MPI2_CONFIG_PAGE_SAS_PHY_3,
+ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_3,
+ Mpi2SasPhyPage3_t, *pMpi2SasPhyPage3_t;
+
+#define MPI2_SASPHY3_PAGEVERSION (0x00)
+
+
+/*SAS PHY Page 4 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_4 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U16
+ Reserved1; /*0x08 */
+ U8
+ Reserved2; /*0x0A */
+ U8
+ Flags; /*0x0B */
+ U8
+ InitialFrame[28]; /*0x0C */
+} MPI2_CONFIG_PAGE_SAS_PHY_4,
+ *PTR_MPI2_CONFIG_PAGE_SAS_PHY_4,
+ Mpi2SasPhyPage4_t, *pMpi2SasPhyPage4_t;
+
+#define MPI2_SASPHY4_PAGEVERSION (0x00)
+
+/*values for the Flags field */
+#define MPI2_SASPHY4_FLAGS_FRAME_VALID (0x02)
+#define MPI2_SASPHY4_FLAGS_SATA_FRAME (0x01)
+
+
+
+
+/****************************************************************************
+* SAS Port Config Pages
+****************************************************************************/
+
+/*SAS Port Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_PORT_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U8
+ PortNumber; /*0x08 */
+ U8
+ PhysicalPort; /*0x09 */
+ U8
+ PortWidth; /*0x0A */
+ U8
+ PhysicalPortWidth; /*0x0B */
+ U8
+ ZoneGroup; /*0x0C */
+ U8
+ Reserved1; /*0x0D */
+ U16
+ Reserved2; /*0x0E */
+ U64
+ SASAddress; /*0x10 */
+ U32
+ DeviceInfo; /*0x18 */
+ U32
+ Reserved3; /*0x1C */
+ U32
+ Reserved4; /*0x20 */
+} MPI2_CONFIG_PAGE_SAS_PORT_0,
+ *PTR_MPI2_CONFIG_PAGE_SAS_PORT_0,
+ Mpi2SasPortPage0_t, *pMpi2SasPortPage0_t;
+
+#define MPI2_SASPORT0_PAGEVERSION (0x00)
+
+/*see mpi2_sas.h for values for SAS Port Page 0 DeviceInfo values */
+
+
+/****************************************************************************
+* SAS Enclosure Config Pages
+****************************************************************************/
+
+/*SAS Enclosure Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U32
+ Reserved1; /*0x08 */
+ U64
+ EnclosureLogicalID; /*0x0C */
+ U16
+ Flags; /*0x14 */
+ U16
+ EnclosureHandle; /*0x16 */
+ U16
+ NumSlots; /*0x18 */
+ U16
+ StartSlot; /*0x1A */
+ U16
+ Reserved2; /*0x1C */
+ U16
+ SEPDevHandle; /*0x1E */
+ U32
+ Reserved3; /*0x20 */
+ U32
+ Reserved4; /*0x24 */
+} MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0,
+ *PTR_MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0,
+ Mpi2SasEnclosurePage0_t, *pMpi2SasEnclosurePage0_t;
+
+#define MPI2_SASENCLOSURE0_PAGEVERSION (0x03)
+
+/*values for SAS Enclosure Page 0 Flags field */
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_MASK (0x000F)
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_UNKNOWN (0x0000)
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SES (0x0001)
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SGPIO (0x0002)
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_EXP_SGPIO (0x0003)
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_SES_ENCLOSURE (0x0004)
+#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_GPIO (0x0005)
+
+
+/****************************************************************************
+* Log Config Page
+****************************************************************************/
+
+/*Log Page 0 */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumLogEntries at runtime.
+ */
+#ifndef MPI2_LOG_0_NUM_LOG_ENTRIES
+#define MPI2_LOG_0_NUM_LOG_ENTRIES (1)
+#endif
+
+#define MPI2_LOG_0_LOG_DATA_LENGTH (0x1C)
+
+typedef struct _MPI2_LOG_0_ENTRY {
+ U64 TimeStamp; /*0x00 */
+ U32 Reserved1; /*0x08 */
+ U16 LogSequence; /*0x0C */
+ U16 LogEntryQualifier; /*0x0E */
+ U8 VP_ID; /*0x10 */
+ U8 VF_ID; /*0x11 */
+ U16 Reserved2; /*0x12 */
+ U8
+ LogData[MPI2_LOG_0_LOG_DATA_LENGTH];/*0x14 */
+} MPI2_LOG_0_ENTRY, *PTR_MPI2_LOG_0_ENTRY,
+ Mpi2Log0Entry_t, *pMpi2Log0Entry_t;
+
+/*values for Log Page 0 LogEntry LogEntryQualifier field */
+#define MPI2_LOG_0_ENTRY_QUAL_ENTRY_UNUSED (0x0000)
+#define MPI2_LOG_0_ENTRY_QUAL_POWER_ON_RESET (0x0001)
+#define MPI2_LOG_0_ENTRY_QUAL_TIMESTAMP_UPDATE (0x0002)
+#define MPI2_LOG_0_ENTRY_QUAL_MIN_IMPLEMENT_SPEC (0x8000)
+#define MPI2_LOG_0_ENTRY_QUAL_MAX_IMPLEMENT_SPEC (0xFFFF)
+
+typedef struct _MPI2_CONFIG_PAGE_LOG_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U32 Reserved1; /*0x08 */
+ U32 Reserved2; /*0x0C */
+ U16 NumLogEntries;/*0x10 */
+ U16 Reserved3; /*0x12 */
+ MPI2_LOG_0_ENTRY
+ LogEntry[MPI2_LOG_0_NUM_LOG_ENTRIES]; /*0x14 */
+} MPI2_CONFIG_PAGE_LOG_0, *PTR_MPI2_CONFIG_PAGE_LOG_0,
+ Mpi2LogPage0_t, *pMpi2LogPage0_t;
+
+#define MPI2_LOG_0_PAGEVERSION (0x02)
+
+
+/****************************************************************************
+* RAID Config Page
+****************************************************************************/
+
+/*RAID Page 0 */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check the value returned for NumElements at runtime.
+ */
+#ifndef MPI2_RAIDCONFIG0_MAX_ELEMENTS
+#define MPI2_RAIDCONFIG0_MAX_ELEMENTS (1)
+#endif
+
+typedef struct _MPI2_RAIDCONFIG0_CONFIG_ELEMENT {
+ U16 ElementFlags; /*0x00 */
+ U16 VolDevHandle; /*0x02 */
+ U8 HotSparePool; /*0x04 */
+ U8 PhysDiskNum; /*0x05 */
+ U16 PhysDiskDevHandle; /*0x06 */
+} MPI2_RAIDCONFIG0_CONFIG_ELEMENT,
+ *PTR_MPI2_RAIDCONFIG0_CONFIG_ELEMENT,
+ Mpi2RaidConfig0ConfigElement_t,
+ *pMpi2RaidConfig0ConfigElement_t;
+
+/*values for the ElementFlags field */
+#define MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE (0x000F)
+#define MPI2_RAIDCONFIG0_EFLAGS_VOLUME_ELEMENT (0x0000)
+#define MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT (0x0001)
+#define MPI2_RAIDCONFIG0_EFLAGS_HOT_SPARE_ELEMENT (0x0002)
+#define MPI2_RAIDCONFIG0_EFLAGS_OCE_ELEMENT (0x0003)
+
+
+typedef struct _MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U8 NumHotSpares; /*0x08 */
+ U8 NumPhysDisks; /*0x09 */
+ U8 NumVolumes; /*0x0A */
+ U8 ConfigNum; /*0x0B */
+ U32 Flags; /*0x0C */
+ U8 ConfigGUID[24]; /*0x10 */
+ U32 Reserved1; /*0x28 */
+ U8 NumElements; /*0x2C */
+ U8 Reserved2; /*0x2D */
+ U16 Reserved3; /*0x2E */
+ MPI2_RAIDCONFIG0_CONFIG_ELEMENT
+ ConfigElement[MPI2_RAIDCONFIG0_MAX_ELEMENTS]; /*0x30 */
+} MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0,
+ *PTR_MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0,
+ Mpi2RaidConfigurationPage0_t,
+ *pMpi2RaidConfigurationPage0_t;
+
+#define MPI2_RAIDCONFIG0_PAGEVERSION (0x00)
+
+/*values for RAID Configuration Page 0 Flags field */
+#define MPI2_RAIDCONFIG0_FLAG_FOREIGN_CONFIG (0x00000001)
+
+
+/****************************************************************************
+* Driver Persistent Mapping Config Pages
+****************************************************************************/
+
+/*Driver Persistent Mapping Page 0 */
+
+typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY {
+ U64 PhysicalIdentifier; /*0x00 */
+ U16 MappingInformation; /*0x08 */
+ U16 DeviceIndex; /*0x0A */
+ U32 PhysicalBitsMapping; /*0x0C */
+ U32 Reserved1; /*0x10 */
+} MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY,
+ *PTR_MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY,
+ Mpi2DriverMap0Entry_t, *pMpi2DriverMap0Entry_t;
+
+typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAPPING_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY Entry; /*0x08 */
+} MPI2_CONFIG_PAGE_DRIVER_MAPPING_0,
+ *PTR_MPI2_CONFIG_PAGE_DRIVER_MAPPING_0,
+ Mpi2DriverMappingPage0_t, *pMpi2DriverMappingPage0_t;
+
+#define MPI2_DRIVERMAPPING0_PAGEVERSION (0x00)
+
+/*values for Driver Persistent Mapping Page 0 MappingInformation field */
+#define MPI2_DRVMAP0_MAPINFO_SLOT_MASK (0x07F0)
+#define MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT (4)
+#define MPI2_DRVMAP0_MAPINFO_MISSING_MASK (0x000F)
+
+
+/****************************************************************************
+* Ethernet Config Pages
+****************************************************************************/
+
+/*Ethernet Page 0 */
+
+/*IP address (union of IPv4 and IPv6) */
+typedef union _MPI2_ETHERNET_IP_ADDR {
+ U32 IPv4Addr;
+ U32 IPv6Addr[4];
+} MPI2_ETHERNET_IP_ADDR, *PTR_MPI2_ETHERNET_IP_ADDR,
+ Mpi2EthernetIpAddr_t, *pMpi2EthernetIpAddr_t;
+
+#define MPI2_ETHERNET_HOST_NAME_LENGTH (32)
+
+typedef struct _MPI2_CONFIG_PAGE_ETHERNET_0 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */
+ U8 NumInterfaces; /*0x08 */
+ U8 Reserved0; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U32 Status; /*0x0C */
+ U8 MediaState; /*0x10 */
+ U8 Reserved2; /*0x11 */
+ U16 Reserved3; /*0x12 */
+ U8 MacAddress[6]; /*0x14 */
+ U8 Reserved4; /*0x1A */
+ U8 Reserved5; /*0x1B */
+ MPI2_ETHERNET_IP_ADDR IpAddress; /*0x1C */
+ MPI2_ETHERNET_IP_ADDR SubnetMask; /*0x2C */
+ MPI2_ETHERNET_IP_ADDR GatewayIpAddress;/*0x3C */
+ MPI2_ETHERNET_IP_ADDR DNS1IpAddress; /*0x4C */
+ MPI2_ETHERNET_IP_ADDR DNS2IpAddress; /*0x5C */
+ MPI2_ETHERNET_IP_ADDR DhcpIpAddress; /*0x6C */
+ U8
+ HostName[MPI2_ETHERNET_HOST_NAME_LENGTH];/*0x7C */
+} MPI2_CONFIG_PAGE_ETHERNET_0,
+ *PTR_MPI2_CONFIG_PAGE_ETHERNET_0,
+ Mpi2EthernetPage0_t, *pMpi2EthernetPage0_t;
+
+#define MPI2_ETHERNETPAGE0_PAGEVERSION (0x00)
+
+/*values for Ethernet Page 0 Status field */
+#define MPI2_ETHPG0_STATUS_IPV6_CAPABLE (0x80000000)
+#define MPI2_ETHPG0_STATUS_IPV4_CAPABLE (0x40000000)
+#define MPI2_ETHPG0_STATUS_CONSOLE_CONNECTED (0x20000000)
+#define MPI2_ETHPG0_STATUS_DEFAULT_IF (0x00000100)
+#define MPI2_ETHPG0_STATUS_FW_DWNLD_ENABLED (0x00000080)
+#define MPI2_ETHPG0_STATUS_TELNET_ENABLED (0x00000040)
+#define MPI2_ETHPG0_STATUS_SSH2_ENABLED (0x00000020)
+#define MPI2_ETHPG0_STATUS_DHCP_CLIENT_ENABLED (0x00000010)
+#define MPI2_ETHPG0_STATUS_IPV6_ENABLED (0x00000008)
+#define MPI2_ETHPG0_STATUS_IPV4_ENABLED (0x00000004)
+#define MPI2_ETHPG0_STATUS_IPV6_ADDRESSES (0x00000002)
+#define MPI2_ETHPG0_STATUS_ETH_IF_ENABLED (0x00000001)
+
+/*values for Ethernet Page 0 MediaState field */
+#define MPI2_ETHPG0_MS_DUPLEX_MASK (0x80)
+#define MPI2_ETHPG0_MS_HALF_DUPLEX (0x00)
+#define MPI2_ETHPG0_MS_FULL_DUPLEX (0x80)
+
+#define MPI2_ETHPG0_MS_CONNECT_SPEED_MASK (0x07)
+#define MPI2_ETHPG0_MS_NOT_CONNECTED (0x00)
+#define MPI2_ETHPG0_MS_10MBIT (0x01)
+#define MPI2_ETHPG0_MS_100MBIT (0x02)
+#define MPI2_ETHPG0_MS_1GBIT (0x03)
+
+
+/*Ethernet Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_ETHERNET_1 {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U32
+ Reserved0; /*0x08 */
+ U32
+ Flags; /*0x0C */
+ U8
+ MediaState; /*0x10 */
+ U8
+ Reserved1; /*0x11 */
+ U16
+ Reserved2; /*0x12 */
+ U8
+ MacAddress[6]; /*0x14 */
+ U8
+ Reserved3; /*0x1A */
+ U8
+ Reserved4; /*0x1B */
+ MPI2_ETHERNET_IP_ADDR
+ StaticIpAddress; /*0x1C */
+ MPI2_ETHERNET_IP_ADDR
+ StaticSubnetMask; /*0x2C */
+ MPI2_ETHERNET_IP_ADDR
+ StaticGatewayIpAddress; /*0x3C */
+ MPI2_ETHERNET_IP_ADDR
+ StaticDNS1IpAddress; /*0x4C */
+ MPI2_ETHERNET_IP_ADDR
+ StaticDNS2IpAddress; /*0x5C */
+ U32
+ Reserved5; /*0x6C */
+ U32
+ Reserved6; /*0x70 */
+ U32
+ Reserved7; /*0x74 */
+ U32
+ Reserved8; /*0x78 */
+ U8
+ HostName[MPI2_ETHERNET_HOST_NAME_LENGTH];/*0x7C */
+} MPI2_CONFIG_PAGE_ETHERNET_1,
+ *PTR_MPI2_CONFIG_PAGE_ETHERNET_1,
+ Mpi2EthernetPage1_t, *pMpi2EthernetPage1_t;
+
+#define MPI2_ETHERNETPAGE1_PAGEVERSION (0x00)
+
+/*values for Ethernet Page 1 Flags field */
+#define MPI2_ETHPG1_FLAG_SET_DEFAULT_IF (0x00000100)
+#define MPI2_ETHPG1_FLAG_ENABLE_FW_DOWNLOAD (0x00000080)
+#define MPI2_ETHPG1_FLAG_ENABLE_TELNET (0x00000040)
+#define MPI2_ETHPG1_FLAG_ENABLE_SSH2 (0x00000020)
+#define MPI2_ETHPG1_FLAG_ENABLE_DHCP_CLIENT (0x00000010)
+#define MPI2_ETHPG1_FLAG_ENABLE_IPV6 (0x00000008)
+#define MPI2_ETHPG1_FLAG_ENABLE_IPV4 (0x00000004)
+#define MPI2_ETHPG1_FLAG_USE_IPV6_ADDRESSES (0x00000002)
+#define MPI2_ETHPG1_FLAG_ENABLE_ETH_IF (0x00000001)
+
+/*values for Ethernet Page 1 MediaState field */
+#define MPI2_ETHPG1_MS_DUPLEX_MASK (0x80)
+#define MPI2_ETHPG1_MS_HALF_DUPLEX (0x00)
+#define MPI2_ETHPG1_MS_FULL_DUPLEX (0x80)
+
+#define MPI2_ETHPG1_MS_DATA_RATE_MASK (0x07)
+#define MPI2_ETHPG1_MS_DATA_RATE_AUTO (0x00)
+#define MPI2_ETHPG1_MS_DATA_RATE_10MBIT (0x01)
+#define MPI2_ETHPG1_MS_DATA_RATE_100MBIT (0x02)
+#define MPI2_ETHPG1_MS_DATA_RATE_1GBIT (0x03)
+
+
+/****************************************************************************
+* Extended Manufacturing Config Pages
+****************************************************************************/
+
+/*
+ *Generic structure to use for product-specific extended manufacturing pages
+ *(currently Extended Manufacturing Page 40 through Extended Manufacturing
+ *Page 60).
+ */
+
+typedef struct _MPI2_CONFIG_PAGE_EXT_MAN_PS {
+ MPI2_CONFIG_EXTENDED_PAGE_HEADER
+ Header; /*0x00 */
+ U32
+ ProductSpecificInfo; /*0x08 */
+} MPI2_CONFIG_PAGE_EXT_MAN_PS,
+ *PTR_MPI2_CONFIG_PAGE_EXT_MAN_PS,
+ Mpi2ExtManufacturingPagePS_t,
+ *pMpi2ExtManufacturingPagePS_t;
+
+/*PageVersion should be provided by product-specific code */
+
+#endif
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_init.h b/drivers/scsi/mpt3sas/mpi/mpi2_init.h
new file mode 100644
index 00000000000..a079e524247
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_init.h
@@ -0,0 +1,560 @@
+/*
+ * Copyright (c) 2000-2012 LSI Corporation.
+ *
+ *
+ * Name: mpi2_init.h
+ * Title: MPI SCSI initiator mode messages and structures
+ * Creation Date: June 23, 2006
+ *
+ * mpi2_init.h Version: 02.00.14
+ *
+ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25
+ * prefix are for use only on MPI v2.5 products, and must not be used
+ * with MPI v2.0 products. Unless otherwise noted, names beginning with
+ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products.
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 10-31-07 02.00.01 Fixed name for pMpi2SCSITaskManagementRequest_t.
+ * 12-18-07 02.00.02 Modified Task Management Target Reset Method defines.
+ * 02-29-08 02.00.03 Added Query Task Set and Query Unit Attention.
+ * 03-03-08 02.00.04 Fixed name of struct _MPI2_SCSI_TASK_MANAGE_REPLY.
+ * 05-21-08 02.00.05 Fixed typo in name of Mpi2SepRequest_t.
+ * 10-02-08 02.00.06 Removed Untagged and No Disconnect values from SCSI IO
+ * Control field Task Attribute flags.
+ * Moved LUN field defines to mpi2.h becasue they are
+ * common to many structures.
+ * 05-06-09 02.00.07 Changed task management type of Query Unit Attention to
+ * Query Asynchronous Event.
+ * Defined two new bits in the SlotStatus field of the SCSI
+ * Enclosure Processor Request and Reply.
+ * 10-28-09 02.00.08 Added defines for decoding the ResponseInfo bytes for
+ * both SCSI IO Error Reply and SCSI Task Management Reply.
+ * Added ResponseInfo field to MPI2_SCSI_TASK_MANAGE_REPLY.
+ * Added MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG define.
+ * 02-10-10 02.00.09 Removed unused structure that had "#if 0" around it.
+ * 05-12-10 02.00.10 Added optional vendor-unique region to SCSI IO Request.
+ * 11-10-10 02.00.11 Added MPI2_SCSIIO_NUM_SGLOFFSETS define.
+ * 11-18-11 02.00.12 Incorporating additions for MPI v2.5.
+ * 02-06-12 02.00.13 Added alternate defines for Task Priority / Command
+ * Priority to match SAM-4.
+ * Added EEDPErrorOffset to MPI2_SCSI_IO_REPLY.
+ * 07-10-12 02.00.14 Added MPI2_SCSIIO_CONTROL_SHIFT_DATADIRECTION.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_INIT_H
+#define MPI2_INIT_H
+
+/*****************************************************************************
+*
+* SCSI Initiator Messages
+*
+*****************************************************************************/
+
+/****************************************************************************
+* SCSI IO messages and associated structures
+****************************************************************************/
+
+typedef struct _MPI2_SCSI_IO_CDB_EEDP32 {
+ U8 CDB[20]; /*0x00 */
+ U32 PrimaryReferenceTag; /*0x14 */
+ U16 PrimaryApplicationTag; /*0x18 */
+ U16 PrimaryApplicationTagMask; /*0x1A */
+ U32 TransferLength; /*0x1C */
+} MPI2_SCSI_IO_CDB_EEDP32, *PTR_MPI2_SCSI_IO_CDB_EEDP32,
+ Mpi2ScsiIoCdbEedp32_t, *pMpi2ScsiIoCdbEedp32_t;
+
+/*MPI v2.0 CDB field */
+typedef union _MPI2_SCSI_IO_CDB_UNION {
+ U8 CDB32[32];
+ MPI2_SCSI_IO_CDB_EEDP32 EEDP32;
+ MPI2_SGE_SIMPLE_UNION SGE;
+} MPI2_SCSI_IO_CDB_UNION, *PTR_MPI2_SCSI_IO_CDB_UNION,
+ Mpi2ScsiIoCdb_t, *pMpi2ScsiIoCdb_t;
+
+/*MPI v2.0 SCSI IO Request Message */
+typedef struct _MPI2_SCSI_IO_REQUEST {
+ U16 DevHandle; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved1; /*0x04 */
+ U8 Reserved2; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+ U32 SenseBufferLowAddress; /*0x0C */
+ U16 SGLFlags; /*0x10 */
+ U8 SenseBufferLength; /*0x12 */
+ U8 Reserved4; /*0x13 */
+ U8 SGLOffset0; /*0x14 */
+ U8 SGLOffset1; /*0x15 */
+ U8 SGLOffset2; /*0x16 */
+ U8 SGLOffset3; /*0x17 */
+ U32 SkipCount; /*0x18 */
+ U32 DataLength; /*0x1C */
+ U32 BidirectionalDataLength; /*0x20 */
+ U16 IoFlags; /*0x24 */
+ U16 EEDPFlags; /*0x26 */
+ U32 EEDPBlockSize; /*0x28 */
+ U32 SecondaryReferenceTag; /*0x2C */
+ U16 SecondaryApplicationTag; /*0x30 */
+ U16 ApplicationTagTranslationMask; /*0x32 */
+ U8 LUN[8]; /*0x34 */
+ U32 Control; /*0x3C */
+ MPI2_SCSI_IO_CDB_UNION CDB; /*0x40 */
+
+#ifdef MPI2_SCSI_IO_VENDOR_UNIQUE_REGION /*typically this is left undefined */
+ MPI2_SCSI_IO_VENDOR_UNIQUE VendorRegion;
+#endif
+
+ MPI2_SGE_IO_UNION SGL; /*0x60 */
+
+} MPI2_SCSI_IO_REQUEST, *PTR_MPI2_SCSI_IO_REQUEST,
+ Mpi2SCSIIORequest_t, *pMpi2SCSIIORequest_t;
+
+/*SCSI IO MsgFlags bits */
+
+/*MsgFlags for SenseBufferAddressSpace */
+#define MPI2_SCSIIO_MSGFLAGS_MASK_SENSE_ADDR (0x0C)
+#define MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR (0x00)
+#define MPI2_SCSIIO_MSGFLAGS_IOCDDR_SENSE_ADDR (0x04)
+#define MPI2_SCSIIO_MSGFLAGS_IOCPLB_SENSE_ADDR (0x08)
+#define MPI2_SCSIIO_MSGFLAGS_IOCPLBNTA_SENSE_ADDR (0x0C)
+
+/*SCSI IO SGLFlags bits */
+
+/*base values for Data Location Address Space */
+#define MPI2_SCSIIO_SGLFLAGS_ADDR_MASK (0x0C)
+#define MPI2_SCSIIO_SGLFLAGS_SYSTEM_ADDR (0x00)
+#define MPI2_SCSIIO_SGLFLAGS_IOCDDR_ADDR (0x04)
+#define MPI2_SCSIIO_SGLFLAGS_IOCPLB_ADDR (0x08)
+#define MPI2_SCSIIO_SGLFLAGS_IOCPLBNTA_ADDR (0x0C)
+
+/*base values for Type */
+#define MPI2_SCSIIO_SGLFLAGS_TYPE_MASK (0x03)
+#define MPI2_SCSIIO_SGLFLAGS_TYPE_MPI (0x00)
+#define MPI2_SCSIIO_SGLFLAGS_TYPE_IEEE32 (0x01)
+#define MPI2_SCSIIO_SGLFLAGS_TYPE_IEEE64 (0x02)
+
+/*shift values for each sub-field */
+#define MPI2_SCSIIO_SGLFLAGS_SGL3_SHIFT (12)
+#define MPI2_SCSIIO_SGLFLAGS_SGL2_SHIFT (8)
+#define MPI2_SCSIIO_SGLFLAGS_SGL1_SHIFT (4)
+#define MPI2_SCSIIO_SGLFLAGS_SGL0_SHIFT (0)
+
+/*number of SGLOffset fields */
+#define MPI2_SCSIIO_NUM_SGLOFFSETS (4)
+
+/*SCSI IO IoFlags bits */
+
+/*Large CDB Address Space */
+#define MPI2_SCSIIO_CDB_ADDR_MASK (0x6000)
+#define MPI2_SCSIIO_CDB_ADDR_SYSTEM (0x0000)
+#define MPI2_SCSIIO_CDB_ADDR_IOCDDR (0x2000)
+#define MPI2_SCSIIO_CDB_ADDR_IOCPLB (0x4000)
+#define MPI2_SCSIIO_CDB_ADDR_IOCPLBNTA (0x6000)
+
+#define MPI2_SCSIIO_IOFLAGS_LARGE_CDB (0x1000)
+#define MPI2_SCSIIO_IOFLAGS_BIDIRECTIONAL (0x0800)
+#define MPI2_SCSIIO_IOFLAGS_MULTICAST (0x0400)
+#define MPI2_SCSIIO_IOFLAGS_CMD_DETERMINES_DATA_DIR (0x0200)
+#define MPI2_SCSIIO_IOFLAGS_CDBLENGTH_MASK (0x01FF)
+
+/*SCSI IO EEDPFlags bits */
+
+#define MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG (0x8000)
+#define MPI2_SCSIIO_EEDPFLAGS_INC_SEC_REFTAG (0x4000)
+#define MPI2_SCSIIO_EEDPFLAGS_INC_PRI_APPTAG (0x2000)
+#define MPI2_SCSIIO_EEDPFLAGS_INC_SEC_APPTAG (0x1000)
+
+#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG (0x0400)
+#define MPI2_SCSIIO_EEDPFLAGS_CHECK_APPTAG (0x0200)
+#define MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD (0x0100)
+
+#define MPI2_SCSIIO_EEDPFLAGS_PASSTHRU_REFTAG (0x0008)
+
+#define MPI2_SCSIIO_EEDPFLAGS_MASK_OP (0x0007)
+#define MPI2_SCSIIO_EEDPFLAGS_NOOP_OP (0x0000)
+#define MPI2_SCSIIO_EEDPFLAGS_CHECK_OP (0x0001)
+#define MPI2_SCSIIO_EEDPFLAGS_STRIP_OP (0x0002)
+#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP (0x0003)
+#define MPI2_SCSIIO_EEDPFLAGS_INSERT_OP (0x0004)
+#define MPI2_SCSIIO_EEDPFLAGS_REPLACE_OP (0x0006)
+#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REGEN_OP (0x0007)
+
+/*SCSI IO LUN fields: use MPI2_LUN_ from mpi2.h */
+
+/*SCSI IO Control bits */
+#define MPI2_SCSIIO_CONTROL_ADDCDBLEN_MASK (0xFC000000)
+#define MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT (26)
+
+#define MPI2_SCSIIO_CONTROL_DATADIRECTION_MASK (0x03000000)
+#define MPI2_SCSIIO_CONTROL_SHIFT_DATADIRECTION (24)
+#define MPI2_SCSIIO_CONTROL_NODATATRANSFER (0x00000000)
+#define MPI2_SCSIIO_CONTROL_WRITE (0x01000000)
+#define MPI2_SCSIIO_CONTROL_READ (0x02000000)
+#define MPI2_SCSIIO_CONTROL_BIDIRECTIONAL (0x03000000)
+
+#define MPI2_SCSIIO_CONTROL_TASKPRI_MASK (0x00007800)
+#define MPI2_SCSIIO_CONTROL_TASKPRI_SHIFT (11)
+/*alternate name for the previous field; called Command Priority in SAM-4 */
+#define MPI2_SCSIIO_CONTROL_CMDPRI_MASK (0x00007800)
+#define MPI2_SCSIIO_CONTROL_CMDPRI_SHIFT (11)
+
+#define MPI2_SCSIIO_CONTROL_TASKATTRIBUTE_MASK (0x00000700)
+#define MPI2_SCSIIO_CONTROL_SIMPLEQ (0x00000000)
+#define MPI2_SCSIIO_CONTROL_HEADOFQ (0x00000100)
+#define MPI2_SCSIIO_CONTROL_ORDEREDQ (0x00000200)
+#define MPI2_SCSIIO_CONTROL_ACAQ (0x00000400)
+
+#define MPI2_SCSIIO_CONTROL_TLR_MASK (0x000000C0)
+#define MPI2_SCSIIO_CONTROL_NO_TLR (0x00000000)
+#define MPI2_SCSIIO_CONTROL_TLR_ON (0x00000040)
+#define MPI2_SCSIIO_CONTROL_TLR_OFF (0x00000080)
+
+/*MPI v2.5 CDB field */
+typedef union _MPI25_SCSI_IO_CDB_UNION {
+ U8 CDB32[32];
+ MPI2_SCSI_IO_CDB_EEDP32 EEDP32;
+ MPI2_IEEE_SGE_SIMPLE64 SGE;
+} MPI25_SCSI_IO_CDB_UNION, *PTR_MPI25_SCSI_IO_CDB_UNION,
+ Mpi25ScsiIoCdb_t, *pMpi25ScsiIoCdb_t;
+
+/*MPI v2.5 SCSI IO Request Message */
+typedef struct _MPI25_SCSI_IO_REQUEST {
+ U16 DevHandle; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved1; /*0x04 */
+ U8 Reserved2; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+ U32 SenseBufferLowAddress; /*0x0C */
+ U8 DMAFlags; /*0x10 */
+ U8 Reserved5; /*0x11 */
+ U8 SenseBufferLength; /*0x12 */
+ U8 Reserved4; /*0x13 */
+ U8 SGLOffset0; /*0x14 */
+ U8 SGLOffset1; /*0x15 */
+ U8 SGLOffset2; /*0x16 */
+ U8 SGLOffset3; /*0x17 */
+ U32 SkipCount; /*0x18 */
+ U32 DataLength; /*0x1C */
+ U32 BidirectionalDataLength; /*0x20 */
+ U16 IoFlags; /*0x24 */
+ U16 EEDPFlags; /*0x26 */
+ U16 EEDPBlockSize; /*0x28 */
+ U16 Reserved6; /*0x2A */
+ U32 SecondaryReferenceTag; /*0x2C */
+ U16 SecondaryApplicationTag; /*0x30 */
+ U16 ApplicationTagTranslationMask; /*0x32 */
+ U8 LUN[8]; /*0x34 */
+ U32 Control; /*0x3C */
+ MPI25_SCSI_IO_CDB_UNION CDB; /*0x40 */
+
+#ifdef MPI25_SCSI_IO_VENDOR_UNIQUE_REGION /*typically this is left undefined */
+ MPI25_SCSI_IO_VENDOR_UNIQUE VendorRegion;
+#endif
+
+ MPI25_SGE_IO_UNION SGL; /*0x60 */
+
+} MPI25_SCSI_IO_REQUEST, *PTR_MPI25_SCSI_IO_REQUEST,
+ Mpi25SCSIIORequest_t, *pMpi25SCSIIORequest_t;
+
+/*use MPI2_SCSIIO_MSGFLAGS_ defines for the MsgFlags field */
+
+/*Defines for the DMAFlags field
+ * Each setting affects 4 SGLS, from SGL0 to SGL3.
+ * D = Data
+ * C = Cache DIF
+ * I = Interleaved
+ * H = Host DIF
+ */
+#define MPI25_SCSIIO_DMAFLAGS_OP_MASK (0x0F)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_D_D (0x00)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_D_C (0x01)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_D_I (0x02)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_C_C (0x03)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_C_I (0x04)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_D_I_I (0x05)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_C_C_C (0x06)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_C_C_I (0x07)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_C_I_I (0x08)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_I_I_I (0x09)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_D_D (0x0A)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_D_C (0x0B)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_D_I (0x0C)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_C_C (0x0D)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_C_I (0x0E)
+#define MPI25_SCSIIO_DMAFLAGS_OP_D_H_I_I (0x0F)
+
+/*number of SGLOffset fields */
+#define MPI25_SCSIIO_NUM_SGLOFFSETS (4)
+
+/*defines for the IoFlags field */
+#define MPI25_SCSIIO_IOFLAGS_IO_PATH_MASK (0xC000)
+#define MPI25_SCSIIO_IOFLAGS_NORMAL_PATH (0x0000)
+#define MPI25_SCSIIO_IOFLAGS_FAST_PATH (0x4000)
+
+#define MPI25_SCSIIO_IOFLAGS_LARGE_CDB (0x1000)
+#define MPI25_SCSIIO_IOFLAGS_BIDIRECTIONAL (0x0800)
+#define MPI25_SCSIIO_IOFLAGS_CDBLENGTH_MASK (0x01FF)
+
+/*MPI v2.5 defines for the EEDPFlags bits */
+/*use MPI2_SCSIIO_EEDPFLAGS_ defines for the other EEDPFlags bits */
+#define MPI25_SCSIIO_EEDPFLAGS_ESCAPE_MODE_MASK (0x00C0)
+#define MPI25_SCSIIO_EEDPFLAGS_COMPATIBLE_MODE (0x0000)
+#define MPI25_SCSIIO_EEDPFLAGS_DO_NOT_DISABLE_MODE (0x0040)
+#define MPI25_SCSIIO_EEDPFLAGS_APPTAG_DISABLE_MODE (0x0080)
+#define MPI25_SCSIIO_EEDPFLAGS_APPTAG_REFTAG_DISABLE_MODE (0x00C0)
+
+#define MPI25_SCSIIO_EEDPFLAGS_HOST_GUARD_METHOD_MASK (0x0030)
+#define MPI25_SCSIIO_EEDPFLAGS_T10_CRC_HOST_GUARD (0x0000)
+#define MPI25_SCSIIO_EEDPFLAGS_IP_CHKSUM_HOST_GUARD (0x0010)
+
+/*use MPI2_LUN_ defines from mpi2.h for the LUN field */
+
+/*use MPI2_SCSIIO_CONTROL_ defines for the Control field */
+
+/*NOTE: The SCSI IO Reply is nearly the same for MPI 2.0 and MPI 2.5, so
+ * MPI2_SCSI_IO_REPLY is used for both.
+ */
+
+/*SCSI IO Error Reply Message */
+typedef struct _MPI2_SCSI_IO_REPLY {
+ U16 DevHandle; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved1; /*0x04 */
+ U8 Reserved2; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+ U8 SCSIStatus; /*0x0C */
+ U8 SCSIState; /*0x0D */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U32 TransferCount; /*0x14 */
+ U32 SenseCount; /*0x18 */
+ U32 ResponseInfo; /*0x1C */
+ U16 TaskTag; /*0x20 */
+ U16 Reserved4; /*0x22 */
+ U32 BidirectionalTransferCount; /*0x24 */
+ U32 EEDPErrorOffset; /*0x28 *//*MPI 2.5 only; Reserved in MPI 2.0*/
+ U32 Reserved6; /*0x2C */
+} MPI2_SCSI_IO_REPLY, *PTR_MPI2_SCSI_IO_REPLY,
+ Mpi2SCSIIOReply_t, *pMpi2SCSIIOReply_t;
+
+/*SCSI IO Reply SCSIStatus values (SAM-4 status codes) */
+
+#define MPI2_SCSI_STATUS_GOOD (0x00)
+#define MPI2_SCSI_STATUS_CHECK_CONDITION (0x02)
+#define MPI2_SCSI_STATUS_CONDITION_MET (0x04)
+#define MPI2_SCSI_STATUS_BUSY (0x08)
+#define MPI2_SCSI_STATUS_INTERMEDIATE (0x10)
+#define MPI2_SCSI_STATUS_INTERMEDIATE_CONDMET (0x14)
+#define MPI2_SCSI_STATUS_RESERVATION_CONFLICT (0x18)
+#define MPI2_SCSI_STATUS_COMMAND_TERMINATED (0x22) /*obsolete */
+#define MPI2_SCSI_STATUS_TASK_SET_FULL (0x28)
+#define MPI2_SCSI_STATUS_ACA_ACTIVE (0x30)
+#define MPI2_SCSI_STATUS_TASK_ABORTED (0x40)
+
+/*SCSI IO Reply SCSIState flags */
+
+#define MPI2_SCSI_STATE_RESPONSE_INFO_VALID (0x10)
+#define MPI2_SCSI_STATE_TERMINATED (0x08)
+#define MPI2_SCSI_STATE_NO_SCSI_STATUS (0x04)
+#define MPI2_SCSI_STATE_AUTOSENSE_FAILED (0x02)
+#define MPI2_SCSI_STATE_AUTOSENSE_VALID (0x01)
+
+/*masks and shifts for the ResponseInfo field */
+
+#define MPI2_SCSI_RI_MASK_REASONCODE (0x000000FF)
+#define MPI2_SCSI_RI_SHIFT_REASONCODE (0)
+
+#define MPI2_SCSI_TASKTAG_UNKNOWN (0xFFFF)
+
+/****************************************************************************
+* SCSI Task Management messages
+****************************************************************************/
+
+/*SCSI Task Management Request Message */
+typedef struct _MPI2_SCSI_TASK_MANAGE_REQUEST {
+ U16 DevHandle; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U8 Reserved1; /*0x04 */
+ U8 TaskType; /*0x05 */
+ U8 Reserved2; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+ U8 LUN[8]; /*0x0C */
+ U32 Reserved4[7]; /*0x14 */
+ U16 TaskMID; /*0x30 */
+ U16 Reserved5; /*0x32 */
+} MPI2_SCSI_TASK_MANAGE_REQUEST,
+ *PTR_MPI2_SCSI_TASK_MANAGE_REQUEST,
+ Mpi2SCSITaskManagementRequest_t,
+ *pMpi2SCSITaskManagementRequest_t;
+
+/*TaskType values */
+
+#define MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK (0x01)
+#define MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET (0x02)
+#define MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET (0x03)
+#define MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET (0x05)
+#define MPI2_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET (0x06)
+#define MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK (0x07)
+#define MPI2_SCSITASKMGMT_TASKTYPE_CLR_ACA (0x08)
+#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_TASK_SET (0x09)
+#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_ASYNC_EVENT (0x0A)
+
+/*obsolete TaskType name */
+#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_UNIT_ATTENTION \
+ (MPI2_SCSITASKMGMT_TASKTYPE_QRY_ASYNC_EVENT)
+
+/*MsgFlags bits */
+
+#define MPI2_SCSITASKMGMT_MSGFLAGS_MASK_TARGET_RESET (0x18)
+#define MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET (0x00)
+#define MPI2_SCSITASKMGMT_MSGFLAGS_NEXUS_RESET_SRST (0x08)
+#define MPI2_SCSITASKMGMT_MSGFLAGS_SAS_HARD_LINK_RESET (0x10)
+
+#define MPI2_SCSITASKMGMT_MSGFLAGS_DO_NOT_SEND_TASK_IU (0x01)
+
+/*SCSI Task Management Reply Message */
+typedef struct _MPI2_SCSI_TASK_MANAGE_REPLY {
+ U16 DevHandle; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U8 ResponseCode; /*0x04 */
+ U8 TaskType; /*0x05 */
+ U8 Reserved1; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U16 Reserved3; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U32 TerminationCount; /*0x14 */
+ U32 ResponseInfo; /*0x18 */
+} MPI2_SCSI_TASK_MANAGE_REPLY,
+ *PTR_MPI2_SCSI_TASK_MANAGE_REPLY,
+ Mpi2SCSITaskManagementReply_t, *pMpi2SCSIManagementReply_t;
+
+/*ResponseCode values */
+
+#define MPI2_SCSITASKMGMT_RSP_TM_COMPLETE (0x00)
+#define MPI2_SCSITASKMGMT_RSP_INVALID_FRAME (0x02)
+#define MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED (0x04)
+#define MPI2_SCSITASKMGMT_RSP_TM_FAILED (0x05)
+#define MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED (0x08)
+#define MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN (0x09)
+#define MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG (0x0A)
+#define MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC (0x80)
+
+/*masks and shifts for the ResponseInfo field */
+
+#define MPI2_SCSITASKMGMT_RI_MASK_REASONCODE (0x000000FF)
+#define MPI2_SCSITASKMGMT_RI_SHIFT_REASONCODE (0)
+#define MPI2_SCSITASKMGMT_RI_MASK_ARI2 (0x0000FF00)
+#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI2 (8)
+#define MPI2_SCSITASKMGMT_RI_MASK_ARI1 (0x00FF0000)
+#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI1 (16)
+#define MPI2_SCSITASKMGMT_RI_MASK_ARI0 (0xFF000000)
+#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI0 (24)
+
+/****************************************************************************
+* SCSI Enclosure Processor messages
+****************************************************************************/
+
+/*SCSI Enclosure Processor Request Message */
+typedef struct _MPI2_SEP_REQUEST {
+ U16 DevHandle; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U8 Action; /*0x04 */
+ U8 Flags; /*0x05 */
+ U8 Reserved1; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U32 SlotStatus; /*0x0C */
+ U32 Reserved3; /*0x10 */
+ U32 Reserved4; /*0x14 */
+ U32 Reserved5; /*0x18 */
+ U16 Slot; /*0x1C */
+ U16 EnclosureHandle; /*0x1E */
+} MPI2_SEP_REQUEST, *PTR_MPI2_SEP_REQUEST,
+ Mpi2SepRequest_t, *pMpi2SepRequest_t;
+
+/*Action defines */
+#define MPI2_SEP_REQ_ACTION_WRITE_STATUS (0x00)
+#define MPI2_SEP_REQ_ACTION_READ_STATUS (0x01)
+
+/*Flags defines */
+#define MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS (0x00)
+#define MPI2_SEP_REQ_FLAGS_ENCLOSURE_SLOT_ADDRESS (0x01)
+
+/*SlotStatus defines */
+#define MPI2_SEP_REQ_SLOTSTATUS_REQUEST_REMOVE (0x00040000)
+#define MPI2_SEP_REQ_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000)
+#define MPI2_SEP_REQ_SLOTSTATUS_REBUILD_STOPPED (0x00000200)
+#define MPI2_SEP_REQ_SLOTSTATUS_HOT_SPARE (0x00000100)
+#define MPI2_SEP_REQ_SLOTSTATUS_UNCONFIGURED (0x00000080)
+#define MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT (0x00000040)
+#define MPI2_SEP_REQ_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010)
+#define MPI2_SEP_REQ_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008)
+#define MPI2_SEP_REQ_SLOTSTATUS_DEV_REBUILDING (0x00000004)
+#define MPI2_SEP_REQ_SLOTSTATUS_DEV_FAULTY (0x00000002)
+#define MPI2_SEP_REQ_SLOTSTATUS_NO_ERROR (0x00000001)
+
+/*SCSI Enclosure Processor Reply Message */
+typedef struct _MPI2_SEP_REPLY {
+ U16 DevHandle; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U8 Action; /*0x04 */
+ U8 Flags; /*0x05 */
+ U8 Reserved1; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U16 Reserved3; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U32 SlotStatus; /*0x14 */
+ U32 Reserved4; /*0x18 */
+ U16 Slot; /*0x1C */
+ U16 EnclosureHandle; /*0x1E */
+} MPI2_SEP_REPLY, *PTR_MPI2_SEP_REPLY,
+ Mpi2SepReply_t, *pMpi2SepReply_t;
+
+/*SlotStatus defines */
+#define MPI2_SEP_REPLY_SLOTSTATUS_REMOVE_READY (0x00040000)
+#define MPI2_SEP_REPLY_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000)
+#define MPI2_SEP_REPLY_SLOTSTATUS_REBUILD_STOPPED (0x00000200)
+#define MPI2_SEP_REPLY_SLOTSTATUS_HOT_SPARE (0x00000100)
+#define MPI2_SEP_REPLY_SLOTSTATUS_UNCONFIGURED (0x00000080)
+#define MPI2_SEP_REPLY_SLOTSTATUS_PREDICTED_FAULT (0x00000040)
+#define MPI2_SEP_REPLY_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010)
+#define MPI2_SEP_REPLY_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008)
+#define MPI2_SEP_REPLY_SLOTSTATUS_DEV_REBUILDING (0x00000004)
+#define MPI2_SEP_REPLY_SLOTSTATUS_DEV_FAULTY (0x00000002)
+#define MPI2_SEP_REPLY_SLOTSTATUS_NO_ERROR (0x00000001)
+
+#endif
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h
new file mode 100644
index 00000000000..0de425d8fd7
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h
@@ -0,0 +1,1665 @@
+/*
+ * Copyright (c) 2000-2012 LSI Corporation.
+ *
+ *
+ * Name: mpi2_ioc.h
+ * Title: MPI IOC, Port, Event, FW Download, and FW Upload messages
+ * Creation Date: October 11, 2006
+ *
+ * mpi2_ioc.h Version: 02.00.21
+ *
+ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25
+ * prefix are for use only on MPI v2.5 products, and must not be used
+ * with MPI v2.0 products. Unless otherwise noted, names beginning with
+ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products.
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 06-04-07 02.00.01 In IOCFacts Reply structure, renamed MaxDevices to
+ * MaxTargets.
+ * Added TotalImageSize field to FWDownload Request.
+ * Added reserved words to FWUpload Request.
+ * 06-26-07 02.00.02 Added IR Configuration Change List Event.
+ * 08-31-07 02.00.03 Removed SystemReplyQueueDepth field from the IOCInit
+ * request and replaced it with
+ * ReplyDescriptorPostQueueDepth and ReplyFreeQueueDepth.
+ * Replaced the MinReplyQueueDepth field of the IOCFacts
+ * reply with MaxReplyDescriptorPostQueueDepth.
+ * Added MPI2_RDPQ_DEPTH_MIN define to specify the minimum
+ * depth for the Reply Descriptor Post Queue.
+ * Added SASAddress field to Initiator Device Table
+ * Overflow Event data.
+ * 10-31-07 02.00.04 Added ReasonCode MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING
+ * for SAS Initiator Device Status Change Event data.
+ * Modified Reason Code defines for SAS Topology Change
+ * List Event data, including adding a bit for PHY Vacant
+ * status, and adding a mask for the Reason Code.
+ * Added define for
+ * MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING.
+ * Added define for MPI2_EXT_IMAGE_TYPE_MEGARAID.
+ * 12-18-07 02.00.05 Added Boot Status defines for the IOCExceptions field of
+ * the IOCFacts Reply.
+ * Removed MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define.
+ * Moved MPI2_VERSION_UNION to mpi2.h.
+ * Changed MPI2_EVENT_NOTIFICATION_REQUEST to use masks
+ * instead of enables, and added SASBroadcastPrimitiveMasks
+ * field.
+ * Added Log Entry Added Event and related structure.
+ * 02-29-08 02.00.06 Added define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID.
+ * Removed define MPI2_IOCFACTS_PROTOCOL_SMP_TARGET.
+ * Added MaxVolumes and MaxPersistentEntries fields to
+ * IOCFacts reply.
+ * Added ProtocalFlags and IOCCapabilities fields to
+ * MPI2_FW_IMAGE_HEADER.
+ * Removed MPI2_PORTENABLE_FLAGS_ENABLE_SINGLE_PORT.
+ * 03-03-08 02.00.07 Fixed MPI2_FW_IMAGE_HEADER by changing Reserved26 to
+ * a U16 (from a U32).
+ * Removed extra 's' from EventMasks name.
+ * 06-27-08 02.00.08 Fixed an offset in a comment.
+ * 10-02-08 02.00.09 Removed SystemReplyFrameSize from MPI2_IOC_INIT_REQUEST.
+ * Removed CurReplyFrameSize from MPI2_IOC_FACTS_REPLY and
+ * renamed MinReplyFrameSize to ReplyFrameSize.
+ * Added MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX.
+ * Added two new RAIDOperation values for Integrated RAID
+ * Operations Status Event data.
+ * Added four new IR Configuration Change List Event data
+ * ReasonCode values.
+ * Added two new ReasonCode defines for SAS Device Status
+ * Change Event data.
+ * Added three new DiscoveryStatus bits for the SAS
+ * Discovery event data.
+ * Added Multiplexing Status Change bit to the PhyStatus
+ * field of the SAS Topology Change List event data.
+ * Removed define for MPI2_INIT_IMAGE_BOOTFLAGS_XMEMCOPY.
+ * BootFlags are now product-specific.
+ * Added defines for the indivdual signature bytes
+ * for MPI2_INIT_IMAGE_FOOTER.
+ * 01-19-09 02.00.10 Added MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY define.
+ * Added MPI2_EVENT_SAS_DISC_DS_DOWNSTREAM_INITIATOR
+ * define.
+ * Added MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE
+ * define.
+ * Removed MPI2_EVENT_SAS_DISC_DS_SATA_INIT_FAILURE define.
+ * 05-06-09 02.00.11 Added MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR define.
+ * Added MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX define.
+ * Added two new reason codes for SAS Device Status Change
+ * Event.
+ * Added new event: SAS PHY Counter.
+ * 07-30-09 02.00.12 Added GPIO Interrupt event define and structure.
+ * Added MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define.
+ * Added new product id family for 2208.
+ * 10-28-09 02.00.13 Added HostMSIxVectors field to MPI2_IOC_INIT_REQUEST.
+ * Added MaxMSIxVectors field to MPI2_IOC_FACTS_REPLY.
+ * Added MinDevHandle field to MPI2_IOC_FACTS_REPLY.
+ * Added MPI2_IOCFACTS_CAPABILITY_HOST_BASED_DISCOVERY.
+ * Added MPI2_EVENT_HOST_BASED_DISCOVERY_PHY define.
+ * Added MPI2_EVENT_SAS_TOPO_ES_NO_EXPANDER define.
+ * Added Host Based Discovery Phy Event data.
+ * Added defines for ProductID Product field
+ * (MPI2_FW_HEADER_PID_).
+ * Modified values for SAS ProductID Family
+ * (MPI2_FW_HEADER_PID_FAMILY_).
+ * 02-10-10 02.00.14 Added SAS Quiesce Event structure and defines.
+ * Added PowerManagementControl Request structures and
+ * defines.
+ * 05-12-10 02.00.15 Marked Task Set Full Event as obsolete.
+ * Added MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY define.
+ * 11-10-10 02.00.16 Added MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC.
+ * 02-23-11 02.00.17 Added SAS NOTIFY Primitive event, and added
+ * SASNotifyPrimitiveMasks field to
+ * MPI2_EVENT_NOTIFICATION_REQUEST.
+ * Added Temperature Threshold Event.
+ * Added Host Message Event.
+ * Added Send Host Message request and reply.
+ * 05-25-11 02.00.18 For Extended Image Header, added
+ * MPI2_EXT_IMAGE_TYPE_MIN_PRODUCT_SPECIFIC and
+ * MPI2_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC defines.
+ * Deprecated MPI2_EXT_IMAGE_TYPE_MAX define.
+ * 08-24-11 02.00.19 Added PhysicalPort field to
+ * MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE structure.
+ * Marked MPI2_PM_CONTROL_FEATURE_PCIE_LINK as obsolete.
+ * 11-18-11 02.00.20 Incorporating additions for MPI v2.5.
+ * 03-29-12 02.00.21 Added a product specific range to event values.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_IOC_H
+#define MPI2_IOC_H
+
+/*****************************************************************************
+*
+* IOC Messages
+*
+*****************************************************************************/
+
+/****************************************************************************
+* IOCInit message
+****************************************************************************/
+
+/*IOCInit Request message */
+typedef struct _MPI2_IOC_INIT_REQUEST {
+ U8 WhoInit; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 MsgVersion; /*0x0C */
+ U16 HeaderVersion; /*0x0E */
+ U32 Reserved5; /*0x10 */
+ U16 Reserved6; /*0x14 */
+ U8 Reserved7; /*0x16 */
+ U8 HostMSIxVectors; /*0x17 */
+ U16 Reserved8; /*0x18 */
+ U16 SystemRequestFrameSize; /*0x1A */
+ U16 ReplyDescriptorPostQueueDepth; /*0x1C */
+ U16 ReplyFreeQueueDepth; /*0x1E */
+ U32 SenseBufferAddressHigh; /*0x20 */
+ U32 SystemReplyAddressHigh; /*0x24 */
+ U64 SystemRequestFrameBaseAddress; /*0x28 */
+ U64 ReplyDescriptorPostQueueAddress; /*0x30 */
+ U64 ReplyFreeQueueAddress; /*0x38 */
+ U64 TimeStamp; /*0x40 */
+} MPI2_IOC_INIT_REQUEST, *PTR_MPI2_IOC_INIT_REQUEST,
+ Mpi2IOCInitRequest_t, *pMpi2IOCInitRequest_t;
+
+/*WhoInit values */
+#define MPI2_WHOINIT_NOT_INITIALIZED (0x00)
+#define MPI2_WHOINIT_SYSTEM_BIOS (0x01)
+#define MPI2_WHOINIT_ROM_BIOS (0x02)
+#define MPI2_WHOINIT_PCI_PEER (0x03)
+#define MPI2_WHOINIT_HOST_DRIVER (0x04)
+#define MPI2_WHOINIT_MANUFACTURER (0x05)
+
+/*MsgVersion */
+#define MPI2_IOCINIT_MSGVERSION_MAJOR_MASK (0xFF00)
+#define MPI2_IOCINIT_MSGVERSION_MAJOR_SHIFT (8)
+#define MPI2_IOCINIT_MSGVERSION_MINOR_MASK (0x00FF)
+#define MPI2_IOCINIT_MSGVERSION_MINOR_SHIFT (0)
+
+/*HeaderVersion */
+#define MPI2_IOCINIT_HDRVERSION_UNIT_MASK (0xFF00)
+#define MPI2_IOCINIT_HDRVERSION_UNIT_SHIFT (8)
+#define MPI2_IOCINIT_HDRVERSION_DEV_MASK (0x00FF)
+#define MPI2_IOCINIT_HDRVERSION_DEV_SHIFT (0)
+
+/*minimum depth for the Reply Descriptor Post Queue */
+#define MPI2_RDPQ_DEPTH_MIN (16)
+
+/*IOCInit Reply message */
+typedef struct _MPI2_IOC_INIT_REPLY {
+ U8 WhoInit; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_IOC_INIT_REPLY, *PTR_MPI2_IOC_INIT_REPLY,
+ Mpi2IOCInitReply_t, *pMpi2IOCInitReply_t;
+
+/****************************************************************************
+* IOCFacts message
+****************************************************************************/
+
+/*IOCFacts Request message */
+typedef struct _MPI2_IOC_FACTS_REQUEST {
+ U16 Reserved1; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+} MPI2_IOC_FACTS_REQUEST, *PTR_MPI2_IOC_FACTS_REQUEST,
+ Mpi2IOCFactsRequest_t, *pMpi2IOCFactsRequest_t;
+
+/*IOCFacts Reply message */
+typedef struct _MPI2_IOC_FACTS_REPLY {
+ U16 MsgVersion; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 HeaderVersion; /*0x04 */
+ U8 IOCNumber; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U16 IOCExceptions; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U8 MaxChainDepth; /*0x14 */
+ U8 WhoInit; /*0x15 */
+ U8 NumberOfPorts; /*0x16 */
+ U8 MaxMSIxVectors; /*0x17 */
+ U16 RequestCredit; /*0x18 */
+ U16 ProductID; /*0x1A */
+ U32 IOCCapabilities; /*0x1C */
+ MPI2_VERSION_UNION FWVersion; /*0x20 */
+ U16 IOCRequestFrameSize; /*0x24 */
+ U16 IOCMaxChainSegmentSize; /*0x26 */
+ U16 MaxInitiators; /*0x28 */
+ U16 MaxTargets; /*0x2A */
+ U16 MaxSasExpanders; /*0x2C */
+ U16 MaxEnclosures; /*0x2E */
+ U16 ProtocolFlags; /*0x30 */
+ U16 HighPriorityCredit; /*0x32 */
+ U16 MaxReplyDescriptorPostQueueDepth; /*0x34 */
+ U8 ReplyFrameSize; /*0x36 */
+ U8 MaxVolumes; /*0x37 */
+ U16 MaxDevHandle; /*0x38 */
+ U16 MaxPersistentEntries; /*0x3A */
+ U16 MinDevHandle; /*0x3C */
+ U16 Reserved4; /*0x3E */
+} MPI2_IOC_FACTS_REPLY, *PTR_MPI2_IOC_FACTS_REPLY,
+ Mpi2IOCFactsReply_t, *pMpi2IOCFactsReply_t;
+
+/*MsgVersion */
+#define MPI2_IOCFACTS_MSGVERSION_MAJOR_MASK (0xFF00)
+#define MPI2_IOCFACTS_MSGVERSION_MAJOR_SHIFT (8)
+#define MPI2_IOCFACTS_MSGVERSION_MINOR_MASK (0x00FF)
+#define MPI2_IOCFACTS_MSGVERSION_MINOR_SHIFT (0)
+
+/*HeaderVersion */
+#define MPI2_IOCFACTS_HDRVERSION_UNIT_MASK (0xFF00)
+#define MPI2_IOCFACTS_HDRVERSION_UNIT_SHIFT (8)
+#define MPI2_IOCFACTS_HDRVERSION_DEV_MASK (0x00FF)
+#define MPI2_IOCFACTS_HDRVERSION_DEV_SHIFT (0)
+
+/*IOCExceptions */
+#define MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX (0x0100)
+
+#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_MASK (0x00E0)
+#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_GOOD (0x0000)
+#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_BACKUP (0x0020)
+#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_RESTORED (0x0040)
+#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_CORRUPT_BACKUP (0x0060)
+
+#define MPI2_IOCFACTS_EXCEPT_METADATA_UNSUPPORTED (0x0010)
+#define MPI2_IOCFACTS_EXCEPT_MANUFACT_CHECKSUM_FAIL (0x0008)
+#define MPI2_IOCFACTS_EXCEPT_FW_CHECKSUM_FAIL (0x0004)
+#define MPI2_IOCFACTS_EXCEPT_RAID_CONFIG_INVALID (0x0002)
+#define MPI2_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL (0x0001)
+
+/*defines for WhoInit field are after the IOCInit Request */
+
+/*ProductID field uses MPI2_FW_HEADER_PID_ */
+
+/*IOCCapabilities */
+#define MPI25_IOCFACTS_CAPABILITY_FAST_PATH_CAPABLE (0x00020000)
+#define MPI2_IOCFACTS_CAPABILITY_HOST_BASED_DISCOVERY (0x00010000)
+#define MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX (0x00008000)
+#define MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR (0x00004000)
+#define MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY (0x00002000)
+#define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID (0x00001000)
+#define MPI2_IOCFACTS_CAPABILITY_TLR (0x00000800)
+#define MPI2_IOCFACTS_CAPABILITY_MULTICAST (0x00000100)
+#define MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET (0x00000080)
+#define MPI2_IOCFACTS_CAPABILITY_EEDP (0x00000040)
+#define MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER (0x00000020)
+#define MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER (0x00000010)
+#define MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER (0x00000008)
+#define MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING (0x00000004)
+
+/*ProtocolFlags */
+#define MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET (0x0001)
+#define MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR (0x0002)
+
+/****************************************************************************
+* PortFacts message
+****************************************************************************/
+
+/*PortFacts Request message */
+typedef struct _MPI2_PORT_FACTS_REQUEST {
+ U16 Reserved1; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 PortNumber; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+} MPI2_PORT_FACTS_REQUEST, *PTR_MPI2_PORT_FACTS_REQUEST,
+ Mpi2PortFactsRequest_t, *pMpi2PortFactsRequest_t;
+
+/*PortFacts Reply message */
+typedef struct _MPI2_PORT_FACTS_REPLY {
+ U16 Reserved1; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 PortNumber; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+ U16 Reserved4; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U8 Reserved5; /*0x14 */
+ U8 PortType; /*0x15 */
+ U16 Reserved6; /*0x16 */
+ U16 MaxPostedCmdBuffers; /*0x18 */
+ U16 Reserved7; /*0x1A */
+} MPI2_PORT_FACTS_REPLY, *PTR_MPI2_PORT_FACTS_REPLY,
+ Mpi2PortFactsReply_t, *pMpi2PortFactsReply_t;
+
+/*PortType values */
+#define MPI2_PORTFACTS_PORTTYPE_INACTIVE (0x00)
+#define MPI2_PORTFACTS_PORTTYPE_FC (0x10)
+#define MPI2_PORTFACTS_PORTTYPE_ISCSI (0x20)
+#define MPI2_PORTFACTS_PORTTYPE_SAS_PHYSICAL (0x30)
+#define MPI2_PORTFACTS_PORTTYPE_SAS_VIRTUAL (0x31)
+
+/****************************************************************************
+* PortEnable message
+****************************************************************************/
+
+/*PortEnable Request message */
+typedef struct _MPI2_PORT_ENABLE_REQUEST {
+ U16 Reserved1; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U8 Reserved2; /*0x04 */
+ U8 PortFlags; /*0x05 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+} MPI2_PORT_ENABLE_REQUEST, *PTR_MPI2_PORT_ENABLE_REQUEST,
+ Mpi2PortEnableRequest_t, *pMpi2PortEnableRequest_t;
+
+/*PortEnable Reply message */
+typedef struct _MPI2_PORT_ENABLE_REPLY {
+ U16 Reserved1; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U8 Reserved2; /*0x04 */
+ U8 PortFlags; /*0x05 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_PORT_ENABLE_REPLY, *PTR_MPI2_PORT_ENABLE_REPLY,
+ Mpi2PortEnableReply_t, *pMpi2PortEnableReply_t;
+
+/****************************************************************************
+* EventNotification message
+****************************************************************************/
+
+/*EventNotification Request message */
+#define MPI2_EVENT_NOTIFY_EVENTMASK_WORDS (4)
+
+typedef struct _MPI2_EVENT_NOTIFICATION_REQUEST {
+ U16 Reserved1; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U32 Reserved5; /*0x0C */
+ U32 Reserved6; /*0x10 */
+ U32 EventMasks[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS]; /*0x14 */
+ U16 SASBroadcastPrimitiveMasks; /*0x24 */
+ U16 SASNotifyPrimitiveMasks; /*0x26 */
+ U32 Reserved8; /*0x28 */
+} MPI2_EVENT_NOTIFICATION_REQUEST,
+ *PTR_MPI2_EVENT_NOTIFICATION_REQUEST,
+ Mpi2EventNotificationRequest_t,
+ *pMpi2EventNotificationRequest_t;
+
+/*EventNotification Reply message */
+typedef struct _MPI2_EVENT_NOTIFICATION_REPLY {
+ U16 EventDataLength; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved1; /*0x04 */
+ U8 AckRequired; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U16 Reserved3; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U16 Event; /*0x14 */
+ U16 Reserved4; /*0x16 */
+ U32 EventContext; /*0x18 */
+ U32 EventData[1]; /*0x1C */
+} MPI2_EVENT_NOTIFICATION_REPLY, *PTR_MPI2_EVENT_NOTIFICATION_REPLY,
+ Mpi2EventNotificationReply_t,
+ *pMpi2EventNotificationReply_t;
+
+/*AckRequired */
+#define MPI2_EVENT_NOTIFICATION_ACK_NOT_REQUIRED (0x00)
+#define MPI2_EVENT_NOTIFICATION_ACK_REQUIRED (0x01)
+
+/*Event */
+#define MPI2_EVENT_LOG_DATA (0x0001)
+#define MPI2_EVENT_STATE_CHANGE (0x0002)
+#define MPI2_EVENT_HARD_RESET_RECEIVED (0x0005)
+#define MPI2_EVENT_EVENT_CHANGE (0x000A)
+#define MPI2_EVENT_TASK_SET_FULL (0x000E) /*obsolete */
+#define MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE (0x000F)
+#define MPI2_EVENT_IR_OPERATION_STATUS (0x0014)
+#define MPI2_EVENT_SAS_DISCOVERY (0x0016)
+#define MPI2_EVENT_SAS_BROADCAST_PRIMITIVE (0x0017)
+#define MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE (0x0018)
+#define MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW (0x0019)
+#define MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST (0x001C)
+#define MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE (0x001D)
+#define MPI2_EVENT_IR_VOLUME (0x001E)
+#define MPI2_EVENT_IR_PHYSICAL_DISK (0x001F)
+#define MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST (0x0020)
+#define MPI2_EVENT_LOG_ENTRY_ADDED (0x0021)
+#define MPI2_EVENT_SAS_PHY_COUNTER (0x0022)
+#define MPI2_EVENT_GPIO_INTERRUPT (0x0023)
+#define MPI2_EVENT_HOST_BASED_DISCOVERY_PHY (0x0024)
+#define MPI2_EVENT_SAS_QUIESCE (0x0025)
+#define MPI2_EVENT_SAS_NOTIFY_PRIMITIVE (0x0026)
+#define MPI2_EVENT_TEMP_THRESHOLD (0x0027)
+#define MPI2_EVENT_HOST_MESSAGE (0x0028)
+#define MPI2_EVENT_POWER_PERFORMANCE_CHANGE (0x0029)
+#define MPI2_EVENT_MIN_PRODUCT_SPECIFIC (0x006E)
+#define MPI2_EVENT_MAX_PRODUCT_SPECIFIC (0x007F)
+
+/*Log Entry Added Event data */
+
+/*the following structure matches MPI2_LOG_0_ENTRY in mpi2_cnfg.h */
+#define MPI2_EVENT_DATA_LOG_DATA_LENGTH (0x1C)
+
+typedef struct _MPI2_EVENT_DATA_LOG_ENTRY_ADDED {
+ U64 TimeStamp; /*0x00 */
+ U32 Reserved1; /*0x08 */
+ U16 LogSequence; /*0x0C */
+ U16 LogEntryQualifier; /*0x0E */
+ U8 VP_ID; /*0x10 */
+ U8 VF_ID; /*0x11 */
+ U16 Reserved2; /*0x12 */
+ U8 LogData[MPI2_EVENT_DATA_LOG_DATA_LENGTH]; /*0x14 */
+} MPI2_EVENT_DATA_LOG_ENTRY_ADDED,
+ *PTR_MPI2_EVENT_DATA_LOG_ENTRY_ADDED,
+ Mpi2EventDataLogEntryAdded_t,
+ *pMpi2EventDataLogEntryAdded_t;
+
+/*GPIO Interrupt Event data */
+
+typedef struct _MPI2_EVENT_DATA_GPIO_INTERRUPT {
+ U8 GPIONum; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+} MPI2_EVENT_DATA_GPIO_INTERRUPT,
+ *PTR_MPI2_EVENT_DATA_GPIO_INTERRUPT,
+ Mpi2EventDataGpioInterrupt_t,
+ *pMpi2EventDataGpioInterrupt_t;
+
+/*Temperature Threshold Event data */
+
+typedef struct _MPI2_EVENT_DATA_TEMPERATURE {
+ U16 Status; /*0x00 */
+ U8 SensorNum; /*0x02 */
+ U8 Reserved1; /*0x03 */
+ U16 CurrentTemperature; /*0x04 */
+ U16 Reserved2; /*0x06 */
+ U32 Reserved3; /*0x08 */
+ U32 Reserved4; /*0x0C */
+} MPI2_EVENT_DATA_TEMPERATURE,
+ *PTR_MPI2_EVENT_DATA_TEMPERATURE,
+ Mpi2EventDataTemperature_t, *pMpi2EventDataTemperature_t;
+
+/*Temperature Threshold Event data Status bits */
+#define MPI2_EVENT_TEMPERATURE3_EXCEEDED (0x0008)
+#define MPI2_EVENT_TEMPERATURE2_EXCEEDED (0x0004)
+#define MPI2_EVENT_TEMPERATURE1_EXCEEDED (0x0002)
+#define MPI2_EVENT_TEMPERATURE0_EXCEEDED (0x0001)
+
+/*Host Message Event data */
+
+typedef struct _MPI2_EVENT_DATA_HOST_MESSAGE {
+ U8 SourceVF_ID; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U32 Reserved3; /*0x04 */
+ U32 HostData[1]; /*0x08 */
+} MPI2_EVENT_DATA_HOST_MESSAGE, *PTR_MPI2_EVENT_DATA_HOST_MESSAGE,
+ Mpi2EventDataHostMessage_t, *pMpi2EventDataHostMessage_t;
+
+/*Power Performance Change Event */
+
+typedef struct _MPI2_EVENT_DATA_POWER_PERF_CHANGE {
+ U8 CurrentPowerMode; /*0x00 */
+ U8 PreviousPowerMode; /*0x01 */
+ U16 Reserved1; /*0x02 */
+} MPI2_EVENT_DATA_POWER_PERF_CHANGE,
+ *PTR_MPI2_EVENT_DATA_POWER_PERF_CHANGE,
+ Mpi2EventDataPowerPerfChange_t,
+ *pMpi2EventDataPowerPerfChange_t;
+
+/*defines for CurrentPowerMode and PreviousPowerMode fields */
+#define MPI2_EVENT_PM_INIT_MASK (0xC0)
+#define MPI2_EVENT_PM_INIT_UNAVAILABLE (0x00)
+#define MPI2_EVENT_PM_INIT_HOST (0x40)
+#define MPI2_EVENT_PM_INIT_IO_UNIT (0x80)
+#define MPI2_EVENT_PM_INIT_PCIE_DPA (0xC0)
+
+#define MPI2_EVENT_PM_MODE_MASK (0x07)
+#define MPI2_EVENT_PM_MODE_UNAVAILABLE (0x00)
+#define MPI2_EVENT_PM_MODE_UNKNOWN (0x01)
+#define MPI2_EVENT_PM_MODE_FULL_POWER (0x04)
+#define MPI2_EVENT_PM_MODE_REDUCED_POWER (0x05)
+#define MPI2_EVENT_PM_MODE_STANDBY (0x06)
+
+/*Hard Reset Received Event data */
+
+typedef struct _MPI2_EVENT_DATA_HARD_RESET_RECEIVED {
+ U8 Reserved1; /*0x00 */
+ U8 Port; /*0x01 */
+ U16 Reserved2; /*0x02 */
+} MPI2_EVENT_DATA_HARD_RESET_RECEIVED,
+ *PTR_MPI2_EVENT_DATA_HARD_RESET_RECEIVED,
+ Mpi2EventDataHardResetReceived_t,
+ *pMpi2EventDataHardResetReceived_t;
+
+/*Task Set Full Event data */
+/* this event is obsolete */
+
+typedef struct _MPI2_EVENT_DATA_TASK_SET_FULL {
+ U16 DevHandle; /*0x00 */
+ U16 CurrentDepth; /*0x02 */
+} MPI2_EVENT_DATA_TASK_SET_FULL, *PTR_MPI2_EVENT_DATA_TASK_SET_FULL,
+ Mpi2EventDataTaskSetFull_t, *pMpi2EventDataTaskSetFull_t;
+
+/*SAS Device Status Change Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE {
+ U16 TaskTag; /*0x00 */
+ U8 ReasonCode; /*0x02 */
+ U8 PhysicalPort; /*0x03 */
+ U8 ASC; /*0x04 */
+ U8 ASCQ; /*0x05 */
+ U16 DevHandle; /*0x06 */
+ U32 Reserved2; /*0x08 */
+ U64 SASAddress; /*0x0C */
+ U8 LUN[8]; /*0x14 */
+} MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE,
+ *PTR_MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE,
+ Mpi2EventDataSasDeviceStatusChange_t,
+ *pMpi2EventDataSasDeviceStatusChange_t;
+
+/*SAS Device Status Change Event data ReasonCode values */
+#define MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA (0x05)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED (0x07)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET (0x08)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL (0x09)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL (0x0A)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL (0x0B)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL (0x0C)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION (0x0D)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET (0x0E)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL (0x0F)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE (0x10)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_EXPANDER_REDUCED_FUNCTIONALITY (0x11)
+#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_EXPANDER_REDUCED_FUNCTIONALITY (0x12)
+
+/*Integrated RAID Operation Status Event data */
+
+typedef struct _MPI2_EVENT_DATA_IR_OPERATION_STATUS {
+ U16 VolDevHandle; /*0x00 */
+ U16 Reserved1; /*0x02 */
+ U8 RAIDOperation; /*0x04 */
+ U8 PercentComplete; /*0x05 */
+ U16 Reserved2; /*0x06 */
+ U32 Resereved3; /*0x08 */
+} MPI2_EVENT_DATA_IR_OPERATION_STATUS,
+ *PTR_MPI2_EVENT_DATA_IR_OPERATION_STATUS,
+ Mpi2EventDataIrOperationStatus_t,
+ *pMpi2EventDataIrOperationStatus_t;
+
+/*Integrated RAID Operation Status Event data RAIDOperation values */
+#define MPI2_EVENT_IR_RAIDOP_RESYNC (0x00)
+#define MPI2_EVENT_IR_RAIDOP_ONLINE_CAP_EXPANSION (0x01)
+#define MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK (0x02)
+#define MPI2_EVENT_IR_RAIDOP_BACKGROUND_INIT (0x03)
+#define MPI2_EVENT_IR_RAIDOP_MAKE_DATA_CONSISTENT (0x04)
+
+/*Integrated RAID Volume Event data */
+
+typedef struct _MPI2_EVENT_DATA_IR_VOLUME {
+ U16 VolDevHandle; /*0x00 */
+ U8 ReasonCode; /*0x02 */
+ U8 Reserved1; /*0x03 */
+ U32 NewValue; /*0x04 */
+ U32 PreviousValue; /*0x08 */
+} MPI2_EVENT_DATA_IR_VOLUME, *PTR_MPI2_EVENT_DATA_IR_VOLUME,
+ Mpi2EventDataIrVolume_t, *pMpi2EventDataIrVolume_t;
+
+/*Integrated RAID Volume Event data ReasonCode values */
+#define MPI2_EVENT_IR_VOLUME_RC_SETTINGS_CHANGED (0x01)
+#define MPI2_EVENT_IR_VOLUME_RC_STATUS_FLAGS_CHANGED (0x02)
+#define MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED (0x03)
+
+/*Integrated RAID Physical Disk Event data */
+
+typedef struct _MPI2_EVENT_DATA_IR_PHYSICAL_DISK {
+ U16 Reserved1; /*0x00 */
+ U8 ReasonCode; /*0x02 */
+ U8 PhysDiskNum; /*0x03 */
+ U16 PhysDiskDevHandle; /*0x04 */
+ U16 Reserved2; /*0x06 */
+ U16 Slot; /*0x08 */
+ U16 EnclosureHandle; /*0x0A */
+ U32 NewValue; /*0x0C */
+ U32 PreviousValue; /*0x10 */
+} MPI2_EVENT_DATA_IR_PHYSICAL_DISK,
+ *PTR_MPI2_EVENT_DATA_IR_PHYSICAL_DISK,
+ Mpi2EventDataIrPhysicalDisk_t,
+ *pMpi2EventDataIrPhysicalDisk_t;
+
+/*Integrated RAID Physical Disk Event data ReasonCode values */
+#define MPI2_EVENT_IR_PHYSDISK_RC_SETTINGS_CHANGED (0x01)
+#define MPI2_EVENT_IR_PHYSDISK_RC_STATUS_FLAGS_CHANGED (0x02)
+#define MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED (0x03)
+
+/*Integrated RAID Configuration Change List Event data */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check NumElements at runtime.
+ */
+#ifndef MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT
+#define MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT (1)
+#endif
+
+typedef struct _MPI2_EVENT_IR_CONFIG_ELEMENT {
+ U16 ElementFlags; /*0x00 */
+ U16 VolDevHandle; /*0x02 */
+ U8 ReasonCode; /*0x04 */
+ U8 PhysDiskNum; /*0x05 */
+ U16 PhysDiskDevHandle; /*0x06 */
+} MPI2_EVENT_IR_CONFIG_ELEMENT, *PTR_MPI2_EVENT_IR_CONFIG_ELEMENT,
+ Mpi2EventIrConfigElement_t, *pMpi2EventIrConfigElement_t;
+
+/*IR Configuration Change List Event data ElementFlags values */
+#define MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK (0x000F)
+#define MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT (0x0000)
+#define MPI2_EVENT_IR_CHANGE_EFLAGS_VOLPHYSDISK_ELEMENT (0x0001)
+#define MPI2_EVENT_IR_CHANGE_EFLAGS_HOTSPARE_ELEMENT (0x0002)
+
+/*IR Configuration Change List Event data ReasonCode values */
+#define MPI2_EVENT_IR_CHANGE_RC_ADDED (0x01)
+#define MPI2_EVENT_IR_CHANGE_RC_REMOVED (0x02)
+#define MPI2_EVENT_IR_CHANGE_RC_NO_CHANGE (0x03)
+#define MPI2_EVENT_IR_CHANGE_RC_HIDE (0x04)
+#define MPI2_EVENT_IR_CHANGE_RC_UNHIDE (0x05)
+#define MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED (0x06)
+#define MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED (0x07)
+#define MPI2_EVENT_IR_CHANGE_RC_PD_CREATED (0x08)
+#define MPI2_EVENT_IR_CHANGE_RC_PD_DELETED (0x09)
+
+typedef struct _MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST {
+ U8 NumElements; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 Reserved2; /*0x02 */
+ U8 ConfigNum; /*0x03 */
+ U32 Flags; /*0x04 */
+ MPI2_EVENT_IR_CONFIG_ELEMENT
+ ConfigElement[MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT];/*0x08 */
+} MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST,
+ *PTR_MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST,
+ Mpi2EventDataIrConfigChangeList_t,
+ *pMpi2EventDataIrConfigChangeList_t;
+
+/*IR Configuration Change List Event data Flags values */
+#define MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG (0x00000001)
+
+/*SAS Discovery Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_DISCOVERY {
+ U8 Flags; /*0x00 */
+ U8 ReasonCode; /*0x01 */
+ U8 PhysicalPort; /*0x02 */
+ U8 Reserved1; /*0x03 */
+ U32 DiscoveryStatus; /*0x04 */
+} MPI2_EVENT_DATA_SAS_DISCOVERY,
+ *PTR_MPI2_EVENT_DATA_SAS_DISCOVERY,
+ Mpi2EventDataSasDiscovery_t, *pMpi2EventDataSasDiscovery_t;
+
+/*SAS Discovery Event data Flags values */
+#define MPI2_EVENT_SAS_DISC_DEVICE_CHANGE (0x02)
+#define MPI2_EVENT_SAS_DISC_IN_PROGRESS (0x01)
+
+/*SAS Discovery Event data ReasonCode values */
+#define MPI2_EVENT_SAS_DISC_RC_STARTED (0x01)
+#define MPI2_EVENT_SAS_DISC_RC_COMPLETED (0x02)
+
+/*SAS Discovery Event data DiscoveryStatus values */
+#define MPI2_EVENT_SAS_DISC_DS_MAX_ENCLOSURES_EXCEED (0x80000000)
+#define MPI2_EVENT_SAS_DISC_DS_MAX_EXPANDERS_EXCEED (0x40000000)
+#define MPI2_EVENT_SAS_DISC_DS_MAX_DEVICES_EXCEED (0x20000000)
+#define MPI2_EVENT_SAS_DISC_DS_MAX_TOPO_PHYS_EXCEED (0x10000000)
+#define MPI2_EVENT_SAS_DISC_DS_DOWNSTREAM_INITIATOR (0x08000000)
+#define MPI2_EVENT_SAS_DISC_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000)
+#define MPI2_EVENT_SAS_DISC_DS_EXP_MULTI_SUBTRACTIVE (0x00004000)
+#define MPI2_EVENT_SAS_DISC_DS_MULTI_PORT_DOMAIN (0x00002000)
+#define MPI2_EVENT_SAS_DISC_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000)
+#define MPI2_EVENT_SAS_DISC_DS_UNSUPPORTED_DEVICE (0x00000800)
+#define MPI2_EVENT_SAS_DISC_DS_TABLE_LINK (0x00000400)
+#define MPI2_EVENT_SAS_DISC_DS_SUBTRACTIVE_LINK (0x00000200)
+#define MPI2_EVENT_SAS_DISC_DS_SMP_CRC_ERROR (0x00000100)
+#define MPI2_EVENT_SAS_DISC_DS_SMP_FUNCTION_FAILED (0x00000080)
+#define MPI2_EVENT_SAS_DISC_DS_INDEX_NOT_EXIST (0x00000040)
+#define MPI2_EVENT_SAS_DISC_DS_OUT_ROUTE_ENTRIES (0x00000020)
+#define MPI2_EVENT_SAS_DISC_DS_SMP_TIMEOUT (0x00000010)
+#define MPI2_EVENT_SAS_DISC_DS_MULTIPLE_PORTS (0x00000004)
+#define MPI2_EVENT_SAS_DISC_DS_UNADDRESSABLE_DEVICE (0x00000002)
+#define MPI2_EVENT_SAS_DISC_DS_LOOP_DETECTED (0x00000001)
+
+/*SAS Broadcast Primitive Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE {
+ U8 PhyNum; /*0x00 */
+ U8 Port; /*0x01 */
+ U8 PortWidth; /*0x02 */
+ U8 Primitive; /*0x03 */
+} MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE,
+ *PTR_MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE,
+ Mpi2EventDataSasBroadcastPrimitive_t,
+ *pMpi2EventDataSasBroadcastPrimitive_t;
+
+/*defines for the Primitive field */
+#define MPI2_EVENT_PRIMITIVE_CHANGE (0x01)
+#define MPI2_EVENT_PRIMITIVE_SES (0x02)
+#define MPI2_EVENT_PRIMITIVE_EXPANDER (0x03)
+#define MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT (0x04)
+#define MPI2_EVENT_PRIMITIVE_RESERVED3 (0x05)
+#define MPI2_EVENT_PRIMITIVE_RESERVED4 (0x06)
+#define MPI2_EVENT_PRIMITIVE_CHANGE0_RESERVED (0x07)
+#define MPI2_EVENT_PRIMITIVE_CHANGE1_RESERVED (0x08)
+
+/*SAS Notify Primitive Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_NOTIFY_PRIMITIVE {
+ U8 PhyNum; /*0x00 */
+ U8 Port; /*0x01 */
+ U8 Reserved1; /*0x02 */
+ U8 Primitive; /*0x03 */
+} MPI2_EVENT_DATA_SAS_NOTIFY_PRIMITIVE,
+ *PTR_MPI2_EVENT_DATA_SAS_NOTIFY_PRIMITIVE,
+ Mpi2EventDataSasNotifyPrimitive_t,
+ *pMpi2EventDataSasNotifyPrimitive_t;
+
+/*defines for the Primitive field */
+#define MPI2_EVENT_NOTIFY_ENABLE_SPINUP (0x01)
+#define MPI2_EVENT_NOTIFY_POWER_LOSS_EXPECTED (0x02)
+#define MPI2_EVENT_NOTIFY_RESERVED1 (0x03)
+#define MPI2_EVENT_NOTIFY_RESERVED2 (0x04)
+
+/*SAS Initiator Device Status Change Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE {
+ U8 ReasonCode; /*0x00 */
+ U8 PhysicalPort; /*0x01 */
+ U16 DevHandle; /*0x02 */
+ U64 SASAddress; /*0x04 */
+} MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE,
+ *PTR_MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE,
+ Mpi2EventDataSasInitDevStatusChange_t,
+ *pMpi2EventDataSasInitDevStatusChange_t;
+
+/*SAS Initiator Device Status Change event ReasonCode values */
+#define MPI2_EVENT_SAS_INIT_RC_ADDED (0x01)
+#define MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING (0x02)
+
+/*SAS Initiator Device Table Overflow Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW {
+ U16 MaxInit; /*0x00 */
+ U16 CurrentInit; /*0x02 */
+ U64 SASAddress; /*0x04 */
+} MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW,
+ *PTR_MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW,
+ Mpi2EventDataSasInitTableOverflow_t,
+ *pMpi2EventDataSasInitTableOverflow_t;
+
+/*SAS Topology Change List Event data */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check NumEntries at runtime.
+ */
+#ifndef MPI2_EVENT_SAS_TOPO_PHY_COUNT
+#define MPI2_EVENT_SAS_TOPO_PHY_COUNT (1)
+#endif
+
+typedef struct _MPI2_EVENT_SAS_TOPO_PHY_ENTRY {
+ U16 AttachedDevHandle; /*0x00 */
+ U8 LinkRate; /*0x02 */
+ U8 PhyStatus; /*0x03 */
+} MPI2_EVENT_SAS_TOPO_PHY_ENTRY, *PTR_MPI2_EVENT_SAS_TOPO_PHY_ENTRY,
+ Mpi2EventSasTopoPhyEntry_t, *pMpi2EventSasTopoPhyEntry_t;
+
+typedef struct _MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST {
+ U16 EnclosureHandle; /*0x00 */
+ U16 ExpanderDevHandle; /*0x02 */
+ U8 NumPhys; /*0x04 */
+ U8 Reserved1; /*0x05 */
+ U16 Reserved2; /*0x06 */
+ U8 NumEntries; /*0x08 */
+ U8 StartPhyNum; /*0x09 */
+ U8 ExpStatus; /*0x0A */
+ U8 PhysicalPort; /*0x0B */
+ MPI2_EVENT_SAS_TOPO_PHY_ENTRY
+ PHY[MPI2_EVENT_SAS_TOPO_PHY_COUNT]; /*0x0C */
+} MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST,
+ *PTR_MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST,
+ Mpi2EventDataSasTopologyChangeList_t,
+ *pMpi2EventDataSasTopologyChangeList_t;
+
+/*values for the ExpStatus field */
+#define MPI2_EVENT_SAS_TOPO_ES_NO_EXPANDER (0x00)
+#define MPI2_EVENT_SAS_TOPO_ES_ADDED (0x01)
+#define MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING (0x02)
+#define MPI2_EVENT_SAS_TOPO_ES_RESPONDING (0x03)
+#define MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING (0x04)
+
+/*defines for the LinkRate field */
+#define MPI2_EVENT_SAS_TOPO_LR_CURRENT_MASK (0xF0)
+#define MPI2_EVENT_SAS_TOPO_LR_CURRENT_SHIFT (4)
+#define MPI2_EVENT_SAS_TOPO_LR_PREV_MASK (0x0F)
+#define MPI2_EVENT_SAS_TOPO_LR_PREV_SHIFT (0)
+
+#define MPI2_EVENT_SAS_TOPO_LR_UNKNOWN_LINK_RATE (0x00)
+#define MPI2_EVENT_SAS_TOPO_LR_PHY_DISABLED (0x01)
+#define MPI2_EVENT_SAS_TOPO_LR_NEGOTIATION_FAILED (0x02)
+#define MPI2_EVENT_SAS_TOPO_LR_SATA_OOB_COMPLETE (0x03)
+#define MPI2_EVENT_SAS_TOPO_LR_PORT_SELECTOR (0x04)
+#define MPI2_EVENT_SAS_TOPO_LR_SMP_RESET_IN_PROGRESS (0x05)
+#define MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY (0x06)
+#define MPI2_EVENT_SAS_TOPO_LR_RATE_1_5 (0x08)
+#define MPI2_EVENT_SAS_TOPO_LR_RATE_3_0 (0x09)
+#define MPI2_EVENT_SAS_TOPO_LR_RATE_6_0 (0x0A)
+#define MPI25_EVENT_SAS_TOPO_LR_RATE_12_0 (0x0B)
+
+/*values for the PhyStatus field */
+#define MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT (0x80)
+#define MPI2_EVENT_SAS_TOPO_PS_MULTIPLEX_CHANGE (0x10)
+/*values for the PhyStatus ReasonCode sub-field */
+#define MPI2_EVENT_SAS_TOPO_RC_MASK (0x0F)
+#define MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED (0x01)
+#define MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING (0x02)
+#define MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED (0x03)
+#define MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE (0x04)
+#define MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING (0x05)
+
+/*SAS Enclosure Device Status Change Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE {
+ U16 EnclosureHandle; /*0x00 */
+ U8 ReasonCode; /*0x02 */
+ U8 PhysicalPort; /*0x03 */
+ U64 EnclosureLogicalID; /*0x04 */
+ U16 NumSlots; /*0x0C */
+ U16 StartSlot; /*0x0E */
+ U32 PhyBits; /*0x10 */
+} MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE,
+ *PTR_MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE,
+ Mpi2EventDataSasEnclDevStatusChange_t,
+ *pMpi2EventDataSasEnclDevStatusChange_t;
+
+/*SAS Enclosure Device Status Change event ReasonCode values */
+#define MPI2_EVENT_SAS_ENCL_RC_ADDED (0x01)
+#define MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING (0x02)
+
+/*SAS PHY Counter Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_PHY_COUNTER {
+ U64 TimeStamp; /*0x00 */
+ U32 Reserved1; /*0x08 */
+ U8 PhyEventCode; /*0x0C */
+ U8 PhyNum; /*0x0D */
+ U16 Reserved2; /*0x0E */
+ U32 PhyEventInfo; /*0x10 */
+ U8 CounterType; /*0x14 */
+ U8 ThresholdWindow; /*0x15 */
+ U8 TimeUnits; /*0x16 */
+ U8 Reserved3; /*0x17 */
+ U32 EventThreshold; /*0x18 */
+ U16 ThresholdFlags; /*0x1C */
+ U16 Reserved4; /*0x1E */
+} MPI2_EVENT_DATA_SAS_PHY_COUNTER,
+ *PTR_MPI2_EVENT_DATA_SAS_PHY_COUNTER,
+ Mpi2EventDataSasPhyCounter_t,
+ *pMpi2EventDataSasPhyCounter_t;
+
+/*use MPI2_SASPHY3_EVENT_CODE_ values from mpi2_cnfg.h
+ *for the PhyEventCode field */
+
+/*use MPI2_SASPHY3_COUNTER_TYPE_ values from mpi2_cnfg.h
+ *for the CounterType field */
+
+/*use MPI2_SASPHY3_TIME_UNITS_ values from mpi2_cnfg.h
+ *for the TimeUnits field */
+
+/*use MPI2_SASPHY3_TFLAGS_ values from mpi2_cnfg.h
+ *for the ThresholdFlags field */
+
+/*SAS Quiesce Event data */
+
+typedef struct _MPI2_EVENT_DATA_SAS_QUIESCE {
+ U8 ReasonCode; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U32 Reserved3; /*0x04 */
+} MPI2_EVENT_DATA_SAS_QUIESCE,
+ *PTR_MPI2_EVENT_DATA_SAS_QUIESCE,
+ Mpi2EventDataSasQuiesce_t, *pMpi2EventDataSasQuiesce_t;
+
+/*SAS Quiesce Event data ReasonCode values */
+#define MPI2_EVENT_SAS_QUIESCE_RC_STARTED (0x01)
+#define MPI2_EVENT_SAS_QUIESCE_RC_COMPLETED (0x02)
+
+/*Host Based Discovery Phy Event data */
+
+typedef struct _MPI2_EVENT_HBD_PHY_SAS {
+ U8 Flags; /*0x00 */
+ U8 NegotiatedLinkRate; /*0x01 */
+ U8 PhyNum; /*0x02 */
+ U8 PhysicalPort; /*0x03 */
+ U32 Reserved1; /*0x04 */
+ U8 InitialFrame[28]; /*0x08 */
+} MPI2_EVENT_HBD_PHY_SAS, *PTR_MPI2_EVENT_HBD_PHY_SAS,
+ Mpi2EventHbdPhySas_t, *pMpi2EventHbdPhySas_t;
+
+/*values for the Flags field */
+#define MPI2_EVENT_HBD_SAS_FLAGS_FRAME_VALID (0x02)
+#define MPI2_EVENT_HBD_SAS_FLAGS_SATA_FRAME (0x01)
+
+/*use MPI2_SAS_NEG_LINK_RATE_ defines from mpi2_cnfg.h
+ *for the NegotiatedLinkRate field */
+
+typedef union _MPI2_EVENT_HBD_DESCRIPTOR {
+ MPI2_EVENT_HBD_PHY_SAS Sas;
+} MPI2_EVENT_HBD_DESCRIPTOR, *PTR_MPI2_EVENT_HBD_DESCRIPTOR,
+ Mpi2EventHbdDescriptor_t, *pMpi2EventHbdDescriptor_t;
+
+typedef struct _MPI2_EVENT_DATA_HBD_PHY {
+ U8 DescriptorType; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U32 Reserved3; /*0x04 */
+ MPI2_EVENT_HBD_DESCRIPTOR Descriptor; /*0x08 */
+} MPI2_EVENT_DATA_HBD_PHY, *PTR_MPI2_EVENT_DATA_HBD_PHY,
+ Mpi2EventDataHbdPhy_t,
+ *pMpi2EventDataMpi2EventDataHbdPhy_t;
+
+/*values for the DescriptorType field */
+#define MPI2_EVENT_HBD_DT_SAS (0x01)
+
+/****************************************************************************
+* EventAck message
+****************************************************************************/
+
+/*EventAck Request message */
+typedef struct _MPI2_EVENT_ACK_REQUEST {
+ U16 Reserved1; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Event; /*0x0C */
+ U16 Reserved5; /*0x0E */
+ U32 EventContext; /*0x10 */
+} MPI2_EVENT_ACK_REQUEST, *PTR_MPI2_EVENT_ACK_REQUEST,
+ Mpi2EventAckRequest_t, *pMpi2EventAckRequest_t;
+
+/*EventAck Reply message */
+typedef struct _MPI2_EVENT_ACK_REPLY {
+ U16 Reserved1; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_EVENT_ACK_REPLY, *PTR_MPI2_EVENT_ACK_REPLY,
+ Mpi2EventAckReply_t, *pMpi2EventAckReply_t;
+
+/****************************************************************************
+* SendHostMessage message
+****************************************************************************/
+
+/*SendHostMessage Request message */
+typedef struct _MPI2_SEND_HOST_MESSAGE_REQUEST {
+ U16 HostDataLength; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved1; /*0x04 */
+ U8 Reserved2; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+ U8 Reserved4; /*0x0C */
+ U8 DestVF_ID; /*0x0D */
+ U16 Reserved5; /*0x0E */
+ U32 Reserved6; /*0x10 */
+ U32 Reserved7; /*0x14 */
+ U32 Reserved8; /*0x18 */
+ U32 Reserved9; /*0x1C */
+ U32 Reserved10; /*0x20 */
+ U32 HostData[1]; /*0x24 */
+} MPI2_SEND_HOST_MESSAGE_REQUEST,
+ *PTR_MPI2_SEND_HOST_MESSAGE_REQUEST,
+ Mpi2SendHostMessageRequest_t,
+ *pMpi2SendHostMessageRequest_t;
+
+/*SendHostMessage Reply message */
+typedef struct _MPI2_SEND_HOST_MESSAGE_REPLY {
+ U16 HostDataLength; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved1; /*0x04 */
+ U8 Reserved2; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+ U16 Reserved4; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_SEND_HOST_MESSAGE_REPLY, *PTR_MPI2_SEND_HOST_MESSAGE_REPLY,
+ Mpi2SendHostMessageReply_t, *pMpi2SendHostMessageReply_t;
+
+/****************************************************************************
+* FWDownload message
+****************************************************************************/
+
+/*MPI v2.0 FWDownload Request message */
+typedef struct _MPI2_FW_DOWNLOAD_REQUEST {
+ U8 ImageType; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U32 TotalImageSize; /*0x0C */
+ U32 Reserved5; /*0x10 */
+ MPI2_MPI_SGE_UNION SGL; /*0x14 */
+} MPI2_FW_DOWNLOAD_REQUEST, *PTR_MPI2_FW_DOWNLOAD_REQUEST,
+ Mpi2FWDownloadRequest, *pMpi2FWDownloadRequest;
+
+#define MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT (0x01)
+
+#define MPI2_FW_DOWNLOAD_ITYPE_FW (0x01)
+#define MPI2_FW_DOWNLOAD_ITYPE_BIOS (0x02)
+#define MPI2_FW_DOWNLOAD_ITYPE_MANUFACTURING (0x06)
+#define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_1 (0x07)
+#define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_2 (0x08)
+#define MPI2_FW_DOWNLOAD_ITYPE_MEGARAID (0x09)
+#define MPI2_FW_DOWNLOAD_ITYPE_COMPLETE (0x0A)
+#define MPI2_FW_DOWNLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B)
+#define MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC (0xF0)
+
+/*MPI v2.0 FWDownload TransactionContext Element */
+typedef struct _MPI2_FW_DOWNLOAD_TCSGE {
+ U8 Reserved1; /*0x00 */
+ U8 ContextSize; /*0x01 */
+ U8 DetailsLength; /*0x02 */
+ U8 Flags; /*0x03 */
+ U32 Reserved2; /*0x04 */
+ U32 ImageOffset; /*0x08 */
+ U32 ImageSize; /*0x0C */
+} MPI2_FW_DOWNLOAD_TCSGE, *PTR_MPI2_FW_DOWNLOAD_TCSGE,
+ Mpi2FWDownloadTCSGE_t, *pMpi2FWDownloadTCSGE_t;
+
+/*MPI v2.5 FWDownload Request message */
+typedef struct _MPI25_FW_DOWNLOAD_REQUEST {
+ U8 ImageType; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U32 TotalImageSize; /*0x0C */
+ U32 Reserved5; /*0x10 */
+ U32 Reserved6; /*0x14 */
+ U32 ImageOffset; /*0x18 */
+ U32 ImageSize; /*0x1C */
+ MPI25_SGE_IO_UNION SGL; /*0x20 */
+} MPI25_FW_DOWNLOAD_REQUEST, *PTR_MPI25_FW_DOWNLOAD_REQUEST,
+ Mpi25FWDownloadRequest, *pMpi25FWDownloadRequest;
+
+/*FWDownload Reply message */
+typedef struct _MPI2_FW_DOWNLOAD_REPLY {
+ U8 ImageType; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_FW_DOWNLOAD_REPLY, *PTR_MPI2_FW_DOWNLOAD_REPLY,
+ Mpi2FWDownloadReply_t, *pMpi2FWDownloadReply_t;
+
+/****************************************************************************
+* FWUpload message
+****************************************************************************/
+
+/*MPI v2.0 FWUpload Request message */
+typedef struct _MPI2_FW_UPLOAD_REQUEST {
+ U8 ImageType; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U32 Reserved5; /*0x0C */
+ U32 Reserved6; /*0x10 */
+ MPI2_MPI_SGE_UNION SGL; /*0x14 */
+} MPI2_FW_UPLOAD_REQUEST, *PTR_MPI2_FW_UPLOAD_REQUEST,
+ Mpi2FWUploadRequest_t, *pMpi2FWUploadRequest_t;
+
+#define MPI2_FW_UPLOAD_ITYPE_FW_CURRENT (0x00)
+#define MPI2_FW_UPLOAD_ITYPE_FW_FLASH (0x01)
+#define MPI2_FW_UPLOAD_ITYPE_BIOS_FLASH (0x02)
+#define MPI2_FW_UPLOAD_ITYPE_FW_BACKUP (0x05)
+#define MPI2_FW_UPLOAD_ITYPE_MANUFACTURING (0x06)
+#define MPI2_FW_UPLOAD_ITYPE_CONFIG_1 (0x07)
+#define MPI2_FW_UPLOAD_ITYPE_CONFIG_2 (0x08)
+#define MPI2_FW_UPLOAD_ITYPE_MEGARAID (0x09)
+#define MPI2_FW_UPLOAD_ITYPE_COMPLETE (0x0A)
+#define MPI2_FW_UPLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B)
+
+/*MPI v2.0 FWUpload TransactionContext Element */
+typedef struct _MPI2_FW_UPLOAD_TCSGE {
+ U8 Reserved1; /*0x00 */
+ U8 ContextSize; /*0x01 */
+ U8 DetailsLength; /*0x02 */
+ U8 Flags; /*0x03 */
+ U32 Reserved2; /*0x04 */
+ U32 ImageOffset; /*0x08 */
+ U32 ImageSize; /*0x0C */
+} MPI2_FW_UPLOAD_TCSGE, *PTR_MPI2_FW_UPLOAD_TCSGE,
+ Mpi2FWUploadTCSGE_t, *pMpi2FWUploadTCSGE_t;
+
+/*MPI v2.5 FWUpload Request message */
+typedef struct _MPI25_FW_UPLOAD_REQUEST {
+ U8 ImageType; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U32 Reserved5; /*0x0C */
+ U32 Reserved6; /*0x10 */
+ U32 Reserved7; /*0x14 */
+ U32 ImageOffset; /*0x18 */
+ U32 ImageSize; /*0x1C */
+ MPI25_SGE_IO_UNION SGL; /*0x20 */
+} MPI25_FW_UPLOAD_REQUEST, *PTR_MPI25_FW_UPLOAD_REQUEST,
+ Mpi25FWUploadRequest_t, *pMpi25FWUploadRequest_t;
+
+/*FWUpload Reply message */
+typedef struct _MPI2_FW_UPLOAD_REPLY {
+ U8 ImageType; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U32 ActualImageSize; /*0x14 */
+} MPI2_FW_UPLOAD_REPLY, *PTR_MPI2_FW_UPLOAD_REPLY,
+ Mpi2FWUploadReply_t, *pMPi2FWUploadReply_t;
+
+/*FW Image Header */
+typedef struct _MPI2_FW_IMAGE_HEADER {
+ U32 Signature; /*0x00 */
+ U32 Signature0; /*0x04 */
+ U32 Signature1; /*0x08 */
+ U32 Signature2; /*0x0C */
+ MPI2_VERSION_UNION MPIVersion; /*0x10 */
+ MPI2_VERSION_UNION FWVersion; /*0x14 */
+ MPI2_VERSION_UNION NVDATAVersion; /*0x18 */
+ MPI2_VERSION_UNION PackageVersion; /*0x1C */
+ U16 VendorID; /*0x20 */
+ U16 ProductID; /*0x22 */
+ U16 ProtocolFlags; /*0x24 */
+ U16 Reserved26; /*0x26 */
+ U32 IOCCapabilities; /*0x28 */
+ U32 ImageSize; /*0x2C */
+ U32 NextImageHeaderOffset; /*0x30 */
+ U32 Checksum; /*0x34 */
+ U32 Reserved38; /*0x38 */
+ U32 Reserved3C; /*0x3C */
+ U32 Reserved40; /*0x40 */
+ U32 Reserved44; /*0x44 */
+ U32 Reserved48; /*0x48 */
+ U32 Reserved4C; /*0x4C */
+ U32 Reserved50; /*0x50 */
+ U32 Reserved54; /*0x54 */
+ U32 Reserved58; /*0x58 */
+ U32 Reserved5C; /*0x5C */
+ U32 Reserved60; /*0x60 */
+ U32 FirmwareVersionNameWhat; /*0x64 */
+ U8 FirmwareVersionName[32]; /*0x68 */
+ U32 VendorNameWhat; /*0x88 */
+ U8 VendorName[32]; /*0x8C */
+ U32 PackageNameWhat; /*0x88 */
+ U8 PackageName[32]; /*0x8C */
+ U32 ReservedD0; /*0xD0 */
+ U32 ReservedD4; /*0xD4 */
+ U32 ReservedD8; /*0xD8 */
+ U32 ReservedDC; /*0xDC */
+ U32 ReservedE0; /*0xE0 */
+ U32 ReservedE4; /*0xE4 */
+ U32 ReservedE8; /*0xE8 */
+ U32 ReservedEC; /*0xEC */
+ U32 ReservedF0; /*0xF0 */
+ U32 ReservedF4; /*0xF4 */
+ U32 ReservedF8; /*0xF8 */
+ U32 ReservedFC; /*0xFC */
+} MPI2_FW_IMAGE_HEADER, *PTR_MPI2_FW_IMAGE_HEADER,
+ Mpi2FWImageHeader_t, *pMpi2FWImageHeader_t;
+
+/*Signature field */
+#define MPI2_FW_HEADER_SIGNATURE_OFFSET (0x00)
+#define MPI2_FW_HEADER_SIGNATURE_MASK (0xFF000000)
+#define MPI2_FW_HEADER_SIGNATURE (0xEA000000)
+
+/*Signature0 field */
+#define MPI2_FW_HEADER_SIGNATURE0_OFFSET (0x04)
+#define MPI2_FW_HEADER_SIGNATURE0 (0x5AFAA55A)
+
+/*Signature1 field */
+#define MPI2_FW_HEADER_SIGNATURE1_OFFSET (0x08)
+#define MPI2_FW_HEADER_SIGNATURE1 (0xA55AFAA5)
+
+/*Signature2 field */
+#define MPI2_FW_HEADER_SIGNATURE2_OFFSET (0x0C)
+#define MPI2_FW_HEADER_SIGNATURE2 (0x5AA55AFA)
+
+/*defines for using the ProductID field */
+#define MPI2_FW_HEADER_PID_TYPE_MASK (0xF000)
+#define MPI2_FW_HEADER_PID_TYPE_SAS (0x2000)
+
+#define MPI2_FW_HEADER_PID_PROD_MASK (0x0F00)
+#define MPI2_FW_HEADER_PID_PROD_A (0x0000)
+#define MPI2_FW_HEADER_PID_PROD_TARGET_INITIATOR_SCSI (0x0200)
+#define MPI2_FW_HEADER_PID_PROD_IR_SCSI (0x0700)
+
+#define MPI2_FW_HEADER_PID_FAMILY_MASK (0x00FF)
+/*SAS ProductID Family bits */
+#define MPI2_FW_HEADER_PID_FAMILY_2108_SAS (0x0013)
+#define MPI2_FW_HEADER_PID_FAMILY_2208_SAS (0x0014)
+#define MPI25_FW_HEADER_PID_FAMILY_3108_SAS (0x0021)
+
+/*use MPI2_IOCFACTS_PROTOCOL_ defines for ProtocolFlags field */
+
+/*use MPI2_IOCFACTS_CAPABILITY_ defines for IOCCapabilities field */
+
+#define MPI2_FW_HEADER_IMAGESIZE_OFFSET (0x2C)
+#define MPI2_FW_HEADER_NEXTIMAGE_OFFSET (0x30)
+#define MPI2_FW_HEADER_VERNMHWAT_OFFSET (0x64)
+
+#define MPI2_FW_HEADER_WHAT_SIGNATURE (0x29232840)
+
+#define MPI2_FW_HEADER_SIZE (0x100)
+
+/*Extended Image Header */
+typedef struct _MPI2_EXT_IMAGE_HEADER {
+ U8 ImageType; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U32 Checksum; /*0x04 */
+ U32 ImageSize; /*0x08 */
+ U32 NextImageHeaderOffset; /*0x0C */
+ U32 PackageVersion; /*0x10 */
+ U32 Reserved3; /*0x14 */
+ U32 Reserved4; /*0x18 */
+ U32 Reserved5; /*0x1C */
+ U8 IdentifyString[32]; /*0x20 */
+} MPI2_EXT_IMAGE_HEADER, *PTR_MPI2_EXT_IMAGE_HEADER,
+ Mpi2ExtImageHeader_t, *pMpi2ExtImageHeader_t;
+
+/*useful offsets */
+#define MPI2_EXT_IMAGE_IMAGETYPE_OFFSET (0x00)
+#define MPI2_EXT_IMAGE_IMAGESIZE_OFFSET (0x08)
+#define MPI2_EXT_IMAGE_NEXTIMAGE_OFFSET (0x0C)
+
+#define MPI2_EXT_IMAGE_HEADER_SIZE (0x40)
+
+/*defines for the ImageType field */
+#define MPI2_EXT_IMAGE_TYPE_UNSPECIFIED (0x00)
+#define MPI2_EXT_IMAGE_TYPE_FW (0x01)
+#define MPI2_EXT_IMAGE_TYPE_NVDATA (0x03)
+#define MPI2_EXT_IMAGE_TYPE_BOOTLOADER (0x04)
+#define MPI2_EXT_IMAGE_TYPE_INITIALIZATION (0x05)
+#define MPI2_EXT_IMAGE_TYPE_FLASH_LAYOUT (0x06)
+#define MPI2_EXT_IMAGE_TYPE_SUPPORTED_DEVICES (0x07)
+#define MPI2_EXT_IMAGE_TYPE_MEGARAID (0x08)
+#define MPI2_EXT_IMAGE_TYPE_MIN_PRODUCT_SPECIFIC (0x80)
+#define MPI2_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC (0xFF)
+
+#define MPI2_EXT_IMAGE_TYPE_MAX (MPI2_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC)
+
+/*FLASH Layout Extended Image Data */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check RegionsPerLayout at runtime.
+ */
+#ifndef MPI2_FLASH_NUMBER_OF_REGIONS
+#define MPI2_FLASH_NUMBER_OF_REGIONS (1)
+#endif
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check NumberOfLayouts at runtime.
+ */
+#ifndef MPI2_FLASH_NUMBER_OF_LAYOUTS
+#define MPI2_FLASH_NUMBER_OF_LAYOUTS (1)
+#endif
+
+typedef struct _MPI2_FLASH_REGION {
+ U8 RegionType; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U32 RegionOffset; /*0x04 */
+ U32 RegionSize; /*0x08 */
+ U32 Reserved3; /*0x0C */
+} MPI2_FLASH_REGION, *PTR_MPI2_FLASH_REGION,
+ Mpi2FlashRegion_t, *pMpi2FlashRegion_t;
+
+typedef struct _MPI2_FLASH_LAYOUT {
+ U32 FlashSize; /*0x00 */
+ U32 Reserved1; /*0x04 */
+ U32 Reserved2; /*0x08 */
+ U32 Reserved3; /*0x0C */
+ MPI2_FLASH_REGION Region[MPI2_FLASH_NUMBER_OF_REGIONS]; /*0x10 */
+} MPI2_FLASH_LAYOUT, *PTR_MPI2_FLASH_LAYOUT,
+ Mpi2FlashLayout_t, *pMpi2FlashLayout_t;
+
+typedef struct _MPI2_FLASH_LAYOUT_DATA {
+ U8 ImageRevision; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 SizeOfRegion; /*0x02 */
+ U8 Reserved2; /*0x03 */
+ U16 NumberOfLayouts; /*0x04 */
+ U16 RegionsPerLayout; /*0x06 */
+ U16 MinimumSectorAlignment; /*0x08 */
+ U16 Reserved3; /*0x0A */
+ U32 Reserved4; /*0x0C */
+ MPI2_FLASH_LAYOUT Layout[MPI2_FLASH_NUMBER_OF_LAYOUTS]; /*0x10 */
+} MPI2_FLASH_LAYOUT_DATA, *PTR_MPI2_FLASH_LAYOUT_DATA,
+ Mpi2FlashLayoutData_t, *pMpi2FlashLayoutData_t;
+
+/*defines for the RegionType field */
+#define MPI2_FLASH_REGION_UNUSED (0x00)
+#define MPI2_FLASH_REGION_FIRMWARE (0x01)
+#define MPI2_FLASH_REGION_BIOS (0x02)
+#define MPI2_FLASH_REGION_NVDATA (0x03)
+#define MPI2_FLASH_REGION_FIRMWARE_BACKUP (0x05)
+#define MPI2_FLASH_REGION_MFG_INFORMATION (0x06)
+#define MPI2_FLASH_REGION_CONFIG_1 (0x07)
+#define MPI2_FLASH_REGION_CONFIG_2 (0x08)
+#define MPI2_FLASH_REGION_MEGARAID (0x09)
+#define MPI2_FLASH_REGION_INIT (0x0A)
+
+/*ImageRevision */
+#define MPI2_FLASH_LAYOUT_IMAGE_REVISION (0x00)
+
+/*Supported Devices Extended Image Data */
+
+/*
+ *Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ *one and check NumberOfDevices at runtime.
+ */
+#ifndef MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES
+#define MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES (1)
+#endif
+
+typedef struct _MPI2_SUPPORTED_DEVICE {
+ U16 DeviceID; /*0x00 */
+ U16 VendorID; /*0x02 */
+ U16 DeviceIDMask; /*0x04 */
+ U16 Reserved1; /*0x06 */
+ U8 LowPCIRev; /*0x08 */
+ U8 HighPCIRev; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U32 Reserved3; /*0x0C */
+} MPI2_SUPPORTED_DEVICE, *PTR_MPI2_SUPPORTED_DEVICE,
+ Mpi2SupportedDevice_t, *pMpi2SupportedDevice_t;
+
+typedef struct _MPI2_SUPPORTED_DEVICES_DATA {
+ U8 ImageRevision; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 NumberOfDevices; /*0x02 */
+ U8 Reserved2; /*0x03 */
+ U32 Reserved3; /*0x04 */
+ MPI2_SUPPORTED_DEVICE
+ SupportedDevice[MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES];/*0x08 */
+} MPI2_SUPPORTED_DEVICES_DATA, *PTR_MPI2_SUPPORTED_DEVICES_DATA,
+ Mpi2SupportedDevicesData_t, *pMpi2SupportedDevicesData_t;
+
+/*ImageRevision */
+#define MPI2_SUPPORTED_DEVICES_IMAGE_REVISION (0x00)
+
+/*Init Extended Image Data */
+
+typedef struct _MPI2_INIT_IMAGE_FOOTER {
+ U32 BootFlags; /*0x00 */
+ U32 ImageSize; /*0x04 */
+ U32 Signature0; /*0x08 */
+ U32 Signature1; /*0x0C */
+ U32 Signature2; /*0x10 */
+ U32 ResetVector; /*0x14 */
+} MPI2_INIT_IMAGE_FOOTER, *PTR_MPI2_INIT_IMAGE_FOOTER,
+ Mpi2InitImageFooter_t, *pMpi2InitImageFooter_t;
+
+/*defines for the BootFlags field */
+#define MPI2_INIT_IMAGE_BOOTFLAGS_OFFSET (0x00)
+
+/*defines for the ImageSize field */
+#define MPI2_INIT_IMAGE_IMAGESIZE_OFFSET (0x04)
+
+/*defines for the Signature0 field */
+#define MPI2_INIT_IMAGE_SIGNATURE0_OFFSET (0x08)
+#define MPI2_INIT_IMAGE_SIGNATURE0 (0x5AA55AEA)
+
+/*defines for the Signature1 field */
+#define MPI2_INIT_IMAGE_SIGNATURE1_OFFSET (0x0C)
+#define MPI2_INIT_IMAGE_SIGNATURE1 (0xA55AEAA5)
+
+/*defines for the Signature2 field */
+#define MPI2_INIT_IMAGE_SIGNATURE2_OFFSET (0x10)
+#define MPI2_INIT_IMAGE_SIGNATURE2 (0x5AEAA55A)
+
+/*Signature fields as individual bytes */
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_0 (0xEA)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_1 (0x5A)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_2 (0xA5)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_3 (0x5A)
+
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_4 (0xA5)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_5 (0xEA)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_6 (0x5A)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_7 (0xA5)
+
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_8 (0x5A)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_9 (0xA5)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_A (0xEA)
+#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_B (0x5A)
+
+/*defines for the ResetVector field */
+#define MPI2_INIT_IMAGE_RESETVECTOR_OFFSET (0x14)
+
+/****************************************************************************
+* PowerManagementControl message
+****************************************************************************/
+
+/*PowerManagementControl Request message */
+typedef struct _MPI2_PWR_MGMT_CONTROL_REQUEST {
+ U8 Feature; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U8 Parameter1; /*0x0C */
+ U8 Parameter2; /*0x0D */
+ U8 Parameter3; /*0x0E */
+ U8 Parameter4; /*0x0F */
+ U32 Reserved5; /*0x10 */
+ U32 Reserved6; /*0x14 */
+} MPI2_PWR_MGMT_CONTROL_REQUEST, *PTR_MPI2_PWR_MGMT_CONTROL_REQUEST,
+ Mpi2PwrMgmtControlRequest_t, *pMpi2PwrMgmtControlRequest_t;
+
+/*defines for the Feature field */
+#define MPI2_PM_CONTROL_FEATURE_DA_PHY_POWER_COND (0x01)
+#define MPI2_PM_CONTROL_FEATURE_PORT_WIDTH_MODULATION (0x02)
+#define MPI2_PM_CONTROL_FEATURE_PCIE_LINK (0x03) /*obsolete */
+#define MPI2_PM_CONTROL_FEATURE_IOC_SPEED (0x04)
+#define MPI2_PM_CONTROL_FEATURE_GLOBAL_PWR_MGMT_MODE (0x05)
+#define MPI2_PM_CONTROL_FEATURE_MIN_PRODUCT_SPECIFIC (0x80)
+#define MPI2_PM_CONTROL_FEATURE_MAX_PRODUCT_SPECIFIC (0xFF)
+
+/*parameter usage for the MPI2_PM_CONTROL_FEATURE_DA_PHY_POWER_COND Feature */
+/*Parameter1 contains a PHY number */
+/*Parameter2 indicates power condition action using these defines */
+#define MPI2_PM_CONTROL_PARAM2_PARTIAL (0x01)
+#define MPI2_PM_CONTROL_PARAM2_SLUMBER (0x02)
+#define MPI2_PM_CONTROL_PARAM2_EXIT_PWR_MGMT (0x03)
+/*Parameter3 and Parameter4 are reserved */
+
+/*parameter usage for the MPI2_PM_CONTROL_FEATURE_PORT_WIDTH_MODULATION
+ * Feature */
+/*Parameter1 contains SAS port width modulation group number */
+/*Parameter2 indicates IOC action using these defines */
+#define MPI2_PM_CONTROL_PARAM2_REQUEST_OWNERSHIP (0x01)
+#define MPI2_PM_CONTROL_PARAM2_CHANGE_MODULATION (0x02)
+#define MPI2_PM_CONTROL_PARAM2_RELINQUISH_OWNERSHIP (0x03)
+/*Parameter3 indicates desired modulation level using these defines */
+#define MPI2_PM_CONTROL_PARAM3_25_PERCENT (0x00)
+#define MPI2_PM_CONTROL_PARAM3_50_PERCENT (0x01)
+#define MPI2_PM_CONTROL_PARAM3_75_PERCENT (0x02)
+#define MPI2_PM_CONTROL_PARAM3_100_PERCENT (0x03)
+/*Parameter4 is reserved */
+
+/*this next set (_PCIE_LINK) is obsolete */
+/*parameter usage for the MPI2_PM_CONTROL_FEATURE_PCIE_LINK Feature */
+/*Parameter1 indicates desired PCIe link speed using these defines */
+#define MPI2_PM_CONTROL_PARAM1_PCIE_2_5_GBPS (0x00) /*obsolete */
+#define MPI2_PM_CONTROL_PARAM1_PCIE_5_0_GBPS (0x01) /*obsolete */
+#define MPI2_PM_CONTROL_PARAM1_PCIE_8_0_GBPS (0x02) /*obsolete */
+/*Parameter2 indicates desired PCIe link width using these defines */
+#define MPI2_PM_CONTROL_PARAM2_WIDTH_X1 (0x01) /*obsolete */
+#define MPI2_PM_CONTROL_PARAM2_WIDTH_X2 (0x02) /*obsolete */
+#define MPI2_PM_CONTROL_PARAM2_WIDTH_X4 (0x04) /*obsolete */
+#define MPI2_PM_CONTROL_PARAM2_WIDTH_X8 (0x08) /*obsolete */
+/*Parameter3 and Parameter4 are reserved */
+
+/*parameter usage for the MPI2_PM_CONTROL_FEATURE_IOC_SPEED Feature */
+/*Parameter1 indicates desired IOC hardware clock speed using these defines */
+#define MPI2_PM_CONTROL_PARAM1_FULL_IOC_SPEED (0x01)
+#define MPI2_PM_CONTROL_PARAM1_HALF_IOC_SPEED (0x02)
+#define MPI2_PM_CONTROL_PARAM1_QUARTER_IOC_SPEED (0x04)
+#define MPI2_PM_CONTROL_PARAM1_EIGHTH_IOC_SPEED (0x08)
+/*Parameter2, Parameter3, and Parameter4 are reserved */
+
+/*parameter usage for the MPI2_PM_CONTROL_FEATURE_GLOBAL_PWR_MGMT_MODE Feature*/
+/*Parameter1 indicates host action regarding global power management mode */
+#define MPI2_PM_CONTROL_PARAM1_TAKE_CONTROL (0x01)
+#define MPI2_PM_CONTROL_PARAM1_CHANGE_GLOBAL_MODE (0x02)
+#define MPI2_PM_CONTROL_PARAM1_RELEASE_CONTROL (0x03)
+/*Parameter2 indicates the requested global power management mode */
+#define MPI2_PM_CONTROL_PARAM2_FULL_PWR_PERF (0x01)
+#define MPI2_PM_CONTROL_PARAM2_REDUCED_PWR_PERF (0x08)
+#define MPI2_PM_CONTROL_PARAM2_STANDBY (0x40)
+/*Parameter3 and Parameter4 are reserved */
+
+/*PowerManagementControl Reply message */
+typedef struct _MPI2_PWR_MGMT_CONTROL_REPLY {
+ U8 Feature; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_PWR_MGMT_CONTROL_REPLY, *PTR_MPI2_PWR_MGMT_CONTROL_REPLY,
+ Mpi2PwrMgmtControlReply_t, *pMpi2PwrMgmtControlReply_t;
+
+#endif
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_raid.h b/drivers/scsi/mpt3sas/mpi/mpi2_raid.h
new file mode 100644
index 00000000000..d1d9866cf30
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_raid.h
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2000-2012 LSI Corporation.
+ *
+ *
+ * Name: mpi2_raid.h
+ * Title: MPI Integrated RAID messages and structures
+ * Creation Date: April 26, 2007
+ *
+ * mpi2_raid.h Version: 02.00.08
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 08-31-07 02.00.01 Modifications to RAID Action request and reply,
+ * including the Actions and ActionData.
+ * 02-29-08 02.00.02 Added MPI2_RAID_ACTION_ADATA_DISABL_FULL_REBUILD.
+ * 05-21-08 02.00.03 Added MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS so that
+ * the PhysDisk array in MPI2_RAID_VOLUME_CREATION_STRUCT
+ * can be sized by the build environment.
+ * 07-30-09 02.00.04 Added proper define for the Use Default Settings bit of
+ * VolumeCreationFlags and marked the old one as obsolete.
+ * 05-12-10 02.00.05 Added MPI2_RAID_VOL_FLAGS_OP_MDC define.
+ * 08-24-10 02.00.06 Added MPI2_RAID_ACTION_COMPATIBILITY_CHECK along with
+ * related structures and defines.
+ * Added product-specific range to RAID Action values.
+ * 11-18-11 02.00.07 Incorporating additions for MPI v2.5.
+ * 02-06-12 02.00.08 Added MPI2_RAID_ACTION_PHYSDISK_HIDDEN.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_RAID_H
+#define MPI2_RAID_H
+
+/*****************************************************************************
+*
+* Integrated RAID Messages
+*
+*****************************************************************************/
+
+/****************************************************************************
+* RAID Action messages
+****************************************************************************/
+
+/*ActionDataWord defines for use with MPI2_RAID_ACTION_DELETE_VOLUME action */
+#define MPI2_RAID_ACTION_ADATA_KEEP_LBA0 (0x00000000)
+#define MPI2_RAID_ACTION_ADATA_ZERO_LBA0 (0x00000001)
+
+/*use MPI2_RAIDVOL0_SETTING_ defines from mpi2_cnfg.h for
+ *MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE action */
+
+/*ActionDataWord defines for use with
+ *MPI2_RAID_ACTION_DISABLE_ALL_VOLUMES action */
+#define MPI2_RAID_ACTION_ADATA_DISABL_FULL_REBUILD (0x00000001)
+
+/*ActionDataWord for MPI2_RAID_ACTION_SET_RAID_FUNCTION_RATE Action */
+typedef struct _MPI2_RAID_ACTION_RATE_DATA {
+ U8 RateToChange; /*0x00 */
+ U8 RateOrMode; /*0x01 */
+ U16 DataScrubDuration; /*0x02 */
+} MPI2_RAID_ACTION_RATE_DATA, *PTR_MPI2_RAID_ACTION_RATE_DATA,
+ Mpi2RaidActionRateData_t, *pMpi2RaidActionRateData_t;
+
+#define MPI2_RAID_ACTION_SET_RATE_RESYNC (0x00)
+#define MPI2_RAID_ACTION_SET_RATE_DATA_SCRUB (0x01)
+#define MPI2_RAID_ACTION_SET_RATE_POWERSAVE_MODE (0x02)
+
+/*ActionDataWord for MPI2_RAID_ACTION_START_RAID_FUNCTION Action */
+typedef struct _MPI2_RAID_ACTION_START_RAID_FUNCTION {
+ U8 RAIDFunction; /*0x00 */
+ U8 Flags; /*0x01 */
+ U16 Reserved1; /*0x02 */
+} MPI2_RAID_ACTION_START_RAID_FUNCTION,
+ *PTR_MPI2_RAID_ACTION_START_RAID_FUNCTION,
+ Mpi2RaidActionStartRaidFunction_t,
+ *pMpi2RaidActionStartRaidFunction_t;
+
+/*defines for the RAIDFunction field */
+#define MPI2_RAID_ACTION_START_BACKGROUND_INIT (0x00)
+#define MPI2_RAID_ACTION_START_ONLINE_CAP_EXPANSION (0x01)
+#define MPI2_RAID_ACTION_START_CONSISTENCY_CHECK (0x02)
+
+/*defines for the Flags field */
+#define MPI2_RAID_ACTION_START_NEW (0x00)
+#define MPI2_RAID_ACTION_START_RESUME (0x01)
+
+/*ActionDataWord for MPI2_RAID_ACTION_STOP_RAID_FUNCTION Action */
+typedef struct _MPI2_RAID_ACTION_STOP_RAID_FUNCTION {
+ U8 RAIDFunction; /*0x00 */
+ U8 Flags; /*0x01 */
+ U16 Reserved1; /*0x02 */
+} MPI2_RAID_ACTION_STOP_RAID_FUNCTION,
+ *PTR_MPI2_RAID_ACTION_STOP_RAID_FUNCTION,
+ Mpi2RaidActionStopRaidFunction_t,
+ *pMpi2RaidActionStopRaidFunction_t;
+
+/*defines for the RAIDFunction field */
+#define MPI2_RAID_ACTION_STOP_BACKGROUND_INIT (0x00)
+#define MPI2_RAID_ACTION_STOP_ONLINE_CAP_EXPANSION (0x01)
+#define MPI2_RAID_ACTION_STOP_CONSISTENCY_CHECK (0x02)
+
+/*defines for the Flags field */
+#define MPI2_RAID_ACTION_STOP_ABORT (0x00)
+#define MPI2_RAID_ACTION_STOP_PAUSE (0x01)
+
+/*ActionDataWord for MPI2_RAID_ACTION_CREATE_HOT_SPARE Action */
+typedef struct _MPI2_RAID_ACTION_HOT_SPARE {
+ U8 HotSparePool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 DevHandle; /*0x02 */
+} MPI2_RAID_ACTION_HOT_SPARE, *PTR_MPI2_RAID_ACTION_HOT_SPARE,
+ Mpi2RaidActionHotSpare_t, *pMpi2RaidActionHotSpare_t;
+
+/*ActionDataWord for MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE Action */
+typedef struct _MPI2_RAID_ACTION_FW_UPDATE_MODE {
+ U8 Flags; /*0x00 */
+ U8 DeviceFirmwareUpdateModeTimeout; /*0x01 */
+ U16 Reserved1; /*0x02 */
+} MPI2_RAID_ACTION_FW_UPDATE_MODE,
+ *PTR_MPI2_RAID_ACTION_FW_UPDATE_MODE,
+ Mpi2RaidActionFwUpdateMode_t,
+ *pMpi2RaidActionFwUpdateMode_t;
+
+/*ActionDataWord defines for use with
+ *MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE action */
+#define MPI2_RAID_ACTION_ADATA_DISABLE_FW_UPDATE (0x00)
+#define MPI2_RAID_ACTION_ADATA_ENABLE_FW_UPDATE (0x01)
+
+typedef union _MPI2_RAID_ACTION_DATA {
+ U32 Word;
+ MPI2_RAID_ACTION_RATE_DATA Rates;
+ MPI2_RAID_ACTION_START_RAID_FUNCTION StartRaidFunction;
+ MPI2_RAID_ACTION_STOP_RAID_FUNCTION StopRaidFunction;
+ MPI2_RAID_ACTION_HOT_SPARE HotSpare;
+ MPI2_RAID_ACTION_FW_UPDATE_MODE FwUpdateMode;
+} MPI2_RAID_ACTION_DATA, *PTR_MPI2_RAID_ACTION_DATA,
+ Mpi2RaidActionData_t, *pMpi2RaidActionData_t;
+
+/*RAID Action Request Message */
+typedef struct _MPI2_RAID_ACTION_REQUEST {
+ U8 Action; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 VolDevHandle; /*0x04 */
+ U8 PhysDiskNum; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U32 Reserved3; /*0x0C */
+ MPI2_RAID_ACTION_DATA ActionDataWord; /*0x10 */
+ MPI2_SGE_SIMPLE_UNION ActionDataSGE; /*0x14 */
+} MPI2_RAID_ACTION_REQUEST, *PTR_MPI2_RAID_ACTION_REQUEST,
+ Mpi2RaidActionRequest_t, *pMpi2RaidActionRequest_t;
+
+/*RAID Action request Action values */
+
+#define MPI2_RAID_ACTION_INDICATOR_STRUCT (0x01)
+#define MPI2_RAID_ACTION_CREATE_VOLUME (0x02)
+#define MPI2_RAID_ACTION_DELETE_VOLUME (0x03)
+#define MPI2_RAID_ACTION_DISABLE_ALL_VOLUMES (0x04)
+#define MPI2_RAID_ACTION_ENABLE_ALL_VOLUMES (0x05)
+#define MPI2_RAID_ACTION_PHYSDISK_OFFLINE (0x0A)
+#define MPI2_RAID_ACTION_PHYSDISK_ONLINE (0x0B)
+#define MPI2_RAID_ACTION_FAIL_PHYSDISK (0x0F)
+#define MPI2_RAID_ACTION_ACTIVATE_VOLUME (0x11)
+#define MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE (0x15)
+#define MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE (0x17)
+#define MPI2_RAID_ACTION_SET_VOLUME_NAME (0x18)
+#define MPI2_RAID_ACTION_SET_RAID_FUNCTION_RATE (0x19)
+#define MPI2_RAID_ACTION_ENABLE_FAILED_VOLUME (0x1C)
+#define MPI2_RAID_ACTION_CREATE_HOT_SPARE (0x1D)
+#define MPI2_RAID_ACTION_DELETE_HOT_SPARE (0x1E)
+#define MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED (0x20)
+#define MPI2_RAID_ACTION_START_RAID_FUNCTION (0x21)
+#define MPI2_RAID_ACTION_STOP_RAID_FUNCTION (0x22)
+#define MPI2_RAID_ACTION_COMPATIBILITY_CHECK (0x23)
+#define MPI2_RAID_ACTION_PHYSDISK_HIDDEN (0x24)
+#define MPI2_RAID_ACTION_MIN_PRODUCT_SPECIFIC (0x80)
+#define MPI2_RAID_ACTION_MAX_PRODUCT_SPECIFIC (0xFF)
+
+/*RAID Volume Creation Structure */
+
+/*
+ *The following define can be customized for the targeted product.
+ */
+#ifndef MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS
+#define MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS (1)
+#endif
+
+typedef struct _MPI2_RAID_VOLUME_PHYSDISK {
+ U8 RAIDSetNum; /*0x00 */
+ U8 PhysDiskMap; /*0x01 */
+ U16 PhysDiskDevHandle; /*0x02 */
+} MPI2_RAID_VOLUME_PHYSDISK, *PTR_MPI2_RAID_VOLUME_PHYSDISK,
+ Mpi2RaidVolumePhysDisk_t, *pMpi2RaidVolumePhysDisk_t;
+
+/*defines for the PhysDiskMap field */
+#define MPI2_RAIDACTION_PHYSDISK_PRIMARY (0x01)
+#define MPI2_RAIDACTION_PHYSDISK_SECONDARY (0x02)
+
+typedef struct _MPI2_RAID_VOLUME_CREATION_STRUCT {
+ U8 NumPhysDisks; /*0x00 */
+ U8 VolumeType; /*0x01 */
+ U16 Reserved1; /*0x02 */
+ U32 VolumeCreationFlags; /*0x04 */
+ U32 VolumeSettings; /*0x08 */
+ U8 Reserved2; /*0x0C */
+ U8 ResyncRate; /*0x0D */
+ U16 DataScrubDuration; /*0x0E */
+ U64 VolumeMaxLBA; /*0x10 */
+ U32 StripeSize; /*0x18 */
+ U8 Name[16]; /*0x1C */
+ MPI2_RAID_VOLUME_PHYSDISK
+ PhysDisk[MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS]; /*0x2C */
+} MPI2_RAID_VOLUME_CREATION_STRUCT,
+ *PTR_MPI2_RAID_VOLUME_CREATION_STRUCT,
+ Mpi2RaidVolumeCreationStruct_t,
+ *pMpi2RaidVolumeCreationStruct_t;
+
+/*use MPI2_RAID_VOL_TYPE_ defines from mpi2_cnfg.h for VolumeType */
+
+/*defines for the VolumeCreationFlags field */
+#define MPI2_RAID_VOL_CREATION_DEFAULT_SETTINGS (0x80000000)
+#define MPI2_RAID_VOL_CREATION_BACKGROUND_INIT (0x00000004)
+#define MPI2_RAID_VOL_CREATION_LOW_LEVEL_INIT (0x00000002)
+#define MPI2_RAID_VOL_CREATION_MIGRATE_DATA (0x00000001)
+/*The following is an obsolete define.
+ *It must be shifted left 24 bits in order to set the proper bit.
+ */
+#define MPI2_RAID_VOL_CREATION_USE_DEFAULT_SETTINGS (0x80)
+
+/*RAID Online Capacity Expansion Structure */
+
+typedef struct _MPI2_RAID_ONLINE_CAPACITY_EXPANSION {
+ U32 Flags; /*0x00 */
+ U16 DevHandle0; /*0x04 */
+ U16 Reserved1; /*0x06 */
+ U16 DevHandle1; /*0x08 */
+ U16 Reserved2; /*0x0A */
+} MPI2_RAID_ONLINE_CAPACITY_EXPANSION,
+ *PTR_MPI2_RAID_ONLINE_CAPACITY_EXPANSION,
+ Mpi2RaidOnlineCapacityExpansion_t,
+ *pMpi2RaidOnlineCapacityExpansion_t;
+
+/*RAID Compatibility Input Structure */
+
+typedef struct _MPI2_RAID_COMPATIBILITY_INPUT_STRUCT {
+ U16 SourceDevHandle; /*0x00 */
+ U16 CandidateDevHandle; /*0x02 */
+ U32 Flags; /*0x04 */
+ U32 Reserved1; /*0x08 */
+ U32 Reserved2; /*0x0C */
+} MPI2_RAID_COMPATIBILITY_INPUT_STRUCT,
+ *PTR_MPI2_RAID_COMPATIBILITY_INPUT_STRUCT,
+ Mpi2RaidCompatibilityInputStruct_t,
+ *pMpi2RaidCompatibilityInputStruct_t;
+
+/*defines for RAID Compatibility Structure Flags field */
+#define MPI2_RAID_COMPAT_SOURCE_IS_VOLUME_FLAG (0x00000002)
+#define MPI2_RAID_COMPAT_REPORT_SOURCE_INFO_FLAG (0x00000001)
+
+/*RAID Volume Indicator Structure */
+
+typedef struct _MPI2_RAID_VOL_INDICATOR {
+ U64 TotalBlocks; /*0x00 */
+ U64 BlocksRemaining; /*0x08 */
+ U32 Flags; /*0x10 */
+} MPI2_RAID_VOL_INDICATOR, *PTR_MPI2_RAID_VOL_INDICATOR,
+ Mpi2RaidVolIndicator_t, *pMpi2RaidVolIndicator_t;
+
+/*defines for RAID Volume Indicator Flags field */
+#define MPI2_RAID_VOL_FLAGS_OP_MASK (0x0000000F)
+#define MPI2_RAID_VOL_FLAGS_OP_BACKGROUND_INIT (0x00000000)
+#define MPI2_RAID_VOL_FLAGS_OP_ONLINE_CAP_EXPANSION (0x00000001)
+#define MPI2_RAID_VOL_FLAGS_OP_CONSISTENCY_CHECK (0x00000002)
+#define MPI2_RAID_VOL_FLAGS_OP_RESYNC (0x00000003)
+#define MPI2_RAID_VOL_FLAGS_OP_MDC (0x00000004)
+
+/*RAID Compatibility Result Structure */
+
+typedef struct _MPI2_RAID_COMPATIBILITY_RESULT_STRUCT {
+ U8 State; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U16 Reserved2; /*0x02 */
+ U32 GenericAttributes; /*0x04 */
+ U32 OEMSpecificAttributes; /*0x08 */
+ U32 Reserved3; /*0x0C */
+ U32 Reserved4; /*0x10 */
+} MPI2_RAID_COMPATIBILITY_RESULT_STRUCT,
+ *PTR_MPI2_RAID_COMPATIBILITY_RESULT_STRUCT,
+ Mpi2RaidCompatibilityResultStruct_t,
+ *pMpi2RaidCompatibilityResultStruct_t;
+
+/*defines for RAID Compatibility Result Structure State field */
+#define MPI2_RAID_COMPAT_STATE_COMPATIBLE (0x00)
+#define MPI2_RAID_COMPAT_STATE_NOT_COMPATIBLE (0x01)
+
+/*defines for RAID Compatibility Result Structure GenericAttributes field */
+#define MPI2_RAID_COMPAT_GENATTRIB_4K_SECTOR (0x00000010)
+
+#define MPI2_RAID_COMPAT_GENATTRIB_MEDIA_MASK (0x0000000C)
+#define MPI2_RAID_COMPAT_GENATTRIB_SOLID_STATE_DRIVE (0x00000008)
+#define MPI2_RAID_COMPAT_GENATTRIB_HARD_DISK_DRIVE (0x00000004)
+
+#define MPI2_RAID_COMPAT_GENATTRIB_PROTOCOL_MASK (0x00000003)
+#define MPI2_RAID_COMPAT_GENATTRIB_SAS_PROTOCOL (0x00000002)
+#define MPI2_RAID_COMPAT_GENATTRIB_SATA_PROTOCOL (0x00000001)
+
+/*RAID Action Reply ActionData union */
+typedef union _MPI2_RAID_ACTION_REPLY_DATA {
+ U32 Word[5];
+ MPI2_RAID_VOL_INDICATOR RaidVolumeIndicator;
+ U16 VolDevHandle;
+ U8 VolumeState;
+ U8 PhysDiskNum;
+ MPI2_RAID_COMPATIBILITY_RESULT_STRUCT RaidCompatibilityResult;
+} MPI2_RAID_ACTION_REPLY_DATA, *PTR_MPI2_RAID_ACTION_REPLY_DATA,
+ Mpi2RaidActionReplyData_t, *pMpi2RaidActionReplyData_t;
+
+/*use MPI2_RAIDVOL0_SETTING_ defines from mpi2_cnfg.h for
+ *MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE action */
+
+/*RAID Action Reply Message */
+typedef struct _MPI2_RAID_ACTION_REPLY {
+ U8 Action; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 VolDevHandle; /*0x04 */
+ U8 PhysDiskNum; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved2; /*0x0A */
+ U16 Reserved3; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ MPI2_RAID_ACTION_REPLY_DATA ActionData; /*0x14 */
+} MPI2_RAID_ACTION_REPLY, *PTR_MPI2_RAID_ACTION_REPLY,
+ Mpi2RaidActionReply_t, *pMpi2RaidActionReply_t;
+
+#endif
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_sas.h b/drivers/scsi/mpt3sas/mpi/mpi2_sas.h
new file mode 100644
index 00000000000..b4e7084aba3
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_sas.h
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2000-2012 LSI Corporation.
+ *
+ *
+ * Name: mpi2_sas.h
+ * Title: MPI Serial Attached SCSI structures and definitions
+ * Creation Date: February 9, 2007
+ *
+ * mpi2_sas.h Version: 02.00.07
+ *
+ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25
+ * prefix are for use only on MPI v2.5 products, and must not be used
+ * with MPI v2.0 products. Unless otherwise noted, names beginning with
+ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products.
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 06-26-07 02.00.01 Added Clear All Persistent Operation to SAS IO Unit
+ * Control Request.
+ * 10-02-08 02.00.02 Added Set IOC Parameter Operation to SAS IO Unit Control
+ * Request.
+ * 10-28-09 02.00.03 Changed the type of SGL in MPI2_SATA_PASSTHROUGH_REQUEST
+ * to MPI2_SGE_IO_UNION since it supports chained SGLs.
+ * 05-12-10 02.00.04 Modified some comments.
+ * 08-11-10 02.00.05 Added NCQ operations to SAS IO Unit Control.
+ * 11-18-11 02.00.06 Incorporating additions for MPI v2.5.
+ * 07-10-12 02.00.07 Added MPI2_SATA_PT_SGE_UNION for use in the SATA
+ * Passthrough Request message.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_SAS_H
+#define MPI2_SAS_H
+
+/*
+ *Values for SASStatus.
+ */
+#define MPI2_SASSTATUS_SUCCESS (0x00)
+#define MPI2_SASSTATUS_UNKNOWN_ERROR (0x01)
+#define MPI2_SASSTATUS_INVALID_FRAME (0x02)
+#define MPI2_SASSTATUS_UTC_BAD_DEST (0x03)
+#define MPI2_SASSTATUS_UTC_BREAK_RECEIVED (0x04)
+#define MPI2_SASSTATUS_UTC_CONNECT_RATE_NOT_SUPPORTED (0x05)
+#define MPI2_SASSTATUS_UTC_PORT_LAYER_REQUEST (0x06)
+#define MPI2_SASSTATUS_UTC_PROTOCOL_NOT_SUPPORTED (0x07)
+#define MPI2_SASSTATUS_UTC_STP_RESOURCES_BUSY (0x08)
+#define MPI2_SASSTATUS_UTC_WRONG_DESTINATION (0x09)
+#define MPI2_SASSTATUS_SHORT_INFORMATION_UNIT (0x0A)
+#define MPI2_SASSTATUS_LONG_INFORMATION_UNIT (0x0B)
+#define MPI2_SASSTATUS_XFER_RDY_INCORRECT_WRITE_DATA (0x0C)
+#define MPI2_SASSTATUS_XFER_RDY_REQUEST_OFFSET_ERROR (0x0D)
+#define MPI2_SASSTATUS_XFER_RDY_NOT_EXPECTED (0x0E)
+#define MPI2_SASSTATUS_DATA_INCORRECT_DATA_LENGTH (0x0F)
+#define MPI2_SASSTATUS_DATA_TOO_MUCH_READ_DATA (0x10)
+#define MPI2_SASSTATUS_DATA_OFFSET_ERROR (0x11)
+#define MPI2_SASSTATUS_SDSF_NAK_RECEIVED (0x12)
+#define MPI2_SASSTATUS_SDSF_CONNECTION_FAILED (0x13)
+#define MPI2_SASSTATUS_INITIATOR_RESPONSE_TIMEOUT (0x14)
+
+/*
+ *Values for the SAS DeviceInfo field used in SAS Device Status Change Event
+ *data and SAS Configuration pages.
+ */
+#define MPI2_SAS_DEVICE_INFO_SEP (0x00004000)
+#define MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE (0x00002000)
+#define MPI2_SAS_DEVICE_INFO_LSI_DEVICE (0x00001000)
+#define MPI2_SAS_DEVICE_INFO_DIRECT_ATTACH (0x00000800)
+#define MPI2_SAS_DEVICE_INFO_SSP_TARGET (0x00000400)
+#define MPI2_SAS_DEVICE_INFO_STP_TARGET (0x00000200)
+#define MPI2_SAS_DEVICE_INFO_SMP_TARGET (0x00000100)
+#define MPI2_SAS_DEVICE_INFO_SATA_DEVICE (0x00000080)
+#define MPI2_SAS_DEVICE_INFO_SSP_INITIATOR (0x00000040)
+#define MPI2_SAS_DEVICE_INFO_STP_INITIATOR (0x00000020)
+#define MPI2_SAS_DEVICE_INFO_SMP_INITIATOR (0x00000010)
+#define MPI2_SAS_DEVICE_INFO_SATA_HOST (0x00000008)
+
+#define MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE (0x00000007)
+#define MPI2_SAS_DEVICE_INFO_NO_DEVICE (0x00000000)
+#define MPI2_SAS_DEVICE_INFO_END_DEVICE (0x00000001)
+#define MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER (0x00000002)
+#define MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER (0x00000003)
+
+/*****************************************************************************
+*
+* SAS Messages
+*
+*****************************************************************************/
+
+/****************************************************************************
+* SMP Passthrough messages
+****************************************************************************/
+
+/*SMP Passthrough Request Message */
+typedef struct _MPI2_SMP_PASSTHROUGH_REQUEST {
+ U8 PassthroughFlags; /*0x00 */
+ U8 PhysicalPort; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 RequestDataLength; /*0x04 */
+ U8 SGLFlags; /*0x06*//*MPI v2.0 only. Reserved on MPI v2.5*/
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U32 Reserved2; /*0x0C */
+ U64 SASAddress; /*0x10 */
+ U32 Reserved3; /*0x18 */
+ U32 Reserved4; /*0x1C */
+ MPI2_SIMPLE_SGE_UNION SGL;/*0x20 */
+} MPI2_SMP_PASSTHROUGH_REQUEST, *PTR_MPI2_SMP_PASSTHROUGH_REQUEST,
+ Mpi2SmpPassthroughRequest_t, *pMpi2SmpPassthroughRequest_t;
+
+/*values for PassthroughFlags field */
+#define MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE (0x80)
+
+/*MPI v2.0: use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
+
+/*SMP Passthrough Reply Message */
+typedef struct _MPI2_SMP_PASSTHROUGH_REPLY {
+ U8 PassthroughFlags; /*0x00 */
+ U8 PhysicalPort; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 ResponseDataLength; /*0x04 */
+ U8 SGLFlags; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U8 Reserved2; /*0x0C */
+ U8 SASStatus; /*0x0D */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U32 Reserved3; /*0x14 */
+ U8 ResponseData[4]; /*0x18 */
+} MPI2_SMP_PASSTHROUGH_REPLY, *PTR_MPI2_SMP_PASSTHROUGH_REPLY,
+ Mpi2SmpPassthroughReply_t, *pMpi2SmpPassthroughReply_t;
+
+/*values for PassthroughFlags field */
+#define MPI2_SMP_PT_REPLY_PT_FLAGS_IMMEDIATE (0x80)
+
+/*values for SASStatus field are at the top of this file */
+
+/****************************************************************************
+* SATA Passthrough messages
+****************************************************************************/
+
+typedef union _MPI2_SATA_PT_SGE_UNION {
+ MPI2_SGE_SIMPLE_UNION MpiSimple; /*MPI v2.0 only */
+ MPI2_SGE_CHAIN_UNION MpiChain; /*MPI v2.0 only */
+ MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple;
+ MPI2_IEEE_SGE_CHAIN_UNION IeeeChain; /*MPI v2.0 only */
+ MPI25_IEEE_SGE_CHAIN64 IeeeChain64; /*MPI v2.5 only */
+} MPI2_SATA_PT_SGE_UNION, *PTR_MPI2_SATA_PT_SGE_UNION,
+ Mpi2SataPTSGEUnion_t, *pMpi2SataPTSGEUnion_t;
+
+/*SATA Passthrough Request Message */
+typedef struct _MPI2_SATA_PASSTHROUGH_REQUEST {
+ U16 DevHandle; /*0x00 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 PassthroughFlags; /*0x04 */
+ U8 SGLFlags; /*0x06*//*MPI v2.0 only. Reserved on MPI v2.5*/
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U32 Reserved2; /*0x0C */
+ U32 Reserved3; /*0x10 */
+ U32 Reserved4; /*0x14 */
+ U32 DataLength; /*0x18 */
+ U8 CommandFIS[20]; /*0x1C */
+ MPI2_SATA_PT_SGE_UNION SGL;/*0x30*//*MPI v2.5: IEEE 64 elements only*/
+} MPI2_SATA_PASSTHROUGH_REQUEST, *PTR_MPI2_SATA_PASSTHROUGH_REQUEST,
+ Mpi2SataPassthroughRequest_t,
+ *pMpi2SataPassthroughRequest_t;
+
+/*values for PassthroughFlags field */
+#define MPI2_SATA_PT_REQ_PT_FLAGS_EXECUTE_DIAG (0x0100)
+#define MPI2_SATA_PT_REQ_PT_FLAGS_DMA (0x0020)
+#define MPI2_SATA_PT_REQ_PT_FLAGS_PIO (0x0010)
+#define MPI2_SATA_PT_REQ_PT_FLAGS_UNSPECIFIED_VU (0x0004)
+#define MPI2_SATA_PT_REQ_PT_FLAGS_WRITE (0x0002)
+#define MPI2_SATA_PT_REQ_PT_FLAGS_READ (0x0001)
+
+/*MPI v2.0: use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
+
+/*SATA Passthrough Reply Message */
+typedef struct _MPI2_SATA_PASSTHROUGH_REPLY {
+ U16 DevHandle; /*0x00 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 PassthroughFlags; /*0x04 */
+ U8 SGLFlags; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved1; /*0x0A */
+ U8 Reserved2; /*0x0C */
+ U8 SASStatus; /*0x0D */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U8 StatusFIS[20]; /*0x14 */
+ U32 StatusControlRegisters; /*0x28 */
+ U32 TransferCount; /*0x2C */
+} MPI2_SATA_PASSTHROUGH_REPLY, *PTR_MPI2_SATA_PASSTHROUGH_REPLY,
+ Mpi2SataPassthroughReply_t, *pMpi2SataPassthroughReply_t;
+
+/*values for SASStatus field are at the top of this file */
+
+/****************************************************************************
+* SAS IO Unit Control messages
+****************************************************************************/
+
+/*SAS IO Unit Control Request Message */
+typedef struct _MPI2_SAS_IOUNIT_CONTROL_REQUEST {
+ U8 Operation; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 DevHandle; /*0x04 */
+ U8 IOCParameter; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+ U16 Reserved4; /*0x0C */
+ U8 PhyNum; /*0x0E */
+ U8 PrimFlags; /*0x0F */
+ U32 Primitive; /*0x10 */
+ U8 LookupMethod; /*0x14 */
+ U8 Reserved5; /*0x15 */
+ U16 SlotNumber; /*0x16 */
+ U64 LookupAddress; /*0x18 */
+ U32 IOCParameterValue; /*0x20 */
+ U32 Reserved7; /*0x24 */
+ U32 Reserved8; /*0x28 */
+} MPI2_SAS_IOUNIT_CONTROL_REQUEST,
+ *PTR_MPI2_SAS_IOUNIT_CONTROL_REQUEST,
+ Mpi2SasIoUnitControlRequest_t,
+ *pMpi2SasIoUnitControlRequest_t;
+
+/*values for the Operation field */
+#define MPI2_SAS_OP_CLEAR_ALL_PERSISTENT (0x02)
+#define MPI2_SAS_OP_PHY_LINK_RESET (0x06)
+#define MPI2_SAS_OP_PHY_HARD_RESET (0x07)
+#define MPI2_SAS_OP_PHY_CLEAR_ERROR_LOG (0x08)
+#define MPI2_SAS_OP_SEND_PRIMITIVE (0x0A)
+#define MPI2_SAS_OP_FORCE_FULL_DISCOVERY (0x0B)
+#define MPI2_SAS_OP_TRANSMIT_PORT_SELECT_SIGNAL (0x0C)
+#define MPI2_SAS_OP_REMOVE_DEVICE (0x0D)
+#define MPI2_SAS_OP_LOOKUP_MAPPING (0x0E)
+#define MPI2_SAS_OP_SET_IOC_PARAMETER (0x0F)
+#define MPI25_SAS_OP_ENABLE_FP_DEVICE (0x10)
+#define MPI25_SAS_OP_DISABLE_FP_DEVICE (0x11)
+#define MPI25_SAS_OP_ENABLE_FP_ALL (0x12)
+#define MPI25_SAS_OP_DISABLE_FP_ALL (0x13)
+#define MPI2_SAS_OP_DEV_ENABLE_NCQ (0x14)
+#define MPI2_SAS_OP_DEV_DISABLE_NCQ (0x15)
+#define MPI2_SAS_OP_PRODUCT_SPECIFIC_MIN (0x80)
+
+/*values for the PrimFlags field */
+#define MPI2_SAS_PRIMFLAGS_SINGLE (0x08)
+#define MPI2_SAS_PRIMFLAGS_TRIPLE (0x02)
+#define MPI2_SAS_PRIMFLAGS_REDUNDANT (0x01)
+
+/*values for the LookupMethod field */
+#define MPI2_SAS_LOOKUP_METHOD_SAS_ADDRESS (0x01)
+#define MPI2_SAS_LOOKUP_METHOD_SAS_ENCLOSURE_SLOT (0x02)
+#define MPI2_SAS_LOOKUP_METHOD_SAS_DEVICE_NAME (0x03)
+
+/*SAS IO Unit Control Reply Message */
+typedef struct _MPI2_SAS_IOUNIT_CONTROL_REPLY {
+ U8 Operation; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 DevHandle; /*0x04 */
+ U8 IOCParameter; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved3; /*0x0A */
+ U16 Reserved4; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_SAS_IOUNIT_CONTROL_REPLY,
+ *PTR_MPI2_SAS_IOUNIT_CONTROL_REPLY,
+ Mpi2SasIoUnitControlReply_t, *pMpi2SasIoUnitControlReply_t;
+
+#endif
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_tool.h b/drivers/scsi/mpt3sas/mpi/mpi2_tool.h
new file mode 100644
index 00000000000..71453d11c1c
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_tool.h
@@ -0,0 +1,437 @@
+/*
+ * Copyright (c) 2000-2012 LSI Corporation.
+ *
+ *
+ * Name: mpi2_tool.h
+ * Title: MPI diagnostic tool structures and definitions
+ * Creation Date: March 26, 2007
+ *
+ * mpi2_tool.h Version: 02.00.09
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * 12-18-07 02.00.01 Added Diagnostic Buffer Post and Diagnostic Release
+ * structures and defines.
+ * 02-29-08 02.00.02 Modified various names to make them 32-character unique.
+ * 05-06-09 02.00.03 Added ISTWI Read Write Tool and Diagnostic CLI Tool.
+ * 07-30-09 02.00.04 Added ExtendedType field to DiagnosticBufferPost request
+ * and reply messages.
+ * Added MPI2_DIAG_BUF_TYPE_EXTENDED.
+ * Incremented MPI2_DIAG_BUF_TYPE_COUNT.
+ * 05-12-10 02.00.05 Added Diagnostic Data Upload tool.
+ * 08-11-10 02.00.06 Added defines that were missing for Diagnostic Buffer
+ * Post Request.
+ * 05-25-11 02.00.07 Added Flags field and related defines to
+ * MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST.
+ * 11-18-11 02.00.08 Incorporating additions for MPI v2.5.
+ * 07-10-12 02.00.09 Add MPI v2.5 Toolbox Diagnostic CLI Tool Request
+ * message.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_TOOL_H
+#define MPI2_TOOL_H
+
+/*****************************************************************************
+*
+* Toolbox Messages
+*
+*****************************************************************************/
+
+/*defines for the Tools */
+#define MPI2_TOOLBOX_CLEAN_TOOL (0x00)
+#define MPI2_TOOLBOX_MEMORY_MOVE_TOOL (0x01)
+#define MPI2_TOOLBOX_DIAG_DATA_UPLOAD_TOOL (0x02)
+#define MPI2_TOOLBOX_ISTWI_READ_WRITE_TOOL (0x03)
+#define MPI2_TOOLBOX_BEACON_TOOL (0x05)
+#define MPI2_TOOLBOX_DIAGNOSTIC_CLI_TOOL (0x06)
+
+/****************************************************************************
+* Toolbox reply
+****************************************************************************/
+
+typedef struct _MPI2_TOOLBOX_REPLY {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_TOOLBOX_REPLY, *PTR_MPI2_TOOLBOX_REPLY,
+ Mpi2ToolboxReply_t, *pMpi2ToolboxReply_t;
+
+/****************************************************************************
+* Toolbox Clean Tool request
+****************************************************************************/
+
+typedef struct _MPI2_TOOLBOX_CLEAN_REQUEST {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U32 Flags; /*0x0C */
+} MPI2_TOOLBOX_CLEAN_REQUEST, *PTR_MPI2_TOOLBOX_CLEAN_REQUEST,
+ Mpi2ToolboxCleanRequest_t, *pMpi2ToolboxCleanRequest_t;
+
+/*values for the Flags field */
+#define MPI2_TOOLBOX_CLEAN_BOOT_SERVICES (0x80000000)
+#define MPI2_TOOLBOX_CLEAN_PERSIST_MANUFACT_PAGES (0x40000000)
+#define MPI2_TOOLBOX_CLEAN_OTHER_PERSIST_PAGES (0x20000000)
+#define MPI2_TOOLBOX_CLEAN_FW_CURRENT (0x10000000)
+#define MPI2_TOOLBOX_CLEAN_FW_BACKUP (0x08000000)
+#define MPI2_TOOLBOX_CLEAN_MEGARAID (0x02000000)
+#define MPI2_TOOLBOX_CLEAN_INITIALIZATION (0x01000000)
+#define MPI2_TOOLBOX_CLEAN_FLASH (0x00000004)
+#define MPI2_TOOLBOX_CLEAN_SEEPROM (0x00000002)
+#define MPI2_TOOLBOX_CLEAN_NVSRAM (0x00000001)
+
+/****************************************************************************
+* Toolbox Memory Move request
+****************************************************************************/
+
+typedef struct _MPI2_TOOLBOX_MEM_MOVE_REQUEST {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ MPI2_SGE_SIMPLE_UNION SGL; /*0x0C */
+} MPI2_TOOLBOX_MEM_MOVE_REQUEST, *PTR_MPI2_TOOLBOX_MEM_MOVE_REQUEST,
+ Mpi2ToolboxMemMoveRequest_t, *pMpi2ToolboxMemMoveRequest_t;
+
+/****************************************************************************
+* Toolbox Diagnostic Data Upload request
+****************************************************************************/
+
+typedef struct _MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U8 SGLFlags; /*0x0C */
+ U8 Reserved5; /*0x0D */
+ U16 Reserved6; /*0x0E */
+ U32 Flags; /*0x10 */
+ U32 DataLength; /*0x14 */
+ MPI2_SGE_SIMPLE_UNION SGL; /*0x18 */
+} MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST,
+ *PTR_MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST,
+ Mpi2ToolboxDiagDataUploadRequest_t,
+ *pMpi2ToolboxDiagDataUploadRequest_t;
+
+/*use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
+
+typedef struct _MPI2_DIAG_DATA_UPLOAD_HEADER {
+ U32 DiagDataLength; /*00h */
+ U8 FormatCode; /*04h */
+ U8 Reserved1; /*05h */
+ U16 Reserved2; /*06h */
+} MPI2_DIAG_DATA_UPLOAD_HEADER, *PTR_MPI2_DIAG_DATA_UPLOAD_HEADER,
+ Mpi2DiagDataUploadHeader_t, *pMpi2DiagDataUploadHeader_t;
+
+/****************************************************************************
+* Toolbox ISTWI Read Write Tool
+****************************************************************************/
+
+/*Toolbox ISTWI Read Write Tool request message */
+typedef struct _MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U32 Reserved5; /*0x0C */
+ U32 Reserved6; /*0x10 */
+ U8 DevIndex; /*0x14 */
+ U8 Action; /*0x15 */
+ U8 SGLFlags; /*0x16 */
+ U8 Flags; /*0x17 */
+ U16 TxDataLength; /*0x18 */
+ U16 RxDataLength; /*0x1A */
+ U32 Reserved8; /*0x1C */
+ U32 Reserved9; /*0x20 */
+ U32 Reserved10; /*0x24 */
+ U32 Reserved11; /*0x28 */
+ U32 Reserved12; /*0x2C */
+ MPI2_SGE_SIMPLE_UNION SGL; /*0x30 */
+} MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST,
+ *PTR_MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST,
+ Mpi2ToolboxIstwiReadWriteRequest_t,
+ *pMpi2ToolboxIstwiReadWriteRequest_t;
+
+/*values for the Action field */
+#define MPI2_TOOL_ISTWI_ACTION_READ_DATA (0x01)
+#define MPI2_TOOL_ISTWI_ACTION_WRITE_DATA (0x02)
+#define MPI2_TOOL_ISTWI_ACTION_SEQUENCE (0x03)
+#define MPI2_TOOL_ISTWI_ACTION_RESERVE_BUS (0x10)
+#define MPI2_TOOL_ISTWI_ACTION_RELEASE_BUS (0x11)
+#define MPI2_TOOL_ISTWI_ACTION_RESET (0x12)
+
+/*use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
+
+/*values for the Flags field */
+#define MPI2_TOOL_ISTWI_FLAG_AUTO_RESERVE_RELEASE (0x80)
+#define MPI2_TOOL_ISTWI_FLAG_PAGE_ADDR_MASK (0x07)
+
+/*Toolbox ISTWI Read Write Tool reply message */
+typedef struct _MPI2_TOOLBOX_ISTWI_REPLY {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U8 DevIndex; /*0x14 */
+ U8 Action; /*0x15 */
+ U8 IstwiStatus; /*0x16 */
+ U8 Reserved6; /*0x17 */
+ U16 TxDataCount; /*0x18 */
+ U16 RxDataCount; /*0x1A */
+} MPI2_TOOLBOX_ISTWI_REPLY, *PTR_MPI2_TOOLBOX_ISTWI_REPLY,
+ Mpi2ToolboxIstwiReply_t, *pMpi2ToolboxIstwiReply_t;
+
+/****************************************************************************
+* Toolbox Beacon Tool request
+****************************************************************************/
+
+typedef struct _MPI2_TOOLBOX_BEACON_REQUEST {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U8 Reserved5; /*0x0C */
+ U8 PhysicalPort; /*0x0D */
+ U8 Reserved6; /*0x0E */
+ U8 Flags; /*0x0F */
+} MPI2_TOOLBOX_BEACON_REQUEST, *PTR_MPI2_TOOLBOX_BEACON_REQUEST,
+ Mpi2ToolboxBeaconRequest_t, *pMpi2ToolboxBeaconRequest_t;
+
+/*values for the Flags field */
+#define MPI2_TOOLBOX_FLAGS_BEACONMODE_OFF (0x00)
+#define MPI2_TOOLBOX_FLAGS_BEACONMODE_ON (0x01)
+
+/****************************************************************************
+* Toolbox Diagnostic CLI Tool
+****************************************************************************/
+
+#define MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH (0x5C)
+
+/*MPI v2.0 Toolbox Diagnostic CLI Tool request message */
+typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U8 SGLFlags; /*0x0C */
+ U8 Reserved5; /*0x0D */
+ U16 Reserved6; /*0x0E */
+ U32 DataLength; /*0x10 */
+ U8 DiagnosticCliCommand[MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH];/*0x14 */
+ MPI2_SGE_SIMPLE_UNION SGL; /*0x70 */
+} MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST,
+ *PTR_MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST,
+ Mpi2ToolboxDiagnosticCliRequest_t,
+ *pMpi2ToolboxDiagnosticCliRequest_t;
+
+/*use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */
+
+/*MPI v2.5 Toolbox Diagnostic CLI Tool request message */
+typedef struct _MPI25_TOOLBOX_DIAGNOSTIC_CLI_REQUEST {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U32 Reserved5; /*0x0C */
+ U32 DataLength; /*0x10 */
+ U8 DiagnosticCliCommand[MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH];/*0x14 */
+ MPI25_SGE_IO_UNION SGL; /*0x70 */
+} MPI25_TOOLBOX_DIAGNOSTIC_CLI_REQUEST,
+ *PTR_MPI25_TOOLBOX_DIAGNOSTIC_CLI_REQUEST,
+ Mpi25ToolboxDiagnosticCliRequest_t,
+ *pMpi25ToolboxDiagnosticCliRequest_t;
+
+/*Toolbox Diagnostic CLI Tool reply message */
+typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY {
+ U8 Tool; /*0x00 */
+ U8 Reserved1; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U32 ReturnedDataLength; /*0x14 */
+} MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY,
+ *PTR_MPI2_TOOLBOX_DIAG_CLI_REPLY,
+ Mpi2ToolboxDiagnosticCliReply_t,
+ *pMpi2ToolboxDiagnosticCliReply_t;
+
+/*****************************************************************************
+*
+* Diagnostic Buffer Messages
+*
+*****************************************************************************/
+
+/****************************************************************************
+* Diagnostic Buffer Post request
+****************************************************************************/
+
+typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST {
+ U8 ExtendedType; /*0x00 */
+ U8 BufferType; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U64 BufferAddress; /*0x0C */
+ U32 BufferLength; /*0x14 */
+ U32 Reserved5; /*0x18 */
+ U32 Reserved6; /*0x1C */
+ U32 Flags; /*0x20 */
+ U32 ProductSpecific[23]; /*0x24 */
+} MPI2_DIAG_BUFFER_POST_REQUEST, *PTR_MPI2_DIAG_BUFFER_POST_REQUEST,
+ Mpi2DiagBufferPostRequest_t, *pMpi2DiagBufferPostRequest_t;
+
+/*values for the ExtendedType field */
+#define MPI2_DIAG_EXTENDED_TYPE_UTILIZATION (0x02)
+
+/*values for the BufferType field */
+#define MPI2_DIAG_BUF_TYPE_TRACE (0x00)
+#define MPI2_DIAG_BUF_TYPE_SNAPSHOT (0x01)
+#define MPI2_DIAG_BUF_TYPE_EXTENDED (0x02)
+/*count of the number of buffer types */
+#define MPI2_DIAG_BUF_TYPE_COUNT (0x03)
+
+/*values for the Flags field */
+#define MPI2_DIAG_BUF_FLAG_RELEASE_ON_FULL (0x00000002)
+#define MPI2_DIAG_BUF_FLAG_IMMEDIATE_RELEASE (0x00000001)
+
+/****************************************************************************
+* Diagnostic Buffer Post reply
+****************************************************************************/
+
+typedef struct _MPI2_DIAG_BUFFER_POST_REPLY {
+ U8 ExtendedType; /*0x00 */
+ U8 BufferType; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+ U32 TransferLength; /*0x14 */
+} MPI2_DIAG_BUFFER_POST_REPLY, *PTR_MPI2_DIAG_BUFFER_POST_REPLY,
+ Mpi2DiagBufferPostReply_t, *pMpi2DiagBufferPostReply_t;
+
+/****************************************************************************
+* Diagnostic Release request
+****************************************************************************/
+
+typedef struct _MPI2_DIAG_RELEASE_REQUEST {
+ U8 Reserved1; /*0x00 */
+ U8 BufferType; /*0x01 */
+ U8 ChainOffset; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+} MPI2_DIAG_RELEASE_REQUEST, *PTR_MPI2_DIAG_RELEASE_REQUEST,
+ Mpi2DiagReleaseRequest_t, *pMpi2DiagReleaseRequest_t;
+
+/****************************************************************************
+* Diagnostic Buffer Post reply
+****************************************************************************/
+
+typedef struct _MPI2_DIAG_RELEASE_REPLY {
+ U8 Reserved1; /*0x00 */
+ U8 BufferType; /*0x01 */
+ U8 MsgLength; /*0x02 */
+ U8 Function; /*0x03 */
+ U16 Reserved2; /*0x04 */
+ U8 Reserved3; /*0x06 */
+ U8 MsgFlags; /*0x07 */
+ U8 VP_ID; /*0x08 */
+ U8 VF_ID; /*0x09 */
+ U16 Reserved4; /*0x0A */
+ U16 Reserved5; /*0x0C */
+ U16 IOCStatus; /*0x0E */
+ U32 IOCLogInfo; /*0x10 */
+} MPI2_DIAG_RELEASE_REPLY, *PTR_MPI2_DIAG_RELEASE_REPLY,
+ Mpi2DiagReleaseReply_t, *pMpi2DiagReleaseReply_t;
+
+#endif
diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_type.h b/drivers/scsi/mpt3sas/mpi/mpi2_type.h
new file mode 100644
index 00000000000..516f959573f
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpi/mpi2_type.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2000-2007 LSI Corporation.
+ *
+ *
+ * Name: mpi2_type.h
+ * Title: MPI basic type definitions
+ * Creation Date: August 16, 2006
+ *
+ * mpi2_type.h Version: 02.00.00
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_TYPE_H
+#define MPI2_TYPE_H
+
+/*******************************************************************************
+ * Define * if it hasn't already been defined. By default
+ * * is defined to be a near pointer. MPI2_POINTER can be defined as
+ * a far pointer by defining * as "far *" before this header file is
+ * included.
+ */
+
+/* the basic types may have already been included by mpi_type.h */
+#ifndef MPI_TYPE_H
+/*****************************************************************************
+*
+* Basic Types
+*
+*****************************************************************************/
+
+typedef u8 U8;
+typedef __le16 U16;
+typedef __le32 U32;
+typedef __le64 U64 __attribute__ ((aligned(4)));
+
+/*****************************************************************************
+*
+* Pointer Types
+*
+*****************************************************************************/
+
+typedef U8 *PU8;
+typedef U16 *PU16;
+typedef U32 *PU32;
+typedef U64 *PU64;
+
+#endif
+
+#endif
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c
new file mode 100644
index 00000000000..04f8010f077
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.c
@@ -0,0 +1,4840 @@
+/*
+ * This is the Fusion MPT base driver providing common API layer interface
+ * for access to MPT (Message Passing Technology) firmware.
+ *
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_base.c
+ * Copyright (C) 2012 LSI Corporation
+ * (mailto:DL-MPTFusionLinux@lsi.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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/time.h>
+#include <linux/kthread.h>
+#include <linux/aer.h>
+
+
+#include "mpt3sas_base.h"
+
+static MPT_CALLBACK mpt_callbacks[MPT_MAX_CALLBACKS];
+
+
+#define FAULT_POLLING_INTERVAL 1000 /* in milliseconds */
+
+ /* maximum controller queue depth */
+#define MAX_HBA_QUEUE_DEPTH 30000
+#define MAX_CHAIN_DEPTH 100000
+static int max_queue_depth = -1;
+module_param(max_queue_depth, int, 0);
+MODULE_PARM_DESC(max_queue_depth, " max controller queue depth ");
+
+static int max_sgl_entries = -1;
+module_param(max_sgl_entries, int, 0);
+MODULE_PARM_DESC(max_sgl_entries, " max sg entries ");
+
+static int msix_disable = -1;
+module_param(msix_disable, int, 0);
+MODULE_PARM_DESC(msix_disable, " disable msix routed interrupts (default=0)");
+
+
+static int mpt3sas_fwfault_debug;
+MODULE_PARM_DESC(mpt3sas_fwfault_debug,
+ " enable detection of firmware fault and halt firmware - (default=0)");
+
+
+/**
+ * _scsih_set_fwfault_debug - global setting of ioc->fwfault_debug.
+ *
+ */
+static int
+_scsih_set_fwfault_debug(const char *val, struct kernel_param *kp)
+{
+ int ret = param_set_int(val, kp);
+ struct MPT3SAS_ADAPTER *ioc;
+
+ if (ret)
+ return ret;
+
+ pr_info("setting fwfault_debug(%d)\n", mpt3sas_fwfault_debug);
+ list_for_each_entry(ioc, &mpt3sas_ioc_list, list)
+ ioc->fwfault_debug = mpt3sas_fwfault_debug;
+ return 0;
+}
+module_param_call(mpt3sas_fwfault_debug, _scsih_set_fwfault_debug,
+ param_get_int, &mpt3sas_fwfault_debug, 0644);
+
+/**
+ * mpt3sas_remove_dead_ioc_func - kthread context to remove dead ioc
+ * @arg: input argument, used to derive ioc
+ *
+ * Return 0 if controller is removed from pci subsystem.
+ * Return -1 for other case.
+ */
+static int mpt3sas_remove_dead_ioc_func(void *arg)
+{
+ struct MPT3SAS_ADAPTER *ioc = (struct MPT3SAS_ADAPTER *)arg;
+ struct pci_dev *pdev;
+
+ if ((ioc == NULL))
+ return -1;
+
+ pdev = ioc->pdev;
+ if ((pdev == NULL))
+ return -1;
+ pci_stop_and_remove_bus_device(pdev);
+ return 0;
+}
+
+/**
+ * _base_fault_reset_work - workq handling ioc fault conditions
+ * @work: input argument, used to derive ioc
+ * Context: sleep.
+ *
+ * Return nothing.
+ */
+static void
+_base_fault_reset_work(struct work_struct *work)
+{
+ struct MPT3SAS_ADAPTER *ioc =
+ container_of(work, struct MPT3SAS_ADAPTER, fault_reset_work.work);
+ unsigned long flags;
+ u32 doorbell;
+ int rc;
+ struct task_struct *p;
+
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ if (ioc->shost_recovery)
+ goto rearm_timer;
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+
+ doorbell = mpt3sas_base_get_iocstate(ioc, 0);
+ if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_MASK) {
+ pr_err(MPT3SAS_FMT "SAS host is non-operational !!!!\n",
+ ioc->name);
+
+ /*
+ * Call _scsih_flush_pending_cmds callback so that we flush all
+ * pending commands back to OS. This call is required to aovid
+ * deadlock at block layer. Dead IOC will fail to do diag reset,
+ * and this call is safe since dead ioc will never return any
+ * command back from HW.
+ */
+ ioc->schedule_dead_ioc_flush_running_cmds(ioc);
+ /*
+ * Set remove_host flag early since kernel thread will
+ * take some time to execute.
+ */
+ ioc->remove_host = 1;
+ /*Remove the Dead Host */
+ p = kthread_run(mpt3sas_remove_dead_ioc_func, ioc,
+ "mpt3sas_dead_ioc_%d", ioc->id);
+ if (IS_ERR(p))
+ pr_err(MPT3SAS_FMT
+ "%s: Running mpt3sas_dead_ioc thread failed !!!!\n",
+ ioc->name, __func__);
+ else
+ pr_err(MPT3SAS_FMT
+ "%s: Running mpt3sas_dead_ioc thread success !!!!\n",
+ ioc->name, __func__);
+ return; /* don't rearm timer */
+ }
+
+ if ((doorbell & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_OPERATIONAL) {
+ rc = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ pr_warn(MPT3SAS_FMT "%s: hard reset: %s\n", ioc->name,
+ __func__, (rc == 0) ? "success" : "failed");
+ doorbell = mpt3sas_base_get_iocstate(ioc, 0);
+ if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT)
+ mpt3sas_base_fault_info(ioc, doorbell &
+ MPI2_DOORBELL_DATA_MASK);
+ if (rc && (doorbell & MPI2_IOC_STATE_MASK) !=
+ MPI2_IOC_STATE_OPERATIONAL)
+ return; /* don't rearm timer */
+ }
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ rearm_timer:
+ if (ioc->fault_reset_work_q)
+ queue_delayed_work(ioc->fault_reset_work_q,
+ &ioc->fault_reset_work,
+ msecs_to_jiffies(FAULT_POLLING_INTERVAL));
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+}
+
+/**
+ * mpt3sas_base_start_watchdog - start the fault_reset_work_q
+ * @ioc: per adapter object
+ * Context: sleep.
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_base_start_watchdog(struct MPT3SAS_ADAPTER *ioc)
+{
+ unsigned long flags;
+
+ if (ioc->fault_reset_work_q)
+ return;
+
+ /* initialize fault polling */
+
+ INIT_DELAYED_WORK(&ioc->fault_reset_work, _base_fault_reset_work);
+ snprintf(ioc->fault_reset_work_q_name,
+ sizeof(ioc->fault_reset_work_q_name), "poll_%d_status", ioc->id);
+ ioc->fault_reset_work_q =
+ create_singlethread_workqueue(ioc->fault_reset_work_q_name);
+ if (!ioc->fault_reset_work_q) {
+ pr_err(MPT3SAS_FMT "%s: failed (line=%d)\n",
+ ioc->name, __func__, __LINE__);
+ return;
+ }
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ if (ioc->fault_reset_work_q)
+ queue_delayed_work(ioc->fault_reset_work_q,
+ &ioc->fault_reset_work,
+ msecs_to_jiffies(FAULT_POLLING_INTERVAL));
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+}
+
+/**
+ * mpt3sas_base_stop_watchdog - stop the fault_reset_work_q
+ * @ioc: per adapter object
+ * Context: sleep.
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_base_stop_watchdog(struct MPT3SAS_ADAPTER *ioc)
+{
+ unsigned long flags;
+ struct workqueue_struct *wq;
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ wq = ioc->fault_reset_work_q;
+ ioc->fault_reset_work_q = NULL;
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+ if (wq) {
+ if (!cancel_delayed_work(&ioc->fault_reset_work))
+ flush_workqueue(wq);
+ destroy_workqueue(wq);
+ }
+}
+
+/**
+ * mpt3sas_base_fault_info - verbose translation of firmware FAULT code
+ * @ioc: per adapter object
+ * @fault_code: fault code
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_base_fault_info(struct MPT3SAS_ADAPTER *ioc , u16 fault_code)
+{
+ pr_err(MPT3SAS_FMT "fault_state(0x%04x)!\n",
+ ioc->name, fault_code);
+}
+
+/**
+ * mpt3sas_halt_firmware - halt's mpt controller firmware
+ * @ioc: per adapter object
+ *
+ * For debugging timeout related issues. Writing 0xCOFFEE00
+ * to the doorbell register will halt controller firmware. With
+ * the purpose to stop both driver and firmware, the enduser can
+ * obtain a ring buffer from controller UART.
+ */
+void
+mpt3sas_halt_firmware(struct MPT3SAS_ADAPTER *ioc)
+{
+ u32 doorbell;
+
+ if (!ioc->fwfault_debug)
+ return;
+
+ dump_stack();
+
+ doorbell = readl(&ioc->chip->Doorbell);
+ if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT)
+ mpt3sas_base_fault_info(ioc , doorbell);
+ else {
+ writel(0xC0FFEE00, &ioc->chip->Doorbell);
+ pr_err(MPT3SAS_FMT "Firmware is halted due to command timeout\n",
+ ioc->name);
+ }
+
+ if (ioc->fwfault_debug == 2)
+ for (;;)
+ ;
+ else
+ panic("panic in %s\n", __func__);
+}
+
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+/**
+ * _base_sas_ioc_info - verbose translation of the ioc status
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @request_hdr: request mf
+ *
+ * Return nothing.
+ */
+static void
+_base_sas_ioc_info(struct MPT3SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply,
+ MPI2RequestHeader_t *request_hdr)
+{
+ u16 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ char *desc = NULL;
+ u16 frame_sz;
+ char *func_str = NULL;
+
+ /* SCSI_IO, RAID_PASS are handled from _scsih_scsi_ioc_info */
+ if (request_hdr->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
+ request_hdr->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH ||
+ request_hdr->Function == MPI2_FUNCTION_EVENT_NOTIFICATION)
+ return;
+
+ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ return;
+
+ switch (ioc_status) {
+
+/****************************************************************************
+* Common IOCStatus values for all replies
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_INVALID_FUNCTION:
+ desc = "invalid function";
+ break;
+ case MPI2_IOCSTATUS_BUSY:
+ desc = "busy";
+ break;
+ case MPI2_IOCSTATUS_INVALID_SGL:
+ desc = "invalid sgl";
+ break;
+ case MPI2_IOCSTATUS_INTERNAL_ERROR:
+ desc = "internal error";
+ break;
+ case MPI2_IOCSTATUS_INVALID_VPID:
+ desc = "invalid vpid";
+ break;
+ case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES:
+ desc = "insufficient resources";
+ break;
+ case MPI2_IOCSTATUS_INVALID_FIELD:
+ desc = "invalid field";
+ break;
+ case MPI2_IOCSTATUS_INVALID_STATE:
+ desc = "invalid state";
+ break;
+ case MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED:
+ desc = "op state not supported";
+ break;
+
+/****************************************************************************
+* Config IOCStatus values
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_CONFIG_INVALID_ACTION:
+ desc = "config invalid action";
+ break;
+ case MPI2_IOCSTATUS_CONFIG_INVALID_TYPE:
+ desc = "config invalid type";
+ break;
+ case MPI2_IOCSTATUS_CONFIG_INVALID_PAGE:
+ desc = "config invalid page";
+ break;
+ case MPI2_IOCSTATUS_CONFIG_INVALID_DATA:
+ desc = "config invalid data";
+ break;
+ case MPI2_IOCSTATUS_CONFIG_NO_DEFAULTS:
+ desc = "config no defaults";
+ break;
+ case MPI2_IOCSTATUS_CONFIG_CANT_COMMIT:
+ desc = "config cant commit";
+ break;
+
+/****************************************************************************
+* SCSI IO Reply
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
+ case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE:
+ case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
+ case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
+ case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN:
+ case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR:
+ case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR:
+ case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
+ case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
+ case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
+ case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
+ case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
+ break;
+
+/****************************************************************************
+* For use by SCSI Initiator and SCSI Target end-to-end data protection
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
+ desc = "eedp guard error";
+ break;
+ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
+ desc = "eedp ref tag error";
+ break;
+ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
+ desc = "eedp app tag error";
+ break;
+
+/****************************************************************************
+* SCSI Target values
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_TARGET_INVALID_IO_INDEX:
+ desc = "target invalid io index";
+ break;
+ case MPI2_IOCSTATUS_TARGET_ABORTED:
+ desc = "target aborted";
+ break;
+ case MPI2_IOCSTATUS_TARGET_NO_CONN_RETRYABLE:
+ desc = "target no conn retryable";
+ break;
+ case MPI2_IOCSTATUS_TARGET_NO_CONNECTION:
+ desc = "target no connection";
+ break;
+ case MPI2_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH:
+ desc = "target xfer count mismatch";
+ break;
+ case MPI2_IOCSTATUS_TARGET_DATA_OFFSET_ERROR:
+ desc = "target data offset error";
+ break;
+ case MPI2_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA:
+ desc = "target too much write data";
+ break;
+ case MPI2_IOCSTATUS_TARGET_IU_TOO_SHORT:
+ desc = "target iu too short";
+ break;
+ case MPI2_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT:
+ desc = "target ack nak timeout";
+ break;
+ case MPI2_IOCSTATUS_TARGET_NAK_RECEIVED:
+ desc = "target nak received";
+ break;
+
+/****************************************************************************
+* Serial Attached SCSI values
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_SAS_SMP_REQUEST_FAILED:
+ desc = "smp request failed";
+ break;
+ case MPI2_IOCSTATUS_SAS_SMP_DATA_OVERRUN:
+ desc = "smp data overrun";
+ break;
+
+/****************************************************************************
+* Diagnostic Buffer Post / Diagnostic Release values
+****************************************************************************/
+
+ case MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED:
+ desc = "diagnostic released";
+ break;
+ default:
+ break;
+ }
+
+ if (!desc)
+ return;
+
+ switch (request_hdr->Function) {
+ case MPI2_FUNCTION_CONFIG:
+ frame_sz = sizeof(Mpi2ConfigRequest_t) + ioc->sge_size;
+ func_str = "config_page";
+ break;
+ case MPI2_FUNCTION_SCSI_TASK_MGMT:
+ frame_sz = sizeof(Mpi2SCSITaskManagementRequest_t);
+ func_str = "task_mgmt";
+ break;
+ case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL:
+ frame_sz = sizeof(Mpi2SasIoUnitControlRequest_t);
+ func_str = "sas_iounit_ctl";
+ break;
+ case MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR:
+ frame_sz = sizeof(Mpi2SepRequest_t);
+ func_str = "enclosure";
+ break;
+ case MPI2_FUNCTION_IOC_INIT:
+ frame_sz = sizeof(Mpi2IOCInitRequest_t);
+ func_str = "ioc_init";
+ break;
+ case MPI2_FUNCTION_PORT_ENABLE:
+ frame_sz = sizeof(Mpi2PortEnableRequest_t);
+ func_str = "port_enable";
+ break;
+ case MPI2_FUNCTION_SMP_PASSTHROUGH:
+ frame_sz = sizeof(Mpi2SmpPassthroughRequest_t) + ioc->sge_size;
+ func_str = "smp_passthru";
+ break;
+ default:
+ frame_sz = 32;
+ func_str = "unknown";
+ break;
+ }
+
+ pr_warn(MPT3SAS_FMT "ioc_status: %s(0x%04x), request(0x%p),(%s)\n",
+ ioc->name, desc, ioc_status, request_hdr, func_str);
+
+ _debug_dump_mf(request_hdr, frame_sz/4);
+}
+
+/**
+ * _base_display_event_data - verbose translation of firmware asyn events
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ *
+ * Return nothing.
+ */
+static void
+_base_display_event_data(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventNotificationReply_t *mpi_reply)
+{
+ char *desc = NULL;
+ u16 event;
+
+ if (!(ioc->logging_level & MPT_DEBUG_EVENTS))
+ return;
+
+ event = le16_to_cpu(mpi_reply->Event);
+
+ switch (event) {
+ case MPI2_EVENT_LOG_DATA:
+ desc = "Log Data";
+ break;
+ case MPI2_EVENT_STATE_CHANGE:
+ desc = "Status Change";
+ break;
+ case MPI2_EVENT_HARD_RESET_RECEIVED:
+ desc = "Hard Reset Received";
+ break;
+ case MPI2_EVENT_EVENT_CHANGE:
+ desc = "Event Change";
+ break;
+ case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE:
+ desc = "Device Status Change";
+ break;
+ case MPI2_EVENT_IR_OPERATION_STATUS:
+ desc = "IR Operation Status";
+ break;
+ case MPI2_EVENT_SAS_DISCOVERY:
+ {
+ Mpi2EventDataSasDiscovery_t *event_data =
+ (Mpi2EventDataSasDiscovery_t *)mpi_reply->EventData;
+ pr_info(MPT3SAS_FMT "Discovery: (%s)", ioc->name,
+ (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ?
+ "start" : "stop");
+ if (event_data->DiscoveryStatus)
+ pr_info("discovery_status(0x%08x)",
+ le32_to_cpu(event_data->DiscoveryStatus));
+ pr_info("\n");
+ return;
+ }
+ case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE:
+ desc = "SAS Broadcast Primitive";
+ break;
+ case MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE:
+ desc = "SAS Init Device Status Change";
+ break;
+ case MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW:
+ desc = "SAS Init Table Overflow";
+ break;
+ case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
+ desc = "SAS Topology Change List";
+ break;
+ case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
+ desc = "SAS Enclosure Device Status Change";
+ break;
+ case MPI2_EVENT_IR_VOLUME:
+ desc = "IR Volume";
+ break;
+ case MPI2_EVENT_IR_PHYSICAL_DISK:
+ desc = "IR Physical Disk";
+ break;
+ case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST:
+ desc = "IR Configuration Change List";
+ break;
+ case MPI2_EVENT_LOG_ENTRY_ADDED:
+ desc = "Log Entry Added";
+ break;
+ }
+
+ if (!desc)
+ return;
+
+ pr_info(MPT3SAS_FMT "%s\n", ioc->name, desc);
+}
+#endif
+
+/**
+ * _base_sas_log_info - verbose translation of firmware log info
+ * @ioc: per adapter object
+ * @log_info: log info
+ *
+ * Return nothing.
+ */
+static void
+_base_sas_log_info(struct MPT3SAS_ADAPTER *ioc , u32 log_info)
+{
+ union loginfo_type {
+ u32 loginfo;
+ struct {
+ u32 subcode:16;
+ u32 code:8;
+ u32 originator:4;
+ u32 bus_type:4;
+ } dw;
+ };
+ union loginfo_type sas_loginfo;
+ char *originator_str = NULL;
+
+ sas_loginfo.loginfo = log_info;
+ if (sas_loginfo.dw.bus_type != 3 /*SAS*/)
+ return;
+
+ /* each nexus loss loginfo */
+ if (log_info == 0x31170000)
+ return;
+
+ /* eat the loginfos associated with task aborts */
+ if (ioc->ignore_loginfos && (log_info == 0x30050000 || log_info ==
+ 0x31140000 || log_info == 0x31130000))
+ return;
+
+ switch (sas_loginfo.dw.originator) {
+ case 0:
+ originator_str = "IOP";
+ break;
+ case 1:
+ originator_str = "PL";
+ break;
+ case 2:
+ originator_str = "IR";
+ break;
+ }
+
+ pr_warn(MPT3SAS_FMT
+ "log_info(0x%08x): originator(%s), code(0x%02x), sub_code(0x%04x)\n",
+ ioc->name, log_info,
+ originator_str, sas_loginfo.dw.code,
+ sas_loginfo.dw.subcode);
+}
+
+/**
+ * _base_display_reply_info -
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Return nothing.
+ */
+static void
+_base_display_reply_info(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+ u16 ioc_status;
+ u32 loginfo = 0;
+
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ if (unlikely(!mpi_reply)) {
+ pr_err(MPT3SAS_FMT "mpi_reply not valid at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus);
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+ if ((ioc_status & MPI2_IOCSTATUS_MASK) &&
+ (ioc->logging_level & MPT_DEBUG_REPLY)) {
+ _base_sas_ioc_info(ioc , mpi_reply,
+ mpt3sas_base_get_msg_frame(ioc, smid));
+ }
+#endif
+ if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) {
+ loginfo = le32_to_cpu(mpi_reply->IOCLogInfo);
+ _base_sas_log_info(ioc, loginfo);
+ }
+
+ if (ioc_status || loginfo) {
+ ioc_status &= MPI2_IOCSTATUS_MASK;
+ mpt3sas_trigger_mpi(ioc, ioc_status, loginfo);
+ }
+}
+
+/**
+ * mpt3sas_base_done - base internal command completion routine
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+u8
+mpt3sas_base_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ if (mpi_reply && mpi_reply->Function == MPI2_FUNCTION_EVENT_ACK)
+ return 1;
+
+ if (ioc->base_cmds.status == MPT3_CMD_NOT_USED)
+ return 1;
+
+ ioc->base_cmds.status |= MPT3_CMD_COMPLETE;
+ if (mpi_reply) {
+ ioc->base_cmds.status |= MPT3_CMD_REPLY_VALID;
+ memcpy(ioc->base_cmds.reply, mpi_reply, mpi_reply->MsgLength*4);
+ }
+ ioc->base_cmds.status &= ~MPT3_CMD_PENDING;
+
+ complete(&ioc->base_cmds.done);
+ return 1;
+}
+
+/**
+ * _base_async_event - main callback handler for firmware asyn events
+ * @ioc: per adapter object
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+static u8
+_base_async_event(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, u32 reply)
+{
+ Mpi2EventNotificationReply_t *mpi_reply;
+ Mpi2EventAckRequest_t *ack_request;
+ u16 smid;
+
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ if (!mpi_reply)
+ return 1;
+ if (mpi_reply->Function != MPI2_FUNCTION_EVENT_NOTIFICATION)
+ return 1;
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+ _base_display_event_data(ioc, mpi_reply);
+#endif
+ if (!(mpi_reply->AckRequired & MPI2_EVENT_NOTIFICATION_ACK_REQUIRED))
+ goto out;
+ smid = mpt3sas_base_get_smid(ioc, ioc->base_cb_idx);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ goto out;
+ }
+
+ ack_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ memset(ack_request, 0, sizeof(Mpi2EventAckRequest_t));
+ ack_request->Function = MPI2_FUNCTION_EVENT_ACK;
+ ack_request->Event = mpi_reply->Event;
+ ack_request->EventContext = mpi_reply->EventContext;
+ ack_request->VF_ID = 0; /* TODO */
+ ack_request->VP_ID = 0;
+ mpt3sas_base_put_smid_default(ioc, smid);
+
+ out:
+
+ /* scsih callback handler */
+ mpt3sas_scsih_event_callback(ioc, msix_index, reply);
+
+ /* ctl callback handler */
+ mpt3sas_ctl_event_callback(ioc, msix_index, reply);
+
+ return 1;
+}
+
+/**
+ * _base_get_cb_idx - obtain the callback index
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Return callback index.
+ */
+static u8
+_base_get_cb_idx(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ int i;
+ u8 cb_idx;
+
+ if (smid < ioc->hi_priority_smid) {
+ i = smid - 1;
+ cb_idx = ioc->scsi_lookup[i].cb_idx;
+ } else if (smid < ioc->internal_smid) {
+ i = smid - ioc->hi_priority_smid;
+ cb_idx = ioc->hpr_lookup[i].cb_idx;
+ } else if (smid <= ioc->hba_queue_depth) {
+ i = smid - ioc->internal_smid;
+ cb_idx = ioc->internal_lookup[i].cb_idx;
+ } else
+ cb_idx = 0xFF;
+ return cb_idx;
+}
+
+/**
+ * _base_mask_interrupts - disable interrupts
+ * @ioc: per adapter object
+ *
+ * Disabling ResetIRQ, Reply and Doorbell Interrupts
+ *
+ * Return nothing.
+ */
+static void
+_base_mask_interrupts(struct MPT3SAS_ADAPTER *ioc)
+{
+ u32 him_register;
+
+ ioc->mask_interrupts = 1;
+ him_register = readl(&ioc->chip->HostInterruptMask);
+ him_register |= MPI2_HIM_DIM + MPI2_HIM_RIM + MPI2_HIM_RESET_IRQ_MASK;
+ writel(him_register, &ioc->chip->HostInterruptMask);
+ readl(&ioc->chip->HostInterruptMask);
+}
+
+/**
+ * _base_unmask_interrupts - enable interrupts
+ * @ioc: per adapter object
+ *
+ * Enabling only Reply Interrupts
+ *
+ * Return nothing.
+ */
+static void
+_base_unmask_interrupts(struct MPT3SAS_ADAPTER *ioc)
+{
+ u32 him_register;
+
+ him_register = readl(&ioc->chip->HostInterruptMask);
+ him_register &= ~MPI2_HIM_RIM;
+ writel(him_register, &ioc->chip->HostInterruptMask);
+ ioc->mask_interrupts = 0;
+}
+
+union reply_descriptor {
+ u64 word;
+ struct {
+ u32 low;
+ u32 high;
+ } u;
+};
+
+/**
+ * _base_interrupt - MPT adapter (IOC) specific interrupt handler.
+ * @irq: irq number (not used)
+ * @bus_id: bus identifier cookie == pointer to MPT_ADAPTER structure
+ * @r: pt_regs pointer (not used)
+ *
+ * Return IRQ_HANDLE if processed, else IRQ_NONE.
+ */
+static irqreturn_t
+_base_interrupt(int irq, void *bus_id)
+{
+ struct adapter_reply_queue *reply_q = bus_id;
+ union reply_descriptor rd;
+ u32 completed_cmds;
+ u8 request_desript_type;
+ u16 smid;
+ u8 cb_idx;
+ u32 reply;
+ u8 msix_index = reply_q->msix_index;
+ struct MPT3SAS_ADAPTER *ioc = reply_q->ioc;
+ Mpi2ReplyDescriptorsUnion_t *rpf;
+ u8 rc;
+
+ if (ioc->mask_interrupts)
+ return IRQ_NONE;
+
+ if (!atomic_add_unless(&reply_q->busy, 1, 1))
+ return IRQ_NONE;
+
+ rpf = &reply_q->reply_post_free[reply_q->reply_post_host_index];
+ request_desript_type = rpf->Default.ReplyFlags
+ & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
+ if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) {
+ atomic_dec(&reply_q->busy);
+ return IRQ_NONE;
+ }
+
+ completed_cmds = 0;
+ cb_idx = 0xFF;
+ do {
+ rd.word = le64_to_cpu(rpf->Words);
+ if (rd.u.low == UINT_MAX || rd.u.high == UINT_MAX)
+ goto out;
+ reply = 0;
+ smid = le16_to_cpu(rpf->Default.DescriptorTypeDependent1);
+ if (request_desript_type ==
+ MPI25_RPY_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO_SUCCESS ||
+ request_desript_type ==
+ MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS) {
+ cb_idx = _base_get_cb_idx(ioc, smid);
+ if ((likely(cb_idx < MPT_MAX_CALLBACKS)) &&
+ (likely(mpt_callbacks[cb_idx] != NULL))) {
+ rc = mpt_callbacks[cb_idx](ioc, smid,
+ msix_index, 0);
+ if (rc)
+ mpt3sas_base_free_smid(ioc, smid);
+ }
+ } else if (request_desript_type ==
+ MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY) {
+ reply = le32_to_cpu(
+ rpf->AddressReply.ReplyFrameAddress);
+ if (reply > ioc->reply_dma_max_address ||
+ reply < ioc->reply_dma_min_address)
+ reply = 0;
+ if (smid) {
+ cb_idx = _base_get_cb_idx(ioc, smid);
+ if ((likely(cb_idx < MPT_MAX_CALLBACKS)) &&
+ (likely(mpt_callbacks[cb_idx] != NULL))) {
+ rc = mpt_callbacks[cb_idx](ioc, smid,
+ msix_index, reply);
+ if (reply)
+ _base_display_reply_info(ioc,
+ smid, msix_index, reply);
+ if (rc)
+ mpt3sas_base_free_smid(ioc,
+ smid);
+ }
+ } else {
+ _base_async_event(ioc, msix_index, reply);
+ }
+
+ /* reply free queue handling */
+ if (reply) {
+ ioc->reply_free_host_index =
+ (ioc->reply_free_host_index ==
+ (ioc->reply_free_queue_depth - 1)) ?
+ 0 : ioc->reply_free_host_index + 1;
+ ioc->reply_free[ioc->reply_free_host_index] =
+ cpu_to_le32(reply);
+ wmb();
+ writel(ioc->reply_free_host_index,
+ &ioc->chip->ReplyFreeHostIndex);
+ }
+ }
+
+ rpf->Words = cpu_to_le64(ULLONG_MAX);
+ reply_q->reply_post_host_index =
+ (reply_q->reply_post_host_index ==
+ (ioc->reply_post_queue_depth - 1)) ? 0 :
+ reply_q->reply_post_host_index + 1;
+ request_desript_type =
+ reply_q->reply_post_free[reply_q->reply_post_host_index].
+ Default.ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
+ completed_cmds++;
+ if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
+ goto out;
+ if (!reply_q->reply_post_host_index)
+ rpf = reply_q->reply_post_free;
+ else
+ rpf++;
+ } while (1);
+
+ out:
+
+ if (!completed_cmds) {
+ atomic_dec(&reply_q->busy);
+ return IRQ_NONE;
+ }
+
+ wmb();
+ writel(reply_q->reply_post_host_index | (msix_index <<
+ MPI2_RPHI_MSIX_INDEX_SHIFT), &ioc->chip->ReplyPostHostIndex);
+ atomic_dec(&reply_q->busy);
+ return IRQ_HANDLED;
+}
+
+/**
+ * _base_is_controller_msix_enabled - is controller support muli-reply queues
+ * @ioc: per adapter object
+ *
+ */
+static inline int
+_base_is_controller_msix_enabled(struct MPT3SAS_ADAPTER *ioc)
+{
+ return (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX) && ioc->msix_enable;
+}
+
+/**
+ * mpt3sas_base_flush_reply_queues - flushing the MSIX reply queues
+ * @ioc: per adapter object
+ * Context: ISR conext
+ *
+ * Called when a Task Management request has completed. We want
+ * to flush the other reply queues so all the outstanding IO has been
+ * completed back to OS before we process the TM completetion.
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_base_flush_reply_queues(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct adapter_reply_queue *reply_q;
+
+ /* If MSIX capability is turned off
+ * then multi-queues are not enabled
+ */
+ if (!_base_is_controller_msix_enabled(ioc))
+ return;
+
+ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) {
+ if (ioc->shost_recovery)
+ return;
+ /* TMs are on msix_index == 0 */
+ if (reply_q->msix_index == 0)
+ continue;
+ _base_interrupt(reply_q->vector, (void *)reply_q);
+ }
+}
+
+/**
+ * mpt3sas_base_release_callback_handler - clear interrupt callback handler
+ * @cb_idx: callback index
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_base_release_callback_handler(u8 cb_idx)
+{
+ mpt_callbacks[cb_idx] = NULL;
+}
+
+/**
+ * mpt3sas_base_register_callback_handler - obtain index for the interrupt callback handler
+ * @cb_func: callback function
+ *
+ * Returns cb_func.
+ */
+u8
+mpt3sas_base_register_callback_handler(MPT_CALLBACK cb_func)
+{
+ u8 cb_idx;
+
+ for (cb_idx = MPT_MAX_CALLBACKS-1; cb_idx; cb_idx--)
+ if (mpt_callbacks[cb_idx] == NULL)
+ break;
+
+ mpt_callbacks[cb_idx] = cb_func;
+ return cb_idx;
+}
+
+/**
+ * mpt3sas_base_initialize_callback_handler - initialize the interrupt callback handler
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_base_initialize_callback_handler(void)
+{
+ u8 cb_idx;
+
+ for (cb_idx = 0; cb_idx < MPT_MAX_CALLBACKS; cb_idx++)
+ mpt3sas_base_release_callback_handler(cb_idx);
+}
+
+
+/**
+ * _base_build_zero_len_sge - build zero length sg entry
+ * @ioc: per adapter object
+ * @paddr: virtual address for SGE
+ *
+ * Create a zero length scatter gather entry to insure the IOCs hardware has
+ * something to use if the target device goes brain dead and tries
+ * to send data even when none is asked for.
+ *
+ * Return nothing.
+ */
+static void
+_base_build_zero_len_sge(struct MPT3SAS_ADAPTER *ioc, void *paddr)
+{
+ u32 flags_length = (u32)((MPI2_SGE_FLAGS_LAST_ELEMENT |
+ MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST |
+ MPI2_SGE_FLAGS_SIMPLE_ELEMENT) <<
+ MPI2_SGE_FLAGS_SHIFT);
+ ioc->base_add_sg_single(paddr, flags_length, -1);
+}
+
+/**
+ * _base_add_sg_single_32 - Place a simple 32 bit SGE at address pAddr.
+ * @paddr: virtual address for SGE
+ * @flags_length: SGE flags and data transfer length
+ * @dma_addr: Physical address
+ *
+ * Return nothing.
+ */
+static void
+_base_add_sg_single_32(void *paddr, u32 flags_length, dma_addr_t dma_addr)
+{
+ Mpi2SGESimple32_t *sgel = paddr;
+
+ flags_length |= (MPI2_SGE_FLAGS_32_BIT_ADDRESSING |
+ MPI2_SGE_FLAGS_SYSTEM_ADDRESS) << MPI2_SGE_FLAGS_SHIFT;
+ sgel->FlagsLength = cpu_to_le32(flags_length);
+ sgel->Address = cpu_to_le32(dma_addr);
+}
+
+
+/**
+ * _base_add_sg_single_64 - Place a simple 64 bit SGE at address pAddr.
+ * @paddr: virtual address for SGE
+ * @flags_length: SGE flags and data transfer length
+ * @dma_addr: Physical address
+ *
+ * Return nothing.
+ */
+static void
+_base_add_sg_single_64(void *paddr, u32 flags_length, dma_addr_t dma_addr)
+{
+ Mpi2SGESimple64_t *sgel = paddr;
+
+ flags_length |= (MPI2_SGE_FLAGS_64_BIT_ADDRESSING |
+ MPI2_SGE_FLAGS_SYSTEM_ADDRESS) << MPI2_SGE_FLAGS_SHIFT;
+ sgel->FlagsLength = cpu_to_le32(flags_length);
+ sgel->Address = cpu_to_le64(dma_addr);
+}
+
+/**
+ * _base_get_chain_buffer_tracker - obtain chain tracker
+ * @ioc: per adapter object
+ * @smid: smid associated to an IO request
+ *
+ * Returns chain tracker(from ioc->free_chain_list)
+ */
+static struct chain_tracker *
+_base_get_chain_buffer_tracker(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ struct chain_tracker *chain_req;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ if (list_empty(&ioc->free_chain_list)) {
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ dfailprintk(ioc, pr_warn(MPT3SAS_FMT
+ "chain buffers not available\n", ioc->name));
+ return NULL;
+ }
+ chain_req = list_entry(ioc->free_chain_list.next,
+ struct chain_tracker, tracker_list);
+ list_del_init(&chain_req->tracker_list);
+ list_add_tail(&chain_req->tracker_list,
+ &ioc->scsi_lookup[smid - 1].chain_list);
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ return chain_req;
+}
+
+
+/**
+ * _base_build_sg - build generic sg
+ * @ioc: per adapter object
+ * @psge: virtual address for SGE
+ * @data_out_dma: physical address for WRITES
+ * @data_out_sz: data xfer size for WRITES
+ * @data_in_dma: physical address for READS
+ * @data_in_sz: data xfer size for READS
+ *
+ * Return nothing.
+ */
+static void
+_base_build_sg(struct MPT3SAS_ADAPTER *ioc, void *psge,
+ dma_addr_t data_out_dma, size_t data_out_sz, dma_addr_t data_in_dma,
+ size_t data_in_sz)
+{
+ u32 sgl_flags;
+
+ if (!data_out_sz && !data_in_sz) {
+ _base_build_zero_len_sge(ioc, psge);
+ return;
+ }
+
+ if (data_out_sz && data_in_sz) {
+ /* WRITE sgel first */
+ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC);
+ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
+ ioc->base_add_sg_single(psge, sgl_flags |
+ data_out_sz, data_out_dma);
+
+ /* incr sgel */
+ psge += ioc->sge_size;
+
+ /* READ sgel last */
+ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
+ MPI2_SGE_FLAGS_END_OF_LIST);
+ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
+ ioc->base_add_sg_single(psge, sgl_flags |
+ data_in_sz, data_in_dma);
+ } else if (data_out_sz) /* WRITE */ {
+ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
+ MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_HOST_TO_IOC);
+ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
+ ioc->base_add_sg_single(psge, sgl_flags |
+ data_out_sz, data_out_dma);
+ } else if (data_in_sz) /* READ */ {
+ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
+ MPI2_SGE_FLAGS_END_OF_LIST);
+ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
+ ioc->base_add_sg_single(psge, sgl_flags |
+ data_in_sz, data_in_dma);
+ }
+}
+
+/* IEEE format sgls */
+
+/**
+ * _base_add_sg_single_ieee - add sg element for IEEE format
+ * @paddr: virtual address for SGE
+ * @flags: SGE flags
+ * @chain_offset: number of 128 byte elements from start of segment
+ * @length: data transfer length
+ * @dma_addr: Physical address
+ *
+ * Return nothing.
+ */
+static void
+_base_add_sg_single_ieee(void *paddr, u8 flags, u8 chain_offset, u32 length,
+ dma_addr_t dma_addr)
+{
+ Mpi25IeeeSgeChain64_t *sgel = paddr;
+
+ sgel->Flags = flags;
+ sgel->NextChainOffset = chain_offset;
+ sgel->Length = cpu_to_le32(length);
+ sgel->Address = cpu_to_le64(dma_addr);
+}
+
+/**
+ * _base_build_zero_len_sge_ieee - build zero length sg entry for IEEE format
+ * @ioc: per adapter object
+ * @paddr: virtual address for SGE
+ *
+ * Create a zero length scatter gather entry to insure the IOCs hardware has
+ * something to use if the target device goes brain dead and tries
+ * to send data even when none is asked for.
+ *
+ * Return nothing.
+ */
+static void
+_base_build_zero_len_sge_ieee(struct MPT3SAS_ADAPTER *ioc, void *paddr)
+{
+ u8 sgl_flags = (MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR |
+ MPI25_IEEE_SGE_FLAGS_END_OF_LIST);
+ _base_add_sg_single_ieee(paddr, sgl_flags, 0, 0, -1);
+}
+
+/**
+ * _base_build_sg_scmd_ieee - main sg creation routine for IEEE format
+ * @ioc: per adapter object
+ * @scmd: scsi command
+ * @smid: system request message index
+ * Context: none.
+ *
+ * The main routine that builds scatter gather table from a given
+ * scsi request sent via the .queuecommand main handler.
+ *
+ * Returns 0 success, anything else error
+ */
+static int
+_base_build_sg_scmd_ieee(struct MPT3SAS_ADAPTER *ioc,
+ struct scsi_cmnd *scmd, u16 smid)
+{
+ Mpi2SCSIIORequest_t *mpi_request;
+ dma_addr_t chain_dma;
+ struct scatterlist *sg_scmd;
+ void *sg_local, *chain;
+ u32 chain_offset;
+ u32 chain_length;
+ u32 chain_flags;
+ int sges_left;
+ u32 sges_in_segment;
+ u8 simple_sgl_flags;
+ u8 simple_sgl_flags_last;
+ u8 chain_sgl_flags;
+ struct chain_tracker *chain_req;
+
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+
+ /* init scatter gather flags */
+ simple_sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR;
+ simple_sgl_flags_last = simple_sgl_flags |
+ MPI25_IEEE_SGE_FLAGS_END_OF_LIST;
+ chain_sgl_flags = MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT |
+ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR;
+
+ sg_scmd = scsi_sglist(scmd);
+ sges_left = scsi_dma_map(scmd);
+ if (!sges_left) {
+ sdev_printk(KERN_ERR, scmd->device,
+ "pci_map_sg failed: request for %d bytes!\n",
+ scsi_bufflen(scmd));
+ return -ENOMEM;
+ }
+
+ sg_local = &mpi_request->SGL;
+ sges_in_segment = (ioc->request_sz -
+ offsetof(Mpi2SCSIIORequest_t, SGL))/ioc->sge_size_ieee;
+ if (sges_left <= sges_in_segment)
+ goto fill_in_last_segment;
+
+ mpi_request->ChainOffset = (sges_in_segment - 1 /* chain element */) +
+ (offsetof(Mpi2SCSIIORequest_t, SGL)/ioc->sge_size_ieee);
+
+ /* fill in main message segment when there is a chain following */
+ while (sges_in_segment > 1) {
+ _base_add_sg_single_ieee(sg_local, simple_sgl_flags, 0,
+ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
+ sg_scmd = sg_next(sg_scmd);
+ sg_local += ioc->sge_size_ieee;
+ sges_left--;
+ sges_in_segment--;
+ }
+
+ /* initializing the chain flags and pointers */
+ chain_flags = MPI2_SGE_FLAGS_CHAIN_ELEMENT << MPI2_SGE_FLAGS_SHIFT;
+ chain_req = _base_get_chain_buffer_tracker(ioc, smid);
+ if (!chain_req)
+ return -1;
+ chain = chain_req->chain_buffer;
+ chain_dma = chain_req->chain_buffer_dma;
+ do {
+ sges_in_segment = (sges_left <=
+ ioc->max_sges_in_chain_message) ? sges_left :
+ ioc->max_sges_in_chain_message;
+ chain_offset = (sges_left == sges_in_segment) ?
+ 0 : sges_in_segment;
+ chain_length = sges_in_segment * ioc->sge_size_ieee;
+ if (chain_offset)
+ chain_length += ioc->sge_size_ieee;
+ _base_add_sg_single_ieee(sg_local, chain_sgl_flags,
+ chain_offset, chain_length, chain_dma);
+
+ sg_local = chain;
+ if (!chain_offset)
+ goto fill_in_last_segment;
+
+ /* fill in chain segments */
+ while (sges_in_segment) {
+ _base_add_sg_single_ieee(sg_local, simple_sgl_flags, 0,
+ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
+ sg_scmd = sg_next(sg_scmd);
+ sg_local += ioc->sge_size_ieee;
+ sges_left--;
+ sges_in_segment--;
+ }
+
+ chain_req = _base_get_chain_buffer_tracker(ioc, smid);
+ if (!chain_req)
+ return -1;
+ chain = chain_req->chain_buffer;
+ chain_dma = chain_req->chain_buffer_dma;
+ } while (1);
+
+
+ fill_in_last_segment:
+
+ /* fill the last segment */
+ while (sges_left) {
+ if (sges_left == 1)
+ _base_add_sg_single_ieee(sg_local,
+ simple_sgl_flags_last, 0, sg_dma_len(sg_scmd),
+ sg_dma_address(sg_scmd));
+ else
+ _base_add_sg_single_ieee(sg_local, simple_sgl_flags, 0,
+ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
+ sg_scmd = sg_next(sg_scmd);
+ sg_local += ioc->sge_size_ieee;
+ sges_left--;
+ }
+
+ return 0;
+}
+
+/**
+ * _base_build_sg_ieee - build generic sg for IEEE format
+ * @ioc: per adapter object
+ * @psge: virtual address for SGE
+ * @data_out_dma: physical address for WRITES
+ * @data_out_sz: data xfer size for WRITES
+ * @data_in_dma: physical address for READS
+ * @data_in_sz: data xfer size for READS
+ *
+ * Return nothing.
+ */
+static void
+_base_build_sg_ieee(struct MPT3SAS_ADAPTER *ioc, void *psge,
+ dma_addr_t data_out_dma, size_t data_out_sz, dma_addr_t data_in_dma,
+ size_t data_in_sz)
+{
+ u8 sgl_flags;
+
+ if (!data_out_sz && !data_in_sz) {
+ _base_build_zero_len_sge_ieee(ioc, psge);
+ return;
+ }
+
+ if (data_out_sz && data_in_sz) {
+ /* WRITE sgel first */
+ sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR;
+ _base_add_sg_single_ieee(psge, sgl_flags, 0, data_out_sz,
+ data_out_dma);
+
+ /* incr sgel */
+ psge += ioc->sge_size_ieee;
+
+ /* READ sgel last */
+ sgl_flags |= MPI25_IEEE_SGE_FLAGS_END_OF_LIST;
+ _base_add_sg_single_ieee(psge, sgl_flags, 0, data_in_sz,
+ data_in_dma);
+ } else if (data_out_sz) /* WRITE */ {
+ sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI25_IEEE_SGE_FLAGS_END_OF_LIST |
+ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR;
+ _base_add_sg_single_ieee(psge, sgl_flags, 0, data_out_sz,
+ data_out_dma);
+ } else if (data_in_sz) /* READ */ {
+ sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI25_IEEE_SGE_FLAGS_END_OF_LIST |
+ MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR;
+ _base_add_sg_single_ieee(psge, sgl_flags, 0, data_in_sz,
+ data_in_dma);
+ }
+}
+
+#define convert_to_kb(x) ((x) << (PAGE_SHIFT - 10))
+
+/**
+ * _base_config_dma_addressing - set dma addressing
+ * @ioc: per adapter object
+ * @pdev: PCI device struct
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_config_dma_addressing(struct MPT3SAS_ADAPTER *ioc, struct pci_dev *pdev)
+{
+ struct sysinfo s;
+ char *desc = NULL;
+
+ if (sizeof(dma_addr_t) > 4) {
+ const uint64_t required_mask =
+ dma_get_required_mask(&pdev->dev);
+ if ((required_mask > DMA_BIT_MASK(32)) &&
+ !pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) &&
+ !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) {
+ ioc->base_add_sg_single = &_base_add_sg_single_64;
+ ioc->sge_size = sizeof(Mpi2SGESimple64_t);
+ desc = "64";
+ goto out;
+ }
+ }
+
+ if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))
+ && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ ioc->base_add_sg_single = &_base_add_sg_single_32;
+ ioc->sge_size = sizeof(Mpi2SGESimple32_t);
+ desc = "32";
+ } else
+ return -ENODEV;
+
+ out:
+ si_meminfo(&s);
+ pr_info(MPT3SAS_FMT
+ "%s BIT PCI BUS DMA ADDRESSING SUPPORTED, total mem (%ld kB)\n",
+ ioc->name, desc, convert_to_kb(s.totalram));
+
+ return 0;
+}
+
+/**
+ * _base_check_enable_msix - checks MSIX capabable.
+ * @ioc: per adapter object
+ *
+ * Check to see if card is capable of MSIX, and set number
+ * of available msix vectors
+ */
+static int
+_base_check_enable_msix(struct MPT3SAS_ADAPTER *ioc)
+{
+ int base;
+ u16 message_control;
+
+ base = pci_find_capability(ioc->pdev, PCI_CAP_ID_MSIX);
+ if (!base) {
+ dfailprintk(ioc, pr_info(MPT3SAS_FMT "msix not supported\n",
+ ioc->name));
+ return -EINVAL;
+ }
+
+ /* get msix vector count */
+
+ pci_read_config_word(ioc->pdev, base + 2, &message_control);
+ ioc->msix_vector_count = (message_control & 0x3FF) + 1;
+ if (ioc->msix_vector_count > 8)
+ ioc->msix_vector_count = 8;
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT
+ "msix is supported, vector_count(%d)\n",
+ ioc->name, ioc->msix_vector_count));
+ return 0;
+}
+
+/**
+ * _base_free_irq - free irq
+ * @ioc: per adapter object
+ *
+ * Freeing respective reply_queue from the list.
+ */
+static void
+_base_free_irq(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct adapter_reply_queue *reply_q, *next;
+
+ if (list_empty(&ioc->reply_queue_list))
+ return;
+
+ list_for_each_entry_safe(reply_q, next, &ioc->reply_queue_list, list) {
+ list_del(&reply_q->list);
+ synchronize_irq(reply_q->vector);
+ free_irq(reply_q->vector, reply_q);
+ kfree(reply_q);
+ }
+}
+
+/**
+ * _base_request_irq - request irq
+ * @ioc: per adapter object
+ * @index: msix index into vector table
+ * @vector: irq vector
+ *
+ * Inserting respective reply_queue into the list.
+ */
+static int
+_base_request_irq(struct MPT3SAS_ADAPTER *ioc, u8 index, u32 vector)
+{
+ struct adapter_reply_queue *reply_q;
+ int r;
+
+ reply_q = kzalloc(sizeof(struct adapter_reply_queue), GFP_KERNEL);
+ if (!reply_q) {
+ pr_err(MPT3SAS_FMT "unable to allocate memory %d!\n",
+ ioc->name, (int)sizeof(struct adapter_reply_queue));
+ return -ENOMEM;
+ }
+ reply_q->ioc = ioc;
+ reply_q->msix_index = index;
+ reply_q->vector = vector;
+ atomic_set(&reply_q->busy, 0);
+ if (ioc->msix_enable)
+ snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d-msix%d",
+ MPT3SAS_DRIVER_NAME, ioc->id, index);
+ else
+ snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d",
+ MPT3SAS_DRIVER_NAME, ioc->id);
+ r = request_irq(vector, _base_interrupt, IRQF_SHARED, reply_q->name,
+ reply_q);
+ if (r) {
+ pr_err(MPT3SAS_FMT "unable to allocate interrupt %d!\n",
+ reply_q->name, vector);
+ kfree(reply_q);
+ return -EBUSY;
+ }
+
+ INIT_LIST_HEAD(&reply_q->list);
+ list_add_tail(&reply_q->list, &ioc->reply_queue_list);
+ return 0;
+}
+
+/**
+ * _base_assign_reply_queues - assigning msix index for each cpu
+ * @ioc: per adapter object
+ *
+ * The enduser would need to set the affinity via /proc/irq/#/smp_affinity
+ *
+ * It would nice if we could call irq_set_affinity, however it is not
+ * an exported symbol
+ */
+static void
+_base_assign_reply_queues(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct adapter_reply_queue *reply_q;
+ int cpu_id;
+ int cpu_grouping, loop, grouping, grouping_mod;
+ int reply_queue;
+
+ if (!_base_is_controller_msix_enabled(ioc))
+ return;
+
+ memset(ioc->cpu_msix_table, 0, ioc->cpu_msix_table_sz);
+
+ /* NUMA Hardware bug workaround - drop to less reply queues */
+ if (ioc->reply_queue_count > ioc->facts.MaxMSIxVectors) {
+ ioc->reply_queue_count = ioc->facts.MaxMSIxVectors;
+ reply_queue = 0;
+ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) {
+ reply_q->msix_index = reply_queue;
+ if (++reply_queue == ioc->reply_queue_count)
+ reply_queue = 0;
+ }
+ }
+
+ /* when there are more cpus than available msix vectors,
+ * then group cpus togeather on same irq
+ */
+ if (ioc->cpu_count > ioc->msix_vector_count) {
+ grouping = ioc->cpu_count / ioc->msix_vector_count;
+ grouping_mod = ioc->cpu_count % ioc->msix_vector_count;
+ if (grouping < 2 || (grouping == 2 && !grouping_mod))
+ cpu_grouping = 2;
+ else if (grouping < 4 || (grouping == 4 && !grouping_mod))
+ cpu_grouping = 4;
+ else if (grouping < 8 || (grouping == 8 && !grouping_mod))
+ cpu_grouping = 8;
+ else
+ cpu_grouping = 16;
+ } else
+ cpu_grouping = 0;
+
+ loop = 0;
+ reply_q = list_entry(ioc->reply_queue_list.next,
+ struct adapter_reply_queue, list);
+ for_each_online_cpu(cpu_id) {
+ if (!cpu_grouping) {
+ ioc->cpu_msix_table[cpu_id] = reply_q->msix_index;
+ reply_q = list_entry(reply_q->list.next,
+ struct adapter_reply_queue, list);
+ } else {
+ if (loop < cpu_grouping) {
+ ioc->cpu_msix_table[cpu_id] =
+ reply_q->msix_index;
+ loop++;
+ } else {
+ reply_q = list_entry(reply_q->list.next,
+ struct adapter_reply_queue, list);
+ ioc->cpu_msix_table[cpu_id] =
+ reply_q->msix_index;
+ loop = 1;
+ }
+ }
+ }
+}
+
+/**
+ * _base_disable_msix - disables msix
+ * @ioc: per adapter object
+ *
+ */
+static void
+_base_disable_msix(struct MPT3SAS_ADAPTER *ioc)
+{
+ if (!ioc->msix_enable)
+ return;
+ pci_disable_msix(ioc->pdev);
+ ioc->msix_enable = 0;
+}
+
+/**
+ * _base_enable_msix - enables msix, failback to io_apic
+ * @ioc: per adapter object
+ *
+ */
+static int
+_base_enable_msix(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct msix_entry *entries, *a;
+ int r;
+ int i;
+ u8 try_msix = 0;
+
+ INIT_LIST_HEAD(&ioc->reply_queue_list);
+
+ if (msix_disable == -1 || msix_disable == 0)
+ try_msix = 1;
+
+ if (!try_msix)
+ goto try_ioapic;
+
+ if (_base_check_enable_msix(ioc) != 0)
+ goto try_ioapic;
+
+ ioc->reply_queue_count = min_t(int, ioc->cpu_count,
+ ioc->msix_vector_count);
+
+ entries = kcalloc(ioc->reply_queue_count, sizeof(struct msix_entry),
+ GFP_KERNEL);
+ if (!entries) {
+ dfailprintk(ioc, pr_info(MPT3SAS_FMT
+ "kcalloc failed @ at %s:%d/%s() !!!\n",
+ ioc->name, __FILE__, __LINE__, __func__));
+ goto try_ioapic;
+ }
+
+ for (i = 0, a = entries; i < ioc->reply_queue_count; i++, a++)
+ a->entry = i;
+
+ r = pci_enable_msix(ioc->pdev, entries, ioc->reply_queue_count);
+ if (r) {
+ dfailprintk(ioc, pr_info(MPT3SAS_FMT
+ "pci_enable_msix failed (r=%d) !!!\n",
+ ioc->name, r));
+ kfree(entries);
+ goto try_ioapic;
+ }
+
+ ioc->msix_enable = 1;
+ for (i = 0, a = entries; i < ioc->reply_queue_count; i++, a++) {
+ r = _base_request_irq(ioc, i, a->vector);
+ if (r) {
+ _base_free_irq(ioc);
+ _base_disable_msix(ioc);
+ kfree(entries);
+ goto try_ioapic;
+ }
+ }
+
+ kfree(entries);
+ return 0;
+
+/* failback to io_apic interrupt routing */
+ try_ioapic:
+
+ r = _base_request_irq(ioc, 0, ioc->pdev->irq);
+
+ return r;
+}
+
+/**
+ * mpt3sas_base_map_resources - map in controller resources (io/irq/memap)
+ * @ioc: per adapter object
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct pci_dev *pdev = ioc->pdev;
+ u32 memap_sz;
+ u32 pio_sz;
+ int i, r = 0;
+ u64 pio_chip = 0;
+ u64 chip_phys = 0;
+ struct adapter_reply_queue *reply_q;
+
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n",
+ ioc->name, __func__));
+
+ ioc->bars = pci_select_bars(pdev, IORESOURCE_MEM);
+ if (pci_enable_device_mem(pdev)) {
+ pr_warn(MPT3SAS_FMT "pci_enable_device_mem: failed\n",
+ ioc->name);
+ return -ENODEV;
+ }
+
+
+ if (pci_request_selected_regions(pdev, ioc->bars,
+ MPT3SAS_DRIVER_NAME)) {
+ pr_warn(MPT3SAS_FMT "pci_request_selected_regions: failed\n",
+ ioc->name);
+ r = -ENODEV;
+ goto out_fail;
+ }
+
+/* AER (Advanced Error Reporting) hooks */
+ pci_enable_pcie_error_reporting(pdev);
+
+ pci_set_master(pdev);
+
+
+ if (_base_config_dma_addressing(ioc, pdev) != 0) {
+ pr_warn(MPT3SAS_FMT "no suitable DMA mask for %s\n",
+ ioc->name, pci_name(pdev));
+ r = -ENODEV;
+ goto out_fail;
+ }
+
+ for (i = 0, memap_sz = 0, pio_sz = 0 ; i < DEVICE_COUNT_RESOURCE; i++) {
+ if (pci_resource_flags(pdev, i) & IORESOURCE_IO) {
+ if (pio_sz)
+ continue;
+ pio_chip = (u64)pci_resource_start(pdev, i);
+ pio_sz = pci_resource_len(pdev, i);
+ } else if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) {
+ if (memap_sz)
+ continue;
+ ioc->chip_phys = pci_resource_start(pdev, i);
+ chip_phys = (u64)ioc->chip_phys;
+ memap_sz = pci_resource_len(pdev, i);
+ ioc->chip = ioremap(ioc->chip_phys, memap_sz);
+ if (ioc->chip == NULL) {
+ pr_err(MPT3SAS_FMT "unable to map adapter memory!\n",
+ ioc->name);
+ r = -EINVAL;
+ goto out_fail;
+ }
+ }
+ }
+
+ _base_mask_interrupts(ioc);
+ r = _base_enable_msix(ioc);
+ if (r)
+ goto out_fail;
+
+ list_for_each_entry(reply_q, &ioc->reply_queue_list, list)
+ pr_info(MPT3SAS_FMT "%s: IRQ %d\n",
+ reply_q->name, ((ioc->msix_enable) ? "PCI-MSI-X enabled" :
+ "IO-APIC enabled"), reply_q->vector);
+
+ pr_info(MPT3SAS_FMT "iomem(0x%016llx), mapped(0x%p), size(%d)\n",
+ ioc->name, (unsigned long long)chip_phys, ioc->chip, memap_sz);
+ pr_info(MPT3SAS_FMT "ioport(0x%016llx), size(%d)\n",
+ ioc->name, (unsigned long long)pio_chip, pio_sz);
+
+ /* Save PCI configuration state for recovery from PCI AER/EEH errors */
+ pci_save_state(pdev);
+ return 0;
+
+ out_fail:
+ if (ioc->chip_phys)
+ iounmap(ioc->chip);
+ ioc->chip_phys = 0;
+ pci_release_selected_regions(ioc->pdev, ioc->bars);
+ pci_disable_pcie_error_reporting(pdev);
+ pci_disable_device(pdev);
+ return r;
+}
+
+/**
+ * mpt3sas_base_get_msg_frame - obtain request mf pointer
+ * @ioc: per adapter object
+ * @smid: system request message index(smid zero is invalid)
+ *
+ * Returns virt pointer to message frame.
+ */
+void *
+mpt3sas_base_get_msg_frame(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ return (void *)(ioc->request + (smid * ioc->request_sz));
+}
+
+/**
+ * mpt3sas_base_get_sense_buffer - obtain a sense buffer virt addr
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Returns virt pointer to sense buffer.
+ */
+void *
+mpt3sas_base_get_sense_buffer(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ return (void *)(ioc->sense + ((smid - 1) * SCSI_SENSE_BUFFERSIZE));
+}
+
+/**
+ * mpt3sas_base_get_sense_buffer_dma - obtain a sense buffer dma addr
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Returns phys pointer to the low 32bit address of the sense buffer.
+ */
+__le32
+mpt3sas_base_get_sense_buffer_dma(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ return cpu_to_le32(ioc->sense_dma + ((smid - 1) *
+ SCSI_SENSE_BUFFERSIZE));
+}
+
+/**
+ * mpt3sas_base_get_reply_virt_addr - obtain reply frames virt address
+ * @ioc: per adapter object
+ * @phys_addr: lower 32 physical addr of the reply
+ *
+ * Converts 32bit lower physical addr into a virt address.
+ */
+void *
+mpt3sas_base_get_reply_virt_addr(struct MPT3SAS_ADAPTER *ioc, u32 phys_addr)
+{
+ if (!phys_addr)
+ return NULL;
+ return ioc->reply + (phys_addr - (u32)ioc->reply_dma);
+}
+
+/**
+ * mpt3sas_base_get_smid - obtain a free smid from internal queue
+ * @ioc: per adapter object
+ * @cb_idx: callback index
+ *
+ * Returns smid (zero is invalid)
+ */
+u16
+mpt3sas_base_get_smid(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx)
+{
+ unsigned long flags;
+ struct request_tracker *request;
+ u16 smid;
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ if (list_empty(&ioc->internal_free_list)) {
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ pr_err(MPT3SAS_FMT "%s: smid not available\n",
+ ioc->name, __func__);
+ return 0;
+ }
+
+ request = list_entry(ioc->internal_free_list.next,
+ struct request_tracker, tracker_list);
+ request->cb_idx = cb_idx;
+ smid = request->smid;
+ list_del(&request->tracker_list);
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ return smid;
+}
+
+/**
+ * mpt3sas_base_get_smid_scsiio - obtain a free smid from scsiio queue
+ * @ioc: per adapter object
+ * @cb_idx: callback index
+ * @scmd: pointer to scsi command object
+ *
+ * Returns smid (zero is invalid)
+ */
+u16
+mpt3sas_base_get_smid_scsiio(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx,
+ struct scsi_cmnd *scmd)
+{
+ unsigned long flags;
+ struct scsiio_tracker *request;
+ u16 smid;
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ if (list_empty(&ioc->free_list)) {
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ pr_err(MPT3SAS_FMT "%s: smid not available\n",
+ ioc->name, __func__);
+ return 0;
+ }
+
+ request = list_entry(ioc->free_list.next,
+ struct scsiio_tracker, tracker_list);
+ request->scmd = scmd;
+ request->cb_idx = cb_idx;
+ smid = request->smid;
+ list_del(&request->tracker_list);
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ return smid;
+}
+
+/**
+ * mpt3sas_base_get_smid_hpr - obtain a free smid from hi-priority queue
+ * @ioc: per adapter object
+ * @cb_idx: callback index
+ *
+ * Returns smid (zero is invalid)
+ */
+u16
+mpt3sas_base_get_smid_hpr(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx)
+{
+ unsigned long flags;
+ struct request_tracker *request;
+ u16 smid;
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ if (list_empty(&ioc->hpr_free_list)) {
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ return 0;
+ }
+
+ request = list_entry(ioc->hpr_free_list.next,
+ struct request_tracker, tracker_list);
+ request->cb_idx = cb_idx;
+ smid = request->smid;
+ list_del(&request->tracker_list);
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ return smid;
+}
+
+/**
+ * mpt3sas_base_free_smid - put smid back on free_list
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_base_free_smid(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ unsigned long flags;
+ int i;
+ struct chain_tracker *chain_req, *next;
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ if (smid < ioc->hi_priority_smid) {
+ /* scsiio queue */
+ i = smid - 1;
+ if (!list_empty(&ioc->scsi_lookup[i].chain_list)) {
+ list_for_each_entry_safe(chain_req, next,
+ &ioc->scsi_lookup[i].chain_list, tracker_list) {
+ list_del_init(&chain_req->tracker_list);
+ list_add(&chain_req->tracker_list,
+ &ioc->free_chain_list);
+ }
+ }
+ ioc->scsi_lookup[i].cb_idx = 0xFF;
+ ioc->scsi_lookup[i].scmd = NULL;
+ list_add(&ioc->scsi_lookup[i].tracker_list, &ioc->free_list);
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+
+ /*
+ * See _wait_for_commands_to_complete() call with regards
+ * to this code.
+ */
+ if (ioc->shost_recovery && ioc->pending_io_count) {
+ if (ioc->pending_io_count == 1)
+ wake_up(&ioc->reset_wq);
+ ioc->pending_io_count--;
+ }
+ return;
+ } else if (smid < ioc->internal_smid) {
+ /* hi-priority */
+ i = smid - ioc->hi_priority_smid;
+ ioc->hpr_lookup[i].cb_idx = 0xFF;
+ list_add(&ioc->hpr_lookup[i].tracker_list, &ioc->hpr_free_list);
+ } else if (smid <= ioc->hba_queue_depth) {
+ /* internal queue */
+ i = smid - ioc->internal_smid;
+ ioc->internal_lookup[i].cb_idx = 0xFF;
+ list_add(&ioc->internal_lookup[i].tracker_list,
+ &ioc->internal_free_list);
+ }
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+}
+
+/**
+ * _base_writeq - 64 bit write to MMIO
+ * @ioc: per adapter object
+ * @b: data payload
+ * @addr: address in MMIO space
+ * @writeq_lock: spin lock
+ *
+ * Glue for handling an atomic 64 bit word to MMIO. This special handling takes
+ * care of 32 bit environment where its not quarenteed to send the entire word
+ * in one transfer.
+ */
+#if defined(writeq) && defined(CONFIG_64BIT)
+static inline void
+_base_writeq(__u64 b, volatile void __iomem *addr, spinlock_t *writeq_lock)
+{
+ writeq(cpu_to_le64(b), addr);
+}
+#else
+static inline void
+_base_writeq(__u64 b, volatile void __iomem *addr, spinlock_t *writeq_lock)
+{
+ unsigned long flags;
+ __u64 data_out = cpu_to_le64(b);
+
+ spin_lock_irqsave(writeq_lock, flags);
+ writel((u32)(data_out), addr);
+ writel((u32)(data_out >> 32), (addr + 4));
+ spin_unlock_irqrestore(writeq_lock, flags);
+}
+#endif
+
+static inline u8
+_base_get_msix_index(struct MPT3SAS_ADAPTER *ioc)
+{
+ return ioc->cpu_msix_table[raw_smp_processor_id()];
+}
+
+/**
+ * mpt3sas_base_put_smid_scsi_io - send SCSI_IO request to firmware
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @handle: device handle
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_base_put_smid_scsi_io(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 handle)
+{
+ Mpi2RequestDescriptorUnion_t descriptor;
+ u64 *request = (u64 *)&descriptor;
+
+
+ descriptor.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO;
+ descriptor.SCSIIO.MSIxIndex = _base_get_msix_index(ioc);
+ descriptor.SCSIIO.SMID = cpu_to_le16(smid);
+ descriptor.SCSIIO.DevHandle = cpu_to_le16(handle);
+ descriptor.SCSIIO.LMID = 0;
+ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
+}
+
+/**
+ * mpt3sas_base_put_smid_fast_path - send fast path request to firmware
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @handle: device handle
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_base_put_smid_fast_path(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u16 handle)
+{
+ Mpi2RequestDescriptorUnion_t descriptor;
+ u64 *request = (u64 *)&descriptor;
+
+ descriptor.SCSIIO.RequestFlags =
+ MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO;
+ descriptor.SCSIIO.MSIxIndex = _base_get_msix_index(ioc);
+ descriptor.SCSIIO.SMID = cpu_to_le16(smid);
+ descriptor.SCSIIO.DevHandle = cpu_to_le16(handle);
+ descriptor.SCSIIO.LMID = 0;
+ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
+}
+
+/**
+ * mpt3sas_base_put_smid_hi_priority - send Task Managment request to firmware
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_base_put_smid_hi_priority(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ Mpi2RequestDescriptorUnion_t descriptor;
+ u64 *request = (u64 *)&descriptor;
+
+ descriptor.HighPriority.RequestFlags =
+ MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY;
+ descriptor.HighPriority.MSIxIndex = 0;
+ descriptor.HighPriority.SMID = cpu_to_le16(smid);
+ descriptor.HighPriority.LMID = 0;
+ descriptor.HighPriority.Reserved1 = 0;
+ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
+}
+
+/**
+ * mpt3sas_base_put_smid_default - Default, primarily used for config pages
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_base_put_smid_default(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ Mpi2RequestDescriptorUnion_t descriptor;
+ u64 *request = (u64 *)&descriptor;
+
+ descriptor.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ descriptor.Default.MSIxIndex = _base_get_msix_index(ioc);
+ descriptor.Default.SMID = cpu_to_le16(smid);
+ descriptor.Default.LMID = 0;
+ descriptor.Default.DescriptorTypeDependent = 0;
+ _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow,
+ &ioc->scsi_lookup_lock);
+}
+
+
+
+/**
+ * _base_display_ioc_capabilities - Disply IOC's capabilities.
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+static void
+_base_display_ioc_capabilities(struct MPT3SAS_ADAPTER *ioc)
+{
+ int i = 0;
+ char desc[16];
+ u32 iounit_pg1_flags;
+ u32 bios_version;
+
+ bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion);
+ strncpy(desc, ioc->manu_pg0.ChipName, 16);
+ pr_info(MPT3SAS_FMT "%s: FWVersion(%02d.%02d.%02d.%02d), "\
+ "ChipRevision(0x%02x), BiosVersion(%02d.%02d.%02d.%02d)\n",
+ ioc->name, desc,
+ (ioc->facts.FWVersion.Word & 0xFF000000) >> 24,
+ (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16,
+ (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8,
+ ioc->facts.FWVersion.Word & 0x000000FF,
+ ioc->pdev->revision,
+ (bios_version & 0xFF000000) >> 24,
+ (bios_version & 0x00FF0000) >> 16,
+ (bios_version & 0x0000FF00) >> 8,
+ bios_version & 0x000000FF);
+
+ pr_info(MPT3SAS_FMT "Protocol=(", ioc->name);
+
+ if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR) {
+ pr_info("Initiator");
+ i++;
+ }
+
+ if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET) {
+ pr_info("%sTarget", i ? "," : "");
+ i++;
+ }
+
+ i = 0;
+ pr_info("), ");
+ pr_info("Capabilities=(");
+
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID) {
+ pr_info("Raid");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR) {
+ pr_info("%sTLR", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_MULTICAST) {
+ pr_info("%sMulticast", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET) {
+ pr_info("%sBIDI Target", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_EEDP) {
+ pr_info("%sEEDP", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER) {
+ pr_info("%sSnapshot Buffer", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER) {
+ pr_info("%sDiag Trace Buffer", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER) {
+ pr_info("%sDiag Extended Buffer", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING) {
+ pr_info("%sTask Set Full", i ? "," : "");
+ i++;
+ }
+
+ iounit_pg1_flags = le32_to_cpu(ioc->iounit_pg1.Flags);
+ if (!(iounit_pg1_flags & MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE)) {
+ pr_info("%sNCQ", i ? "," : "");
+ i++;
+ }
+
+ pr_info(")\n");
+}
+
+/**
+ * mpt3sas_base_update_missing_delay - change the missing delay timers
+ * @ioc: per adapter object
+ * @device_missing_delay: amount of time till device is reported missing
+ * @io_missing_delay: interval IO is returned when there is a missing device
+ *
+ * Return nothing.
+ *
+ * Passed on the command line, this function will modify the device missing
+ * delay, as well as the io missing delay. This should be called at driver
+ * load time.
+ */
+void
+mpt3sas_base_update_missing_delay(struct MPT3SAS_ADAPTER *ioc,
+ u16 device_missing_delay, u8 io_missing_delay)
+{
+ u16 dmd, dmd_new, dmd_orignal;
+ u8 io_missing_delay_original;
+ u16 sz;
+ Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
+ Mpi2ConfigReply_t mpi_reply;
+ u8 num_phys = 0;
+ u16 ioc_status;
+
+ mpt3sas_config_get_number_hba_phys(ioc, &num_phys);
+ if (!num_phys)
+ return;
+
+ sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (num_phys *
+ sizeof(Mpi2SasIOUnit1PhyData_t));
+ sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg1) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ if ((mpt3sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
+ sas_iounit_pg1, sz))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ /* device missing delay */
+ dmd = sas_iounit_pg1->ReportDeviceMissingDelay;
+ if (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16)
+ dmd = (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16;
+ else
+ dmd = dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK;
+ dmd_orignal = dmd;
+ if (device_missing_delay > 0x7F) {
+ dmd = (device_missing_delay > 0x7F0) ? 0x7F0 :
+ device_missing_delay;
+ dmd = dmd / 16;
+ dmd |= MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16;
+ } else
+ dmd = device_missing_delay;
+ sas_iounit_pg1->ReportDeviceMissingDelay = dmd;
+
+ /* io missing delay */
+ io_missing_delay_original = sas_iounit_pg1->IODeviceMissingDelay;
+ sas_iounit_pg1->IODeviceMissingDelay = io_missing_delay;
+
+ if (!mpt3sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1,
+ sz)) {
+ if (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16)
+ dmd_new = (dmd &
+ MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16;
+ else
+ dmd_new =
+ dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK;
+ pr_info(MPT3SAS_FMT "device_missing_delay: old(%d), new(%d)\n",
+ ioc->name, dmd_orignal, dmd_new);
+ pr_info(MPT3SAS_FMT "ioc_missing_delay: old(%d), new(%d)\n",
+ ioc->name, io_missing_delay_original,
+ io_missing_delay);
+ ioc->device_missing_delay = dmd_new;
+ ioc->io_missing_delay = io_missing_delay;
+ }
+
+out:
+ kfree(sas_iounit_pg1);
+}
+/**
+ * _base_static_config_pages - static start of day config pages
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+static void
+_base_static_config_pages(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ u32 iounit_pg1_flags;
+
+ mpt3sas_config_get_manufacturing_pg0(ioc, &mpi_reply, &ioc->manu_pg0);
+ if (ioc->ir_firmware)
+ mpt3sas_config_get_manufacturing_pg10(ioc, &mpi_reply,
+ &ioc->manu_pg10);
+
+ /*
+ * Ensure correct T10 PI operation if vendor left EEDPTagMode
+ * flag unset in NVDATA.
+ */
+ mpt3sas_config_get_manufacturing_pg11(ioc, &mpi_reply, &ioc->manu_pg11);
+ if (ioc->manu_pg11.EEDPTagMode == 0) {
+ pr_err("%s: overriding NVDATA EEDPTagMode setting\n",
+ ioc->name);
+ ioc->manu_pg11.EEDPTagMode &= ~0x3;
+ ioc->manu_pg11.EEDPTagMode |= 0x1;
+ mpt3sas_config_set_manufacturing_pg11(ioc, &mpi_reply,
+ &ioc->manu_pg11);
+ }
+
+ mpt3sas_config_get_bios_pg2(ioc, &mpi_reply, &ioc->bios_pg2);
+ mpt3sas_config_get_bios_pg3(ioc, &mpi_reply, &ioc->bios_pg3);
+ mpt3sas_config_get_ioc_pg8(ioc, &mpi_reply, &ioc->ioc_pg8);
+ mpt3sas_config_get_iounit_pg0(ioc, &mpi_reply, &ioc->iounit_pg0);
+ mpt3sas_config_get_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1);
+ _base_display_ioc_capabilities(ioc);
+
+ /*
+ * Enable task_set_full handling in iounit_pg1 when the
+ * facts capabilities indicate that its supported.
+ */
+ iounit_pg1_flags = le32_to_cpu(ioc->iounit_pg1.Flags);
+ if ((ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING))
+ iounit_pg1_flags &=
+ ~MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING;
+ else
+ iounit_pg1_flags |=
+ MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING;
+ ioc->iounit_pg1.Flags = cpu_to_le32(iounit_pg1_flags);
+ mpt3sas_config_set_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1);
+}
+
+/**
+ * _base_release_memory_pools - release memory
+ * @ioc: per adapter object
+ *
+ * Free memory allocated from _base_allocate_memory_pools.
+ *
+ * Return nothing.
+ */
+static void
+_base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc)
+{
+ int i;
+
+ dexitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+ if (ioc->request) {
+ pci_free_consistent(ioc->pdev, ioc->request_dma_sz,
+ ioc->request, ioc->request_dma);
+ dexitprintk(ioc, pr_info(MPT3SAS_FMT
+ "request_pool(0x%p): free\n",
+ ioc->name, ioc->request));
+ ioc->request = NULL;
+ }
+
+ if (ioc->sense) {
+ pci_pool_free(ioc->sense_dma_pool, ioc->sense, ioc->sense_dma);
+ if (ioc->sense_dma_pool)
+ pci_pool_destroy(ioc->sense_dma_pool);
+ dexitprintk(ioc, pr_info(MPT3SAS_FMT
+ "sense_pool(0x%p): free\n",
+ ioc->name, ioc->sense));
+ ioc->sense = NULL;
+ }
+
+ if (ioc->reply) {
+ pci_pool_free(ioc->reply_dma_pool, ioc->reply, ioc->reply_dma);
+ if (ioc->reply_dma_pool)
+ pci_pool_destroy(ioc->reply_dma_pool);
+ dexitprintk(ioc, pr_info(MPT3SAS_FMT
+ "reply_pool(0x%p): free\n",
+ ioc->name, ioc->reply));
+ ioc->reply = NULL;
+ }
+
+ if (ioc->reply_free) {
+ pci_pool_free(ioc->reply_free_dma_pool, ioc->reply_free,
+ ioc->reply_free_dma);
+ if (ioc->reply_free_dma_pool)
+ pci_pool_destroy(ioc->reply_free_dma_pool);
+ dexitprintk(ioc, pr_info(MPT3SAS_FMT
+ "reply_free_pool(0x%p): free\n",
+ ioc->name, ioc->reply_free));
+ ioc->reply_free = NULL;
+ }
+
+ if (ioc->reply_post_free) {
+ pci_pool_free(ioc->reply_post_free_dma_pool,
+ ioc->reply_post_free, ioc->reply_post_free_dma);
+ if (ioc->reply_post_free_dma_pool)
+ pci_pool_destroy(ioc->reply_post_free_dma_pool);
+ dexitprintk(ioc, pr_info(MPT3SAS_FMT
+ "reply_post_free_pool(0x%p): free\n", ioc->name,
+ ioc->reply_post_free));
+ ioc->reply_post_free = NULL;
+ }
+
+ if (ioc->config_page) {
+ dexitprintk(ioc, pr_info(MPT3SAS_FMT
+ "config_page(0x%p): free\n", ioc->name,
+ ioc->config_page));
+ pci_free_consistent(ioc->pdev, ioc->config_page_sz,
+ ioc->config_page, ioc->config_page_dma);
+ }
+
+ if (ioc->scsi_lookup) {
+ free_pages((ulong)ioc->scsi_lookup, ioc->scsi_lookup_pages);
+ ioc->scsi_lookup = NULL;
+ }
+ kfree(ioc->hpr_lookup);
+ kfree(ioc->internal_lookup);
+ if (ioc->chain_lookup) {
+ for (i = 0; i < ioc->chain_depth; i++) {
+ if (ioc->chain_lookup[i].chain_buffer)
+ pci_pool_free(ioc->chain_dma_pool,
+ ioc->chain_lookup[i].chain_buffer,
+ ioc->chain_lookup[i].chain_buffer_dma);
+ }
+ if (ioc->chain_dma_pool)
+ pci_pool_destroy(ioc->chain_dma_pool);
+ free_pages((ulong)ioc->chain_lookup, ioc->chain_pages);
+ ioc->chain_lookup = NULL;
+ }
+}
+
+/**
+ * _base_allocate_memory_pools - allocate start of day memory pools
+ * @ioc: per adapter object
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 success, anything else error
+ */
+static int
+_base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
+{
+ struct mpt3sas_facts *facts;
+ u16 max_sge_elements;
+ u16 chains_needed_per_io;
+ u32 sz, total_sz, reply_post_free_sz;
+ u32 retry_sz;
+ u16 max_request_credit;
+ unsigned short sg_tablesize;
+ u16 sge_size;
+ int i;
+
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+
+ retry_sz = 0;
+ facts = &ioc->facts;
+
+ /* command line tunables for max sgl entries */
+ if (max_sgl_entries != -1)
+ sg_tablesize = max_sgl_entries;
+ else
+ sg_tablesize = MPT3SAS_SG_DEPTH;
+
+ if (sg_tablesize < MPT3SAS_MIN_PHYS_SEGMENTS)
+ sg_tablesize = MPT3SAS_MIN_PHYS_SEGMENTS;
+ else if (sg_tablesize > MPT3SAS_MAX_PHYS_SEGMENTS)
+ sg_tablesize = MPT3SAS_MAX_PHYS_SEGMENTS;
+ ioc->shost->sg_tablesize = sg_tablesize;
+
+ ioc->hi_priority_depth = facts->HighPriorityCredit;
+ ioc->internal_depth = ioc->hi_priority_depth + (5);
+ /* command line tunables for max controller queue depth */
+ if (max_queue_depth != -1 && max_queue_depth != 0) {
+ max_request_credit = min_t(u16, max_queue_depth +
+ ioc->hi_priority_depth + ioc->internal_depth,
+ facts->RequestCredit);
+ if (max_request_credit > MAX_HBA_QUEUE_DEPTH)
+ max_request_credit = MAX_HBA_QUEUE_DEPTH;
+ } else
+ max_request_credit = min_t(u16, facts->RequestCredit,
+ MAX_HBA_QUEUE_DEPTH);
+
+ ioc->hba_queue_depth = max_request_credit;
+
+ /* request frame size */
+ ioc->request_sz = facts->IOCRequestFrameSize * 4;
+
+ /* reply frame size */
+ ioc->reply_sz = facts->ReplyFrameSize * 4;
+
+ /* calculate the max scatter element size */
+ sge_size = max_t(u16, ioc->sge_size, ioc->sge_size_ieee);
+
+ retry_allocation:
+ total_sz = 0;
+ /* calculate number of sg elements left over in the 1st frame */
+ max_sge_elements = ioc->request_sz - ((sizeof(Mpi2SCSIIORequest_t) -
+ sizeof(Mpi2SGEIOUnion_t)) + sge_size);
+ ioc->max_sges_in_main_message = max_sge_elements/sge_size;
+
+ /* now do the same for a chain buffer */
+ max_sge_elements = ioc->request_sz - sge_size;
+ ioc->max_sges_in_chain_message = max_sge_elements/sge_size;
+
+ /*
+ * MPT3SAS_SG_DEPTH = CONFIG_FUSION_MAX_SGE
+ */
+ chains_needed_per_io = ((ioc->shost->sg_tablesize -
+ ioc->max_sges_in_main_message)/ioc->max_sges_in_chain_message)
+ + 1;
+ if (chains_needed_per_io > facts->MaxChainDepth) {
+ chains_needed_per_io = facts->MaxChainDepth;
+ ioc->shost->sg_tablesize = min_t(u16,
+ ioc->max_sges_in_main_message + (ioc->max_sges_in_chain_message
+ * chains_needed_per_io), ioc->shost->sg_tablesize);
+ }
+ ioc->chains_needed_per_io = chains_needed_per_io;
+
+ /* reply free queue sizing - taking into account for 64 FW events */
+ ioc->reply_free_queue_depth = ioc->hba_queue_depth + 64;
+
+ /* calculate reply descriptor post queue depth */
+ ioc->reply_post_queue_depth = ioc->hba_queue_depth +
+ ioc->reply_free_queue_depth + 1 ;
+ /* align the reply post queue on the next 16 count boundary */
+ if (ioc->reply_post_queue_depth % 16)
+ ioc->reply_post_queue_depth += 16 -
+ (ioc->reply_post_queue_depth % 16);
+
+
+ if (ioc->reply_post_queue_depth >
+ facts->MaxReplyDescriptorPostQueueDepth) {
+ ioc->reply_post_queue_depth =
+ facts->MaxReplyDescriptorPostQueueDepth -
+ (facts->MaxReplyDescriptorPostQueueDepth % 16);
+ ioc->hba_queue_depth =
+ ((ioc->reply_post_queue_depth - 64) / 2) - 1;
+ ioc->reply_free_queue_depth = ioc->hba_queue_depth + 64;
+ }
+
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "scatter gather: " \
+ "sge_in_main_msg(%d), sge_per_chain(%d), sge_per_io(%d), "
+ "chains_per_io(%d)\n", ioc->name, ioc->max_sges_in_main_message,
+ ioc->max_sges_in_chain_message, ioc->shost->sg_tablesize,
+ ioc->chains_needed_per_io));
+
+ ioc->scsiio_depth = ioc->hba_queue_depth -
+ ioc->hi_priority_depth - ioc->internal_depth;
+
+ /* set the scsi host can_queue depth
+ * with some internal commands that could be outstanding
+ */
+ ioc->shost->can_queue = ioc->scsiio_depth;
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT
+ "scsi host: can_queue depth (%d)\n",
+ ioc->name, ioc->shost->can_queue));
+
+
+ /* contiguous pool for request and chains, 16 byte align, one extra "
+ * "frame for smid=0
+ */
+ ioc->chain_depth = ioc->chains_needed_per_io * ioc->scsiio_depth;
+ sz = ((ioc->scsiio_depth + 1) * ioc->request_sz);
+
+ /* hi-priority queue */
+ sz += (ioc->hi_priority_depth * ioc->request_sz);
+
+ /* internal queue */
+ sz += (ioc->internal_depth * ioc->request_sz);
+
+ ioc->request_dma_sz = sz;
+ ioc->request = pci_alloc_consistent(ioc->pdev, sz, &ioc->request_dma);
+ if (!ioc->request) {
+ pr_err(MPT3SAS_FMT "request pool: pci_alloc_consistent " \
+ "failed: hba_depth(%d), chains_per_io(%d), frame_sz(%d), "
+ "total(%d kB)\n", ioc->name, ioc->hba_queue_depth,
+ ioc->chains_needed_per_io, ioc->request_sz, sz/1024);
+ if (ioc->scsiio_depth < MPT3SAS_SAS_QUEUE_DEPTH)
+ goto out;
+ retry_sz += 64;
+ ioc->hba_queue_depth = max_request_credit - retry_sz;
+ goto retry_allocation;
+ }
+
+ if (retry_sz)
+ pr_err(MPT3SAS_FMT "request pool: pci_alloc_consistent " \
+ "succeed: hba_depth(%d), chains_per_io(%d), frame_sz(%d), "
+ "total(%d kb)\n", ioc->name, ioc->hba_queue_depth,
+ ioc->chains_needed_per_io, ioc->request_sz, sz/1024);
+
+ /* hi-priority queue */
+ ioc->hi_priority = ioc->request + ((ioc->scsiio_depth + 1) *
+ ioc->request_sz);
+ ioc->hi_priority_dma = ioc->request_dma + ((ioc->scsiio_depth + 1) *
+ ioc->request_sz);
+
+ /* internal queue */
+ ioc->internal = ioc->hi_priority + (ioc->hi_priority_depth *
+ ioc->request_sz);
+ ioc->internal_dma = ioc->hi_priority_dma + (ioc->hi_priority_depth *
+ ioc->request_sz);
+
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT
+ "request pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB)\n",
+ ioc->name, ioc->request, ioc->hba_queue_depth, ioc->request_sz,
+ (ioc->hba_queue_depth * ioc->request_sz)/1024));
+
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "request pool: dma(0x%llx)\n",
+ ioc->name, (unsigned long long) ioc->request_dma));
+ total_sz += sz;
+
+ sz = ioc->scsiio_depth * sizeof(struct scsiio_tracker);
+ ioc->scsi_lookup_pages = get_order(sz);
+ ioc->scsi_lookup = (struct scsiio_tracker *)__get_free_pages(
+ GFP_KERNEL, ioc->scsi_lookup_pages);
+ if (!ioc->scsi_lookup) {
+ pr_err(MPT3SAS_FMT "scsi_lookup: get_free_pages failed, sz(%d)\n",
+ ioc->name, (int)sz);
+ goto out;
+ }
+
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "scsiio(0x%p): depth(%d)\n",
+ ioc->name, ioc->request, ioc->scsiio_depth));
+
+ ioc->chain_depth = min_t(u32, ioc->chain_depth, MAX_CHAIN_DEPTH);
+ sz = ioc->chain_depth * sizeof(struct chain_tracker);
+ ioc->chain_pages = get_order(sz);
+ ioc->chain_lookup = (struct chain_tracker *)__get_free_pages(
+ GFP_KERNEL, ioc->chain_pages);
+ if (!ioc->chain_lookup) {
+ pr_err(MPT3SAS_FMT "chain_lookup: __get_free_pages failed\n",
+ ioc->name);
+ goto out;
+ }
+ ioc->chain_dma_pool = pci_pool_create("chain pool", ioc->pdev,
+ ioc->request_sz, 16, 0);
+ if (!ioc->chain_dma_pool) {
+ pr_err(MPT3SAS_FMT "chain_dma_pool: pci_pool_create failed\n",
+ ioc->name);
+ goto out;
+ }
+ for (i = 0; i < ioc->chain_depth; i++) {
+ ioc->chain_lookup[i].chain_buffer = pci_pool_alloc(
+ ioc->chain_dma_pool , GFP_KERNEL,
+ &ioc->chain_lookup[i].chain_buffer_dma);
+ if (!ioc->chain_lookup[i].chain_buffer) {
+ ioc->chain_depth = i;
+ goto chain_done;
+ }
+ total_sz += ioc->request_sz;
+ }
+ chain_done:
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT
+ "chain pool depth(%d), frame_size(%d), pool_size(%d kB)\n",
+ ioc->name, ioc->chain_depth, ioc->request_sz,
+ ((ioc->chain_depth * ioc->request_sz))/1024));
+
+ /* initialize hi-priority queue smid's */
+ ioc->hpr_lookup = kcalloc(ioc->hi_priority_depth,
+ sizeof(struct request_tracker), GFP_KERNEL);
+ if (!ioc->hpr_lookup) {
+ pr_err(MPT3SAS_FMT "hpr_lookup: kcalloc failed\n",
+ ioc->name);
+ goto out;
+ }
+ ioc->hi_priority_smid = ioc->scsiio_depth + 1;
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT
+ "hi_priority(0x%p): depth(%d), start smid(%d)\n",
+ ioc->name, ioc->hi_priority,
+ ioc->hi_priority_depth, ioc->hi_priority_smid));
+
+ /* initialize internal queue smid's */
+ ioc->internal_lookup = kcalloc(ioc->internal_depth,
+ sizeof(struct request_tracker), GFP_KERNEL);
+ if (!ioc->internal_lookup) {
+ pr_err(MPT3SAS_FMT "internal_lookup: kcalloc failed\n",
+ ioc->name);
+ goto out;
+ }
+ ioc->internal_smid = ioc->hi_priority_smid + ioc->hi_priority_depth;
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT
+ "internal(0x%p): depth(%d), start smid(%d)\n",
+ ioc->name, ioc->internal,
+ ioc->internal_depth, ioc->internal_smid));
+
+ /* sense buffers, 4 byte align */
+ sz = ioc->scsiio_depth * SCSI_SENSE_BUFFERSIZE;
+ ioc->sense_dma_pool = pci_pool_create("sense pool", ioc->pdev, sz, 4,
+ 0);
+ if (!ioc->sense_dma_pool) {
+ pr_err(MPT3SAS_FMT "sense pool: pci_pool_create failed\n",
+ ioc->name);
+ goto out;
+ }
+ ioc->sense = pci_pool_alloc(ioc->sense_dma_pool , GFP_KERNEL,
+ &ioc->sense_dma);
+ if (!ioc->sense) {
+ pr_err(MPT3SAS_FMT "sense pool: pci_pool_alloc failed\n",
+ ioc->name);
+ goto out;
+ }
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT
+ "sense pool(0x%p): depth(%d), element_size(%d), pool_size"
+ "(%d kB)\n", ioc->name, ioc->sense, ioc->scsiio_depth,
+ SCSI_SENSE_BUFFERSIZE, sz/1024));
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "sense_dma(0x%llx)\n",
+ ioc->name, (unsigned long long)ioc->sense_dma));
+ total_sz += sz;
+
+ /* reply pool, 4 byte align */
+ sz = ioc->reply_free_queue_depth * ioc->reply_sz;
+ ioc->reply_dma_pool = pci_pool_create("reply pool", ioc->pdev, sz, 4,
+ 0);
+ if (!ioc->reply_dma_pool) {
+ pr_err(MPT3SAS_FMT "reply pool: pci_pool_create failed\n",
+ ioc->name);
+ goto out;
+ }
+ ioc->reply = pci_pool_alloc(ioc->reply_dma_pool , GFP_KERNEL,
+ &ioc->reply_dma);
+ if (!ioc->reply) {
+ pr_err(MPT3SAS_FMT "reply pool: pci_pool_alloc failed\n",
+ ioc->name);
+ goto out;
+ }
+ ioc->reply_dma_min_address = (u32)(ioc->reply_dma);
+ ioc->reply_dma_max_address = (u32)(ioc->reply_dma) + sz;
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT
+ "reply pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB)\n",
+ ioc->name, ioc->reply,
+ ioc->reply_free_queue_depth, ioc->reply_sz, sz/1024));
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "reply_dma(0x%llx)\n",
+ ioc->name, (unsigned long long)ioc->reply_dma));
+ total_sz += sz;
+
+ /* reply free queue, 16 byte align */
+ sz = ioc->reply_free_queue_depth * 4;
+ ioc->reply_free_dma_pool = pci_pool_create("reply_free pool",
+ ioc->pdev, sz, 16, 0);
+ if (!ioc->reply_free_dma_pool) {
+ pr_err(MPT3SAS_FMT "reply_free pool: pci_pool_create failed\n",
+ ioc->name);
+ goto out;
+ }
+ ioc->reply_free = pci_pool_alloc(ioc->reply_free_dma_pool , GFP_KERNEL,
+ &ioc->reply_free_dma);
+ if (!ioc->reply_free) {
+ pr_err(MPT3SAS_FMT "reply_free pool: pci_pool_alloc failed\n",
+ ioc->name);
+ goto out;
+ }
+ memset(ioc->reply_free, 0, sz);
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "reply_free pool(0x%p): " \
+ "depth(%d), element_size(%d), pool_size(%d kB)\n", ioc->name,
+ ioc->reply_free, ioc->reply_free_queue_depth, 4, sz/1024));
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT
+ "reply_free_dma (0x%llx)\n",
+ ioc->name, (unsigned long long)ioc->reply_free_dma));
+ total_sz += sz;
+
+ /* reply post queue, 16 byte align */
+ reply_post_free_sz = ioc->reply_post_queue_depth *
+ sizeof(Mpi2DefaultReplyDescriptor_t);
+ if (_base_is_controller_msix_enabled(ioc))
+ sz = reply_post_free_sz * ioc->reply_queue_count;
+ else
+ sz = reply_post_free_sz;
+ ioc->reply_post_free_dma_pool = pci_pool_create("reply_post_free pool",
+ ioc->pdev, sz, 16, 0);
+ if (!ioc->reply_post_free_dma_pool) {
+ pr_err(MPT3SAS_FMT
+ "reply_post_free pool: pci_pool_create failed\n",
+ ioc->name);
+ goto out;
+ }
+ ioc->reply_post_free = pci_pool_alloc(ioc->reply_post_free_dma_pool ,
+ GFP_KERNEL, &ioc->reply_post_free_dma);
+ if (!ioc->reply_post_free) {
+ pr_err(MPT3SAS_FMT
+ "reply_post_free pool: pci_pool_alloc failed\n",
+ ioc->name);
+ goto out;
+ }
+ memset(ioc->reply_post_free, 0, sz);
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "reply post free pool" \
+ "(0x%p): depth(%d), element_size(%d), pool_size(%d kB)\n",
+ ioc->name, ioc->reply_post_free, ioc->reply_post_queue_depth, 8,
+ sz/1024));
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT
+ "reply_post_free_dma = (0x%llx)\n",
+ ioc->name, (unsigned long long)
+ ioc->reply_post_free_dma));
+ total_sz += sz;
+
+ ioc->config_page_sz = 512;
+ ioc->config_page = pci_alloc_consistent(ioc->pdev,
+ ioc->config_page_sz, &ioc->config_page_dma);
+ if (!ioc->config_page) {
+ pr_err(MPT3SAS_FMT
+ "config page: pci_pool_alloc failed\n",
+ ioc->name);
+ goto out;
+ }
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT
+ "config page(0x%p): size(%d)\n",
+ ioc->name, ioc->config_page, ioc->config_page_sz));
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "config_page_dma(0x%llx)\n",
+ ioc->name, (unsigned long long)ioc->config_page_dma));
+ total_sz += ioc->config_page_sz;
+
+ pr_info(MPT3SAS_FMT "Allocated physical memory: size(%d kB)\n",
+ ioc->name, total_sz/1024);
+ pr_info(MPT3SAS_FMT
+ "Current Controller Queue Depth(%d),Max Controller Queue Depth(%d)\n",
+ ioc->name, ioc->shost->can_queue, facts->RequestCredit);
+ pr_info(MPT3SAS_FMT "Scatter Gather Elements per IO(%d)\n",
+ ioc->name, ioc->shost->sg_tablesize);
+ return 0;
+
+ out:
+ return -ENOMEM;
+}
+
+/**
+ * mpt3sas_base_get_iocstate - Get the current state of a MPT adapter.
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @cooked: Request raw or cooked IOC state
+ *
+ * Returns all IOC Doorbell register bits if cooked==0, else just the
+ * Doorbell bits in MPI_IOC_STATE_MASK.
+ */
+u32
+mpt3sas_base_get_iocstate(struct MPT3SAS_ADAPTER *ioc, int cooked)
+{
+ u32 s, sc;
+
+ s = readl(&ioc->chip->Doorbell);
+ sc = s & MPI2_IOC_STATE_MASK;
+ return cooked ? sc : s;
+}
+
+/**
+ * _base_wait_on_iocstate - waiting on a particular ioc state
+ * @ioc_state: controller state { READY, OPERATIONAL, or RESET }
+ * @timeout: timeout in second
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_wait_on_iocstate(struct MPT3SAS_ADAPTER *ioc, u32 ioc_state, int timeout,
+ int sleep_flag)
+{
+ u32 count, cntdn;
+ u32 current_state;
+
+ count = 0;
+ cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout;
+ do {
+ current_state = mpt3sas_base_get_iocstate(ioc, 1);
+ if (current_state == ioc_state)
+ return 0;
+ if (count && current_state == MPI2_IOC_STATE_FAULT)
+ break;
+ if (sleep_flag == CAN_SLEEP)
+ usleep_range(1000, 1500);
+ else
+ udelay(500);
+ count++;
+ } while (--cntdn);
+
+ return current_state;
+}
+
+/**
+ * _base_wait_for_doorbell_int - waiting for controller interrupt(generated by
+ * a write to the doorbell)
+ * @ioc: per adapter object
+ * @timeout: timeout in second
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ *
+ * Notes: MPI2_HIS_IOC2SYS_DB_STATUS - set to one when IOC writes to doorbell.
+ */
+static int
+_base_wait_for_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout,
+ int sleep_flag)
+{
+ u32 cntdn, count;
+ u32 int_status;
+
+ count = 0;
+ cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout;
+ do {
+ int_status = readl(&ioc->chip->HostInterruptStatus);
+ if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) {
+ dhsprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: successful count(%d), timeout(%d)\n",
+ ioc->name, __func__, count, timeout));
+ return 0;
+ }
+ if (sleep_flag == CAN_SLEEP)
+ usleep_range(1000, 1500);
+ else
+ udelay(500);
+ count++;
+ } while (--cntdn);
+
+ pr_err(MPT3SAS_FMT
+ "%s: failed due to timeout count(%d), int_status(%x)!\n",
+ ioc->name, __func__, count, int_status);
+ return -EFAULT;
+}
+
+/**
+ * _base_wait_for_doorbell_ack - waiting for controller to read the doorbell.
+ * @ioc: per adapter object
+ * @timeout: timeout in second
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ *
+ * Notes: MPI2_HIS_SYS2IOC_DB_STATUS - set to one when host writes to
+ * doorbell.
+ */
+static int
+_base_wait_for_doorbell_ack(struct MPT3SAS_ADAPTER *ioc, int timeout,
+ int sleep_flag)
+{
+ u32 cntdn, count;
+ u32 int_status;
+ u32 doorbell;
+
+ count = 0;
+ cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout;
+ do {
+ int_status = readl(&ioc->chip->HostInterruptStatus);
+ if (!(int_status & MPI2_HIS_SYS2IOC_DB_STATUS)) {
+ dhsprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: successful count(%d), timeout(%d)\n",
+ ioc->name, __func__, count, timeout));
+ return 0;
+ } else if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) {
+ doorbell = readl(&ioc->chip->Doorbell);
+ if ((doorbell & MPI2_IOC_STATE_MASK) ==
+ MPI2_IOC_STATE_FAULT) {
+ mpt3sas_base_fault_info(ioc , doorbell);
+ return -EFAULT;
+ }
+ } else if (int_status == 0xFFFFFFFF)
+ goto out;
+
+ if (sleep_flag == CAN_SLEEP)
+ usleep_range(1000, 1500);
+ else
+ udelay(500);
+ count++;
+ } while (--cntdn);
+
+ out:
+ pr_err(MPT3SAS_FMT
+ "%s: failed due to timeout count(%d), int_status(%x)!\n",
+ ioc->name, __func__, count, int_status);
+ return -EFAULT;
+}
+
+/**
+ * _base_wait_for_doorbell_not_used - waiting for doorbell to not be in use
+ * @ioc: per adapter object
+ * @timeout: timeout in second
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ *
+ */
+static int
+_base_wait_for_doorbell_not_used(struct MPT3SAS_ADAPTER *ioc, int timeout,
+ int sleep_flag)
+{
+ u32 cntdn, count;
+ u32 doorbell_reg;
+
+ count = 0;
+ cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout;
+ do {
+ doorbell_reg = readl(&ioc->chip->Doorbell);
+ if (!(doorbell_reg & MPI2_DOORBELL_USED)) {
+ dhsprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: successful count(%d), timeout(%d)\n",
+ ioc->name, __func__, count, timeout));
+ return 0;
+ }
+ if (sleep_flag == CAN_SLEEP)
+ usleep_range(1000, 1500);
+ else
+ udelay(500);
+ count++;
+ } while (--cntdn);
+
+ pr_err(MPT3SAS_FMT
+ "%s: failed due to timeout count(%d), doorbell_reg(%x)!\n",
+ ioc->name, __func__, count, doorbell_reg);
+ return -EFAULT;
+}
+
+/**
+ * _base_send_ioc_reset - send doorbell reset
+ * @ioc: per adapter object
+ * @reset_type: currently only supports: MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET
+ * @timeout: timeout in second
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_send_ioc_reset(struct MPT3SAS_ADAPTER *ioc, u8 reset_type, int timeout,
+ int sleep_flag)
+{
+ u32 ioc_state;
+ int r = 0;
+
+ if (reset_type != MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET) {
+ pr_err(MPT3SAS_FMT "%s: unknown reset_type\n",
+ ioc->name, __func__);
+ return -EFAULT;
+ }
+
+ if (!(ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY))
+ return -EFAULT;
+
+ pr_info(MPT3SAS_FMT "sending message unit reset !!\n", ioc->name);
+
+ writel(reset_type << MPI2_DOORBELL_FUNCTION_SHIFT,
+ &ioc->chip->Doorbell);
+ if ((_base_wait_for_doorbell_ack(ioc, 15, sleep_flag))) {
+ r = -EFAULT;
+ goto out;
+ }
+ ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY,
+ timeout, sleep_flag);
+ if (ioc_state) {
+ pr_err(MPT3SAS_FMT
+ "%s: failed going to ready state (ioc_state=0x%x)\n",
+ ioc->name, __func__, ioc_state);
+ r = -EFAULT;
+ goto out;
+ }
+ out:
+ pr_info(MPT3SAS_FMT "message unit reset: %s\n",
+ ioc->name, ((r == 0) ? "SUCCESS" : "FAILED"));
+ return r;
+}
+
+/**
+ * _base_handshake_req_reply_wait - send request thru doorbell interface
+ * @ioc: per adapter object
+ * @request_bytes: request length
+ * @request: pointer having request payload
+ * @reply_bytes: reply length
+ * @reply: pointer to reply payload
+ * @timeout: timeout in second
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes,
+ u32 *request, int reply_bytes, u16 *reply, int timeout, int sleep_flag)
+{
+ MPI2DefaultReply_t *default_reply = (MPI2DefaultReply_t *)reply;
+ int i;
+ u8 failed;
+ u16 dummy;
+ __le32 *mfp;
+
+ /* make sure doorbell is not in use */
+ if ((readl(&ioc->chip->Doorbell) & MPI2_DOORBELL_USED)) {
+ pr_err(MPT3SAS_FMT
+ "doorbell is in use (line=%d)\n",
+ ioc->name, __LINE__);
+ return -EFAULT;
+ }
+
+ /* clear pending doorbell interrupts from previous state changes */
+ if (readl(&ioc->chip->HostInterruptStatus) &
+ MPI2_HIS_IOC2SYS_DB_STATUS)
+ writel(0, &ioc->chip->HostInterruptStatus);
+
+ /* send message to ioc */
+ writel(((MPI2_FUNCTION_HANDSHAKE<<MPI2_DOORBELL_FUNCTION_SHIFT) |
+ ((request_bytes/4)<<MPI2_DOORBELL_ADD_DWORDS_SHIFT)),
+ &ioc->chip->Doorbell);
+
+ if ((_base_wait_for_doorbell_int(ioc, 5, NO_SLEEP))) {
+ pr_err(MPT3SAS_FMT
+ "doorbell handshake int failed (line=%d)\n",
+ ioc->name, __LINE__);
+ return -EFAULT;
+ }
+ writel(0, &ioc->chip->HostInterruptStatus);
+
+ if ((_base_wait_for_doorbell_ack(ioc, 5, sleep_flag))) {
+ pr_err(MPT3SAS_FMT
+ "doorbell handshake ack failed (line=%d)\n",
+ ioc->name, __LINE__);
+ return -EFAULT;
+ }
+
+ /* send message 32-bits at a time */
+ for (i = 0, failed = 0; i < request_bytes/4 && !failed; i++) {
+ writel(cpu_to_le32(request[i]), &ioc->chip->Doorbell);
+ if ((_base_wait_for_doorbell_ack(ioc, 5, sleep_flag)))
+ failed = 1;
+ }
+
+ if (failed) {
+ pr_err(MPT3SAS_FMT
+ "doorbell handshake sending request failed (line=%d)\n",
+ ioc->name, __LINE__);
+ return -EFAULT;
+ }
+
+ /* now wait for the reply */
+ if ((_base_wait_for_doorbell_int(ioc, timeout, sleep_flag))) {
+ pr_err(MPT3SAS_FMT
+ "doorbell handshake int failed (line=%d)\n",
+ ioc->name, __LINE__);
+ return -EFAULT;
+ }
+
+ /* read the first two 16-bits, it gives the total length of the reply */
+ reply[0] = le16_to_cpu(readl(&ioc->chip->Doorbell)
+ & MPI2_DOORBELL_DATA_MASK);
+ writel(0, &ioc->chip->HostInterruptStatus);
+ if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) {
+ pr_err(MPT3SAS_FMT
+ "doorbell handshake int failed (line=%d)\n",
+ ioc->name, __LINE__);
+ return -EFAULT;
+ }
+ reply[1] = le16_to_cpu(readl(&ioc->chip->Doorbell)
+ & MPI2_DOORBELL_DATA_MASK);
+ writel(0, &ioc->chip->HostInterruptStatus);
+
+ for (i = 2; i < default_reply->MsgLength * 2; i++) {
+ if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) {
+ pr_err(MPT3SAS_FMT
+ "doorbell handshake int failed (line=%d)\n",
+ ioc->name, __LINE__);
+ return -EFAULT;
+ }
+ if (i >= reply_bytes/2) /* overflow case */
+ dummy = readl(&ioc->chip->Doorbell);
+ else
+ reply[i] = le16_to_cpu(readl(&ioc->chip->Doorbell)
+ & MPI2_DOORBELL_DATA_MASK);
+ writel(0, &ioc->chip->HostInterruptStatus);
+ }
+
+ _base_wait_for_doorbell_int(ioc, 5, sleep_flag);
+ if (_base_wait_for_doorbell_not_used(ioc, 5, sleep_flag) != 0) {
+ dhsprintk(ioc, pr_info(MPT3SAS_FMT
+ "doorbell is in use (line=%d)\n", ioc->name, __LINE__));
+ }
+ writel(0, &ioc->chip->HostInterruptStatus);
+
+ if (ioc->logging_level & MPT_DEBUG_INIT) {
+ mfp = (__le32 *)reply;
+ pr_info("\toffset:data\n");
+ for (i = 0; i < reply_bytes/4; i++)
+ pr_info("\t[0x%02x]:%08x\n", i*4,
+ le32_to_cpu(mfp[i]));
+ }
+ return 0;
+}
+
+/**
+ * mpt3sas_base_sas_iounit_control - send sas iounit control to FW
+ * @ioc: per adapter object
+ * @mpi_reply: the reply payload from FW
+ * @mpi_request: the request payload sent to FW
+ *
+ * The SAS IO Unit Control Request message allows the host to perform low-level
+ * operations, such as resets on the PHYs of the IO Unit, also allows the host
+ * to obtain the IOC assigned device handles for a device if it has other
+ * identifying information about the device, in addition allows the host to
+ * remove IOC resources associated with the device.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2SasIoUnitControlReply_t *mpi_reply,
+ Mpi2SasIoUnitControlRequest_t *mpi_request)
+{
+ u16 smid;
+ u32 ioc_state;
+ unsigned long timeleft;
+ u8 issue_reset;
+ int rc;
+ void *request;
+ u16 wait_state_count;
+
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+ mutex_lock(&ioc->base_cmds.mutex);
+
+ if (ioc->base_cmds.status != MPT3_CMD_NOT_USED) {
+ pr_err(MPT3SAS_FMT "%s: base_cmd in use\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ wait_state_count = 0;
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ if (wait_state_count++ == 10) {
+ pr_err(MPT3SAS_FMT
+ "%s: failed due to ioc not operational\n",
+ ioc->name, __func__);
+ rc = -EFAULT;
+ goto out;
+ }
+ ssleep(1);
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ pr_info(MPT3SAS_FMT
+ "%s: waiting for operational state(count=%d)\n",
+ ioc->name, __func__, wait_state_count);
+ }
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->base_cb_idx);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ rc = 0;
+ ioc->base_cmds.status = MPT3_CMD_PENDING;
+ request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->base_cmds.smid = smid;
+ memcpy(request, mpi_request, sizeof(Mpi2SasIoUnitControlRequest_t));
+ if (mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET ||
+ mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET)
+ ioc->ioc_link_reset_in_progress = 1;
+ init_completion(&ioc->base_cmds.done);
+ mpt3sas_base_put_smid_default(ioc, smid);
+ timeleft = wait_for_completion_timeout(&ioc->base_cmds.done,
+ msecs_to_jiffies(10000));
+ if ((mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET ||
+ mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) &&
+ ioc->ioc_link_reset_in_progress)
+ ioc->ioc_link_reset_in_progress = 0;
+ if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) {
+ pr_err(MPT3SAS_FMT "%s: timeout\n",
+ ioc->name, __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2SasIoUnitControlRequest_t)/4);
+ if (!(ioc->base_cmds.status & MPT3_CMD_RESET))
+ issue_reset = 1;
+ goto issue_host_reset;
+ }
+ if (ioc->base_cmds.status & MPT3_CMD_REPLY_VALID)
+ memcpy(mpi_reply, ioc->base_cmds.reply,
+ sizeof(Mpi2SasIoUnitControlReply_t));
+ else
+ memset(mpi_reply, 0, sizeof(Mpi2SasIoUnitControlReply_t));
+ ioc->base_cmds.status = MPT3_CMD_NOT_USED;
+ goto out;
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ ioc->base_cmds.status = MPT3_CMD_NOT_USED;
+ rc = -EFAULT;
+ out:
+ mutex_unlock(&ioc->base_cmds.mutex);
+ return rc;
+}
+
+/**
+ * mpt3sas_base_scsi_enclosure_processor - sending request to sep device
+ * @ioc: per adapter object
+ * @mpi_reply: the reply payload from FW
+ * @mpi_request: the request payload sent to FW
+ *
+ * The SCSI Enclosure Processor request message causes the IOC to
+ * communicate with SES devices to control LED status signals.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request)
+{
+ u16 smid;
+ u32 ioc_state;
+ unsigned long timeleft;
+ u8 issue_reset;
+ int rc;
+ void *request;
+ u16 wait_state_count;
+
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+ mutex_lock(&ioc->base_cmds.mutex);
+
+ if (ioc->base_cmds.status != MPT3_CMD_NOT_USED) {
+ pr_err(MPT3SAS_FMT "%s: base_cmd in use\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ wait_state_count = 0;
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ if (wait_state_count++ == 10) {
+ pr_err(MPT3SAS_FMT
+ "%s: failed due to ioc not operational\n",
+ ioc->name, __func__);
+ rc = -EFAULT;
+ goto out;
+ }
+ ssleep(1);
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ pr_info(MPT3SAS_FMT
+ "%s: waiting for operational state(count=%d)\n",
+ ioc->name,
+ __func__, wait_state_count);
+ }
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->base_cb_idx);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ rc = 0;
+ ioc->base_cmds.status = MPT3_CMD_PENDING;
+ request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->base_cmds.smid = smid;
+ memcpy(request, mpi_request, sizeof(Mpi2SepReply_t));
+ init_completion(&ioc->base_cmds.done);
+ mpt3sas_base_put_smid_default(ioc, smid);
+ timeleft = wait_for_completion_timeout(&ioc->base_cmds.done,
+ msecs_to_jiffies(10000));
+ if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) {
+ pr_err(MPT3SAS_FMT "%s: timeout\n",
+ ioc->name, __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2SepRequest_t)/4);
+ if (!(ioc->base_cmds.status & MPT3_CMD_RESET))
+ issue_reset = 1;
+ goto issue_host_reset;
+ }
+ if (ioc->base_cmds.status & MPT3_CMD_REPLY_VALID)
+ memcpy(mpi_reply, ioc->base_cmds.reply,
+ sizeof(Mpi2SepReply_t));
+ else
+ memset(mpi_reply, 0, sizeof(Mpi2SepReply_t));
+ ioc->base_cmds.status = MPT3_CMD_NOT_USED;
+ goto out;
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ ioc->base_cmds.status = MPT3_CMD_NOT_USED;
+ rc = -EFAULT;
+ out:
+ mutex_unlock(&ioc->base_cmds.mutex);
+ return rc;
+}
+
+/**
+ * _base_get_port_facts - obtain port facts reply and save in ioc
+ * @ioc: per adapter object
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_get_port_facts(struct MPT3SAS_ADAPTER *ioc, int port, int sleep_flag)
+{
+ Mpi2PortFactsRequest_t mpi_request;
+ Mpi2PortFactsReply_t mpi_reply;
+ struct mpt3sas_port_facts *pfacts;
+ int mpi_reply_sz, mpi_request_sz, r;
+
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+ mpi_reply_sz = sizeof(Mpi2PortFactsReply_t);
+ mpi_request_sz = sizeof(Mpi2PortFactsRequest_t);
+ memset(&mpi_request, 0, mpi_request_sz);
+ mpi_request.Function = MPI2_FUNCTION_PORT_FACTS;
+ mpi_request.PortNumber = port;
+ r = _base_handshake_req_reply_wait(ioc, mpi_request_sz,
+ (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5, CAN_SLEEP);
+
+ if (r != 0) {
+ pr_err(MPT3SAS_FMT "%s: handshake failed (r=%d)\n",
+ ioc->name, __func__, r);
+ return r;
+ }
+
+ pfacts = &ioc->pfacts[port];
+ memset(pfacts, 0, sizeof(struct mpt3sas_port_facts));
+ pfacts->PortNumber = mpi_reply.PortNumber;
+ pfacts->VP_ID = mpi_reply.VP_ID;
+ pfacts->VF_ID = mpi_reply.VF_ID;
+ pfacts->MaxPostedCmdBuffers =
+ le16_to_cpu(mpi_reply.MaxPostedCmdBuffers);
+
+ return 0;
+}
+
+/**
+ * _base_get_ioc_facts - obtain ioc facts reply and save in ioc
+ * @ioc: per adapter object
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
+{
+ Mpi2IOCFactsRequest_t mpi_request;
+ Mpi2IOCFactsReply_t mpi_reply;
+ struct mpt3sas_facts *facts;
+ int mpi_reply_sz, mpi_request_sz, r;
+
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+ mpi_reply_sz = sizeof(Mpi2IOCFactsReply_t);
+ mpi_request_sz = sizeof(Mpi2IOCFactsRequest_t);
+ memset(&mpi_request, 0, mpi_request_sz);
+ mpi_request.Function = MPI2_FUNCTION_IOC_FACTS;
+ r = _base_handshake_req_reply_wait(ioc, mpi_request_sz,
+ (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5, CAN_SLEEP);
+
+ if (r != 0) {
+ pr_err(MPT3SAS_FMT "%s: handshake failed (r=%d)\n",
+ ioc->name, __func__, r);
+ return r;
+ }
+
+ facts = &ioc->facts;
+ memset(facts, 0, sizeof(struct mpt3sas_facts));
+ facts->MsgVersion = le16_to_cpu(mpi_reply.MsgVersion);
+ facts->HeaderVersion = le16_to_cpu(mpi_reply.HeaderVersion);
+ facts->VP_ID = mpi_reply.VP_ID;
+ facts->VF_ID = mpi_reply.VF_ID;
+ facts->IOCExceptions = le16_to_cpu(mpi_reply.IOCExceptions);
+ facts->MaxChainDepth = mpi_reply.MaxChainDepth;
+ facts->WhoInit = mpi_reply.WhoInit;
+ facts->NumberOfPorts = mpi_reply.NumberOfPorts;
+ facts->MaxMSIxVectors = mpi_reply.MaxMSIxVectors;
+ facts->RequestCredit = le16_to_cpu(mpi_reply.RequestCredit);
+ facts->MaxReplyDescriptorPostQueueDepth =
+ le16_to_cpu(mpi_reply.MaxReplyDescriptorPostQueueDepth);
+ facts->ProductID = le16_to_cpu(mpi_reply.ProductID);
+ facts->IOCCapabilities = le32_to_cpu(mpi_reply.IOCCapabilities);
+ if ((facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID))
+ ioc->ir_firmware = 1;
+ facts->FWVersion.Word = le32_to_cpu(mpi_reply.FWVersion.Word);
+ facts->IOCRequestFrameSize =
+ le16_to_cpu(mpi_reply.IOCRequestFrameSize);
+ facts->MaxInitiators = le16_to_cpu(mpi_reply.MaxInitiators);
+ facts->MaxTargets = le16_to_cpu(mpi_reply.MaxTargets);
+ ioc->shost->max_id = -1;
+ facts->MaxSasExpanders = le16_to_cpu(mpi_reply.MaxSasExpanders);
+ facts->MaxEnclosures = le16_to_cpu(mpi_reply.MaxEnclosures);
+ facts->ProtocolFlags = le16_to_cpu(mpi_reply.ProtocolFlags);
+ facts->HighPriorityCredit =
+ le16_to_cpu(mpi_reply.HighPriorityCredit);
+ facts->ReplyFrameSize = mpi_reply.ReplyFrameSize;
+ facts->MaxDevHandle = le16_to_cpu(mpi_reply.MaxDevHandle);
+
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT
+ "hba queue depth(%d), max chains per io(%d)\n",
+ ioc->name, facts->RequestCredit,
+ facts->MaxChainDepth));
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT
+ "request frame size(%d), reply frame size(%d)\n", ioc->name,
+ facts->IOCRequestFrameSize * 4, facts->ReplyFrameSize * 4));
+ return 0;
+}
+
+/**
+ * _base_send_ioc_init - send ioc_init to firmware
+ * @ioc: per adapter object
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
+{
+ Mpi2IOCInitRequest_t mpi_request;
+ Mpi2IOCInitReply_t mpi_reply;
+ int r;
+ struct timeval current_time;
+ u16 ioc_status;
+
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+ memset(&mpi_request, 0, sizeof(Mpi2IOCInitRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_IOC_INIT;
+ mpi_request.WhoInit = MPI2_WHOINIT_HOST_DRIVER;
+ mpi_request.VF_ID = 0; /* TODO */
+ mpi_request.VP_ID = 0;
+ mpi_request.MsgVersion = cpu_to_le16(MPI2_VERSION);
+ mpi_request.HeaderVersion = cpu_to_le16(MPI2_HEADER_VERSION);
+
+ if (_base_is_controller_msix_enabled(ioc))
+ mpi_request.HostMSIxVectors = ioc->reply_queue_count;
+ mpi_request.SystemRequestFrameSize = cpu_to_le16(ioc->request_sz/4);
+ mpi_request.ReplyDescriptorPostQueueDepth =
+ cpu_to_le16(ioc->reply_post_queue_depth);
+ mpi_request.ReplyFreeQueueDepth =
+ cpu_to_le16(ioc->reply_free_queue_depth);
+
+ mpi_request.SenseBufferAddressHigh =
+ cpu_to_le32((u64)ioc->sense_dma >> 32);
+ mpi_request.SystemReplyAddressHigh =
+ cpu_to_le32((u64)ioc->reply_dma >> 32);
+ mpi_request.SystemRequestFrameBaseAddress =
+ cpu_to_le64((u64)ioc->request_dma);
+ mpi_request.ReplyFreeQueueAddress =
+ cpu_to_le64((u64)ioc->reply_free_dma);
+ mpi_request.ReplyDescriptorPostQueueAddress =
+ cpu_to_le64((u64)ioc->reply_post_free_dma);
+
+
+ /* This time stamp specifies number of milliseconds
+ * since epoch ~ midnight January 1, 1970.
+ */
+ do_gettimeofday(&current_time);
+ mpi_request.TimeStamp = cpu_to_le64((u64)current_time.tv_sec * 1000 +
+ (current_time.tv_usec / 1000));
+
+ if (ioc->logging_level & MPT_DEBUG_INIT) {
+ __le32 *mfp;
+ int i;
+
+ mfp = (__le32 *)&mpi_request;
+ pr_info("\toffset:data\n");
+ for (i = 0; i < sizeof(Mpi2IOCInitRequest_t)/4; i++)
+ pr_info("\t[0x%02x]:%08x\n", i*4,
+ le32_to_cpu(mfp[i]));
+ }
+
+ r = _base_handshake_req_reply_wait(ioc,
+ sizeof(Mpi2IOCInitRequest_t), (u32 *)&mpi_request,
+ sizeof(Mpi2IOCInitReply_t), (u16 *)&mpi_reply, 10,
+ sleep_flag);
+
+ if (r != 0) {
+ pr_err(MPT3SAS_FMT "%s: handshake failed (r=%d)\n",
+ ioc->name, __func__, r);
+ return r;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS ||
+ mpi_reply.IOCLogInfo) {
+ pr_err(MPT3SAS_FMT "%s: failed\n", ioc->name, __func__);
+ r = -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * mpt3sas_port_enable_done - command completion routine for port enable
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+u8
+mpt3sas_port_enable_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+ u16 ioc_status;
+
+ if (ioc->port_enable_cmds.status == MPT3_CMD_NOT_USED)
+ return 1;
+
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ if (!mpi_reply)
+ return 1;
+
+ if (mpi_reply->Function != MPI2_FUNCTION_PORT_ENABLE)
+ return 1;
+
+ ioc->port_enable_cmds.status &= ~MPT3_CMD_PENDING;
+ ioc->port_enable_cmds.status |= MPT3_CMD_COMPLETE;
+ ioc->port_enable_cmds.status |= MPT3_CMD_REPLY_VALID;
+ memcpy(ioc->port_enable_cmds.reply, mpi_reply, mpi_reply->MsgLength*4);
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ ioc->port_enable_failed = 1;
+
+ if (ioc->is_driver_loading) {
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
+ mpt3sas_port_enable_complete(ioc);
+ return 1;
+ } else {
+ ioc->start_scan_failed = ioc_status;
+ ioc->start_scan = 0;
+ return 1;
+ }
+ }
+ complete(&ioc->port_enable_cmds.done);
+ return 1;
+}
+
+/**
+ * _base_send_port_enable - send port_enable(discovery stuff) to firmware
+ * @ioc: per adapter object
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_send_port_enable(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
+{
+ Mpi2PortEnableRequest_t *mpi_request;
+ Mpi2PortEnableReply_t *mpi_reply;
+ unsigned long timeleft;
+ int r = 0;
+ u16 smid;
+ u16 ioc_status;
+
+ pr_info(MPT3SAS_FMT "sending port enable !!\n", ioc->name);
+
+ if (ioc->port_enable_cmds.status & MPT3_CMD_PENDING) {
+ pr_err(MPT3SAS_FMT "%s: internal command already in use\n",
+ ioc->name, __func__);
+ return -EAGAIN;
+ }
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->port_enable_cb_idx);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ return -EAGAIN;
+ }
+
+ ioc->port_enable_cmds.status = MPT3_CMD_PENDING;
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->port_enable_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE;
+
+ init_completion(&ioc->port_enable_cmds.done);
+ mpt3sas_base_put_smid_default(ioc, smid);
+ timeleft = wait_for_completion_timeout(&ioc->port_enable_cmds.done,
+ 300*HZ);
+ if (!(ioc->port_enable_cmds.status & MPT3_CMD_COMPLETE)) {
+ pr_err(MPT3SAS_FMT "%s: timeout\n",
+ ioc->name, __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2PortEnableRequest_t)/4);
+ if (ioc->port_enable_cmds.status & MPT3_CMD_RESET)
+ r = -EFAULT;
+ else
+ r = -ETIME;
+ goto out;
+ }
+
+ mpi_reply = ioc->port_enable_cmds.reply;
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_err(MPT3SAS_FMT "%s: failed with (ioc_status=0x%08x)\n",
+ ioc->name, __func__, ioc_status);
+ r = -EFAULT;
+ goto out;
+ }
+
+ out:
+ ioc->port_enable_cmds.status = MPT3_CMD_NOT_USED;
+ pr_info(MPT3SAS_FMT "port enable: %s\n", ioc->name, ((r == 0) ?
+ "SUCCESS" : "FAILED"));
+ return r;
+}
+
+/**
+ * mpt3sas_port_enable - initiate firmware discovery (don't wait for reply)
+ * @ioc: per adapter object
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_port_enable(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2PortEnableRequest_t *mpi_request;
+ u16 smid;
+
+ pr_info(MPT3SAS_FMT "sending port enable !!\n", ioc->name);
+
+ if (ioc->port_enable_cmds.status & MPT3_CMD_PENDING) {
+ pr_err(MPT3SAS_FMT "%s: internal command already in use\n",
+ ioc->name, __func__);
+ return -EAGAIN;
+ }
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->port_enable_cb_idx);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ return -EAGAIN;
+ }
+
+ ioc->port_enable_cmds.status = MPT3_CMD_PENDING;
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->port_enable_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE;
+
+ mpt3sas_base_put_smid_default(ioc, smid);
+ return 0;
+}
+
+/**
+ * _base_determine_wait_on_discovery - desposition
+ * @ioc: per adapter object
+ *
+ * Decide whether to wait on discovery to complete. Used to either
+ * locate boot device, or report volumes ahead of physical devices.
+ *
+ * Returns 1 for wait, 0 for don't wait
+ */
+static int
+_base_determine_wait_on_discovery(struct MPT3SAS_ADAPTER *ioc)
+{
+ /* We wait for discovery to complete if IR firmware is loaded.
+ * The sas topology events arrive before PD events, so we need time to
+ * turn on the bit in ioc->pd_handles to indicate PD
+ * Also, it maybe required to report Volumes ahead of physical
+ * devices when MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING is set.
+ */
+ if (ioc->ir_firmware)
+ return 1;
+
+ /* if no Bios, then we don't need to wait */
+ if (!ioc->bios_pg3.BiosVersion)
+ return 0;
+
+ /* Bios is present, then we drop down here.
+ *
+ * If there any entries in the Bios Page 2, then we wait
+ * for discovery to complete.
+ */
+
+ /* Current Boot Device */
+ if ((ioc->bios_pg2.CurrentBootDeviceForm &
+ MPI2_BIOSPAGE2_FORM_MASK) ==
+ MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED &&
+ /* Request Boot Device */
+ (ioc->bios_pg2.ReqBootDeviceForm &
+ MPI2_BIOSPAGE2_FORM_MASK) ==
+ MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED &&
+ /* Alternate Request Boot Device */
+ (ioc->bios_pg2.ReqAltBootDeviceForm &
+ MPI2_BIOSPAGE2_FORM_MASK) ==
+ MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED)
+ return 0;
+
+ return 1;
+}
+
+/**
+ * _base_unmask_events - turn on notification for this event
+ * @ioc: per adapter object
+ * @event: firmware event
+ *
+ * The mask is stored in ioc->event_masks.
+ */
+static void
+_base_unmask_events(struct MPT3SAS_ADAPTER *ioc, u16 event)
+{
+ u32 desired_event;
+
+ if (event >= 128)
+ return;
+
+ desired_event = (1 << (event % 32));
+
+ if (event < 32)
+ ioc->event_masks[0] &= ~desired_event;
+ else if (event < 64)
+ ioc->event_masks[1] &= ~desired_event;
+ else if (event < 96)
+ ioc->event_masks[2] &= ~desired_event;
+ else if (event < 128)
+ ioc->event_masks[3] &= ~desired_event;
+}
+
+/**
+ * _base_event_notification - send event notification
+ * @ioc: per adapter object
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_event_notification(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
+{
+ Mpi2EventNotificationRequest_t *mpi_request;
+ unsigned long timeleft;
+ u16 smid;
+ int r = 0;
+ int i;
+
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+ if (ioc->base_cmds.status & MPT3_CMD_PENDING) {
+ pr_err(MPT3SAS_FMT "%s: internal command already in use\n",
+ ioc->name, __func__);
+ return -EAGAIN;
+ }
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->base_cb_idx);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ return -EAGAIN;
+ }
+ ioc->base_cmds.status = MPT3_CMD_PENDING;
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->base_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi2EventNotificationRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_EVENT_NOTIFICATION;
+ mpi_request->VF_ID = 0; /* TODO */
+ mpi_request->VP_ID = 0;
+ for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
+ mpi_request->EventMasks[i] =
+ cpu_to_le32(ioc->event_masks[i]);
+ init_completion(&ioc->base_cmds.done);
+ mpt3sas_base_put_smid_default(ioc, smid);
+ timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ);
+ if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) {
+ pr_err(MPT3SAS_FMT "%s: timeout\n",
+ ioc->name, __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2EventNotificationRequest_t)/4);
+ if (ioc->base_cmds.status & MPT3_CMD_RESET)
+ r = -EFAULT;
+ else
+ r = -ETIME;
+ } else
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s: complete\n",
+ ioc->name, __func__));
+ ioc->base_cmds.status = MPT3_CMD_NOT_USED;
+ return r;
+}
+
+/**
+ * mpt3sas_base_validate_event_type - validating event types
+ * @ioc: per adapter object
+ * @event: firmware event
+ *
+ * This will turn on firmware event notification when application
+ * ask for that event. We don't mask events that are already enabled.
+ */
+void
+mpt3sas_base_validate_event_type(struct MPT3SAS_ADAPTER *ioc, u32 *event_type)
+{
+ int i, j;
+ u32 event_mask, desired_event;
+ u8 send_update_to_fw;
+
+ for (i = 0, send_update_to_fw = 0; i <
+ MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) {
+ event_mask = ~event_type[i];
+ desired_event = 1;
+ for (j = 0; j < 32; j++) {
+ if (!(event_mask & desired_event) &&
+ (ioc->event_masks[i] & desired_event)) {
+ ioc->event_masks[i] &= ~desired_event;
+ send_update_to_fw = 1;
+ }
+ desired_event = (desired_event << 1);
+ }
+ }
+
+ if (!send_update_to_fw)
+ return;
+
+ mutex_lock(&ioc->base_cmds.mutex);
+ _base_event_notification(ioc, CAN_SLEEP);
+ mutex_unlock(&ioc->base_cmds.mutex);
+}
+
+/**
+ * _base_diag_reset - the "big hammer" start of day reset
+ * @ioc: per adapter object
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
+{
+ u32 host_diagnostic;
+ u32 ioc_state;
+ u32 count;
+ u32 hcb_size;
+
+ pr_info(MPT3SAS_FMT "sending diag reset !!\n", ioc->name);
+
+ drsprintk(ioc, pr_info(MPT3SAS_FMT "clear interrupts\n",
+ ioc->name));
+
+ count = 0;
+ do {
+ /* Write magic sequence to WriteSequence register
+ * Loop until in diagnostic mode
+ */
+ drsprintk(ioc, pr_info(MPT3SAS_FMT
+ "write magic sequence\n", ioc->name));
+ writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence);
+ writel(MPI2_WRSEQ_1ST_KEY_VALUE, &ioc->chip->WriteSequence);
+ writel(MPI2_WRSEQ_2ND_KEY_VALUE, &ioc->chip->WriteSequence);
+ writel(MPI2_WRSEQ_3RD_KEY_VALUE, &ioc->chip->WriteSequence);
+ writel(MPI2_WRSEQ_4TH_KEY_VALUE, &ioc->chip->WriteSequence);
+ writel(MPI2_WRSEQ_5TH_KEY_VALUE, &ioc->chip->WriteSequence);
+ writel(MPI2_WRSEQ_6TH_KEY_VALUE, &ioc->chip->WriteSequence);
+
+ /* wait 100 msec */
+ if (sleep_flag == CAN_SLEEP)
+ msleep(100);
+ else
+ mdelay(100);
+
+ if (count++ > 20)
+ goto out;
+
+ host_diagnostic = readl(&ioc->chip->HostDiagnostic);
+ drsprintk(ioc, pr_info(MPT3SAS_FMT
+ "wrote magic sequence: count(%d), host_diagnostic(0x%08x)\n",
+ ioc->name, count, host_diagnostic));
+
+ } while ((host_diagnostic & MPI2_DIAG_DIAG_WRITE_ENABLE) == 0);
+
+ hcb_size = readl(&ioc->chip->HCBSize);
+
+ drsprintk(ioc, pr_info(MPT3SAS_FMT "diag reset: issued\n",
+ ioc->name));
+ writel(host_diagnostic | MPI2_DIAG_RESET_ADAPTER,
+ &ioc->chip->HostDiagnostic);
+
+ /* don't access any registers for 50 milliseconds */
+ msleep(50);
+
+ /* 300 second max wait */
+ for (count = 0; count < 3000000 ; count++) {
+
+ host_diagnostic = readl(&ioc->chip->HostDiagnostic);
+
+ if (host_diagnostic == 0xFFFFFFFF)
+ goto out;
+ if (!(host_diagnostic & MPI2_DIAG_RESET_ADAPTER))
+ break;
+
+ /* wait 1 msec */
+ if (sleep_flag == CAN_SLEEP)
+ usleep_range(1000, 1500);
+ else
+ mdelay(1);
+ }
+
+ if (host_diagnostic & MPI2_DIAG_HCB_MODE) {
+
+ drsprintk(ioc, pr_info(MPT3SAS_FMT
+ "restart the adapter assuming the HCB Address points to good F/W\n",
+ ioc->name));
+ host_diagnostic &= ~MPI2_DIAG_BOOT_DEVICE_SELECT_MASK;
+ host_diagnostic |= MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW;
+ writel(host_diagnostic, &ioc->chip->HostDiagnostic);
+
+ drsprintk(ioc, pr_info(MPT3SAS_FMT
+ "re-enable the HCDW\n", ioc->name));
+ writel(hcb_size | MPI2_HCB_SIZE_HCB_ENABLE,
+ &ioc->chip->HCBSize);
+ }
+
+ drsprintk(ioc, pr_info(MPT3SAS_FMT "restart the adapter\n",
+ ioc->name));
+ writel(host_diagnostic & ~MPI2_DIAG_HOLD_IOC_RESET,
+ &ioc->chip->HostDiagnostic);
+
+ drsprintk(ioc, pr_info(MPT3SAS_FMT
+ "disable writes to the diagnostic register\n", ioc->name));
+ writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence);
+
+ drsprintk(ioc, pr_info(MPT3SAS_FMT
+ "Wait for FW to go to the READY state\n", ioc->name));
+ ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, 20,
+ sleep_flag);
+ if (ioc_state) {
+ pr_err(MPT3SAS_FMT
+ "%s: failed going to ready state (ioc_state=0x%x)\n",
+ ioc->name, __func__, ioc_state);
+ goto out;
+ }
+
+ pr_info(MPT3SAS_FMT "diag reset: SUCCESS\n", ioc->name);
+ return 0;
+
+ out:
+ pr_err(MPT3SAS_FMT "diag reset: FAILED\n", ioc->name);
+ return -EFAULT;
+}
+
+/**
+ * _base_make_ioc_ready - put controller in READY state
+ * @ioc: per adapter object
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ * @type: FORCE_BIG_HAMMER or SOFT_RESET
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_make_ioc_ready(struct MPT3SAS_ADAPTER *ioc, int sleep_flag,
+ enum reset_type type)
+{
+ u32 ioc_state;
+ int rc;
+ int count;
+
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+ if (ioc->pci_error_recovery)
+ return 0;
+
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 0);
+ dhsprintk(ioc, pr_info(MPT3SAS_FMT "%s: ioc_state(0x%08x)\n",
+ ioc->name, __func__, ioc_state));
+
+ /* if in RESET state, it should move to READY state shortly */
+ count = 0;
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_RESET) {
+ while ((ioc_state & MPI2_IOC_STATE_MASK) !=
+ MPI2_IOC_STATE_READY) {
+ if (count++ == 10) {
+ pr_err(MPT3SAS_FMT
+ "%s: failed going to ready state (ioc_state=0x%x)\n",
+ ioc->name, __func__, ioc_state);
+ return -EFAULT;
+ }
+ if (sleep_flag == CAN_SLEEP)
+ ssleep(1);
+ else
+ mdelay(1000);
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 0);
+ }
+ }
+
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_READY)
+ return 0;
+
+ if (ioc_state & MPI2_DOORBELL_USED) {
+ dhsprintk(ioc, pr_info(MPT3SAS_FMT
+ "unexpected doorbell active!\n",
+ ioc->name));
+ goto issue_diag_reset;
+ }
+
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
+ mpt3sas_base_fault_info(ioc, ioc_state &
+ MPI2_DOORBELL_DATA_MASK);
+ goto issue_diag_reset;
+ }
+
+ if (type == FORCE_BIG_HAMMER)
+ goto issue_diag_reset;
+
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_OPERATIONAL)
+ if (!(_base_send_ioc_reset(ioc,
+ MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET, 15, CAN_SLEEP))) {
+ return 0;
+ }
+
+ issue_diag_reset:
+ rc = _base_diag_reset(ioc, CAN_SLEEP);
+ return rc;
+}
+
+/**
+ * _base_make_ioc_operational - put controller in OPERATIONAL state
+ * @ioc: per adapter object
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
+{
+ int r, i;
+ unsigned long flags;
+ u32 reply_address;
+ u16 smid;
+ struct _tr_list *delayed_tr, *delayed_tr_next;
+ struct adapter_reply_queue *reply_q;
+ long reply_post_free;
+ u32 reply_post_free_sz;
+
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+ /* clean the delayed target reset list */
+ list_for_each_entry_safe(delayed_tr, delayed_tr_next,
+ &ioc->delayed_tr_list, list) {
+ list_del(&delayed_tr->list);
+ kfree(delayed_tr);
+ }
+
+
+ list_for_each_entry_safe(delayed_tr, delayed_tr_next,
+ &ioc->delayed_tr_volume_list, list) {
+ list_del(&delayed_tr->list);
+ kfree(delayed_tr);
+ }
+
+ /* initialize the scsi lookup free list */
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ INIT_LIST_HEAD(&ioc->free_list);
+ smid = 1;
+ for (i = 0; i < ioc->scsiio_depth; i++, smid++) {
+ INIT_LIST_HEAD(&ioc->scsi_lookup[i].chain_list);
+ ioc->scsi_lookup[i].cb_idx = 0xFF;
+ ioc->scsi_lookup[i].smid = smid;
+ ioc->scsi_lookup[i].scmd = NULL;
+ list_add_tail(&ioc->scsi_lookup[i].tracker_list,
+ &ioc->free_list);
+ }
+
+ /* hi-priority queue */
+ INIT_LIST_HEAD(&ioc->hpr_free_list);
+ smid = ioc->hi_priority_smid;
+ for (i = 0; i < ioc->hi_priority_depth; i++, smid++) {
+ ioc->hpr_lookup[i].cb_idx = 0xFF;
+ ioc->hpr_lookup[i].smid = smid;
+ list_add_tail(&ioc->hpr_lookup[i].tracker_list,
+ &ioc->hpr_free_list);
+ }
+
+ /* internal queue */
+ INIT_LIST_HEAD(&ioc->internal_free_list);
+ smid = ioc->internal_smid;
+ for (i = 0; i < ioc->internal_depth; i++, smid++) {
+ ioc->internal_lookup[i].cb_idx = 0xFF;
+ ioc->internal_lookup[i].smid = smid;
+ list_add_tail(&ioc->internal_lookup[i].tracker_list,
+ &ioc->internal_free_list);
+ }
+
+ /* chain pool */
+ INIT_LIST_HEAD(&ioc->free_chain_list);
+ for (i = 0; i < ioc->chain_depth; i++)
+ list_add_tail(&ioc->chain_lookup[i].tracker_list,
+ &ioc->free_chain_list);
+
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+
+ /* initialize Reply Free Queue */
+ for (i = 0, reply_address = (u32)ioc->reply_dma ;
+ i < ioc->reply_free_queue_depth ; i++, reply_address +=
+ ioc->reply_sz)
+ ioc->reply_free[i] = cpu_to_le32(reply_address);
+
+ /* initialize reply queues */
+ if (ioc->is_driver_loading)
+ _base_assign_reply_queues(ioc);
+
+ /* initialize Reply Post Free Queue */
+ reply_post_free = (long)ioc->reply_post_free;
+ reply_post_free_sz = ioc->reply_post_queue_depth *
+ sizeof(Mpi2DefaultReplyDescriptor_t);
+ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) {
+ reply_q->reply_post_host_index = 0;
+ reply_q->reply_post_free = (Mpi2ReplyDescriptorsUnion_t *)
+ reply_post_free;
+ for (i = 0; i < ioc->reply_post_queue_depth; i++)
+ reply_q->reply_post_free[i].Words =
+ cpu_to_le64(ULLONG_MAX);
+ if (!_base_is_controller_msix_enabled(ioc))
+ goto skip_init_reply_post_free_queue;
+ reply_post_free += reply_post_free_sz;
+ }
+ skip_init_reply_post_free_queue:
+
+ r = _base_send_ioc_init(ioc, sleep_flag);
+ if (r)
+ return r;
+
+ /* initialize reply free host index */
+ ioc->reply_free_host_index = ioc->reply_free_queue_depth - 1;
+ writel(ioc->reply_free_host_index, &ioc->chip->ReplyFreeHostIndex);
+
+ /* initialize reply post host index */
+ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) {
+ writel(reply_q->msix_index << MPI2_RPHI_MSIX_INDEX_SHIFT,
+ &ioc->chip->ReplyPostHostIndex);
+ if (!_base_is_controller_msix_enabled(ioc))
+ goto skip_init_reply_post_host_index;
+ }
+
+ skip_init_reply_post_host_index:
+
+ _base_unmask_interrupts(ioc);
+ r = _base_event_notification(ioc, sleep_flag);
+ if (r)
+ return r;
+
+ if (sleep_flag == CAN_SLEEP)
+ _base_static_config_pages(ioc);
+
+
+ if (ioc->is_driver_loading) {
+ ioc->wait_for_discovery_to_complete =
+ _base_determine_wait_on_discovery(ioc);
+
+ return r; /* scan_start and scan_finished support */
+ }
+
+ r = _base_send_port_enable(ioc, sleep_flag);
+ if (r)
+ return r;
+
+ return r;
+}
+
+/**
+ * mpt3sas_base_free_resources - free resources controller resources
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct pci_dev *pdev = ioc->pdev;
+
+ dexitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+ _base_mask_interrupts(ioc);
+ ioc->shost_recovery = 1;
+ _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET);
+ ioc->shost_recovery = 0;
+ _base_free_irq(ioc);
+ _base_disable_msix(ioc);
+ if (ioc->chip_phys)
+ iounmap(ioc->chip);
+ ioc->chip_phys = 0;
+ pci_release_selected_regions(ioc->pdev, ioc->bars);
+ pci_disable_pcie_error_reporting(pdev);
+ pci_disable_device(pdev);
+ return;
+}
+
+/**
+ * mpt3sas_base_attach - attach controller instance
+ * @ioc: per adapter object
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc)
+{
+ int r, i;
+ int cpu_id, last_cpu_id = 0;
+
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+ /* setup cpu_msix_table */
+ ioc->cpu_count = num_online_cpus();
+ for_each_online_cpu(cpu_id)
+ last_cpu_id = cpu_id;
+ ioc->cpu_msix_table_sz = last_cpu_id + 1;
+ ioc->cpu_msix_table = kzalloc(ioc->cpu_msix_table_sz, GFP_KERNEL);
+ ioc->reply_queue_count = 1;
+ if (!ioc->cpu_msix_table) {
+ dfailprintk(ioc, pr_info(MPT3SAS_FMT
+ "allocation for cpu_msix_table failed!!!\n",
+ ioc->name));
+ r = -ENOMEM;
+ goto out_free_resources;
+ }
+
+ r = mpt3sas_base_map_resources(ioc);
+ if (r)
+ goto out_free_resources;
+
+
+ pci_set_drvdata(ioc->pdev, ioc->shost);
+ r = _base_get_ioc_facts(ioc, CAN_SLEEP);
+ if (r)
+ goto out_free_resources;
+
+ /*
+ * In SAS3.0,
+ * SCSI_IO, SMP_PASSTHRU, SATA_PASSTHRU, Target Assist, and
+ * Target Status - all require the IEEE formated scatter gather
+ * elements.
+ */
+
+ ioc->build_sg_scmd = &_base_build_sg_scmd_ieee;
+ ioc->build_sg = &_base_build_sg_ieee;
+ ioc->build_zero_len_sge = &_base_build_zero_len_sge_ieee;
+ ioc->mpi25 = 1;
+ ioc->sge_size_ieee = sizeof(Mpi2IeeeSgeSimple64_t);
+
+ /*
+ * These function pointers for other requests that don't
+ * the require IEEE scatter gather elements.
+ *
+ * For example Configuration Pages and SAS IOUNIT Control don't.
+ */
+ ioc->build_sg_mpi = &_base_build_sg;
+ ioc->build_zero_len_sge_mpi = &_base_build_zero_len_sge;
+
+ r = _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET);
+ if (r)
+ goto out_free_resources;
+
+ ioc->pfacts = kcalloc(ioc->facts.NumberOfPorts,
+ sizeof(struct mpt3sas_port_facts), GFP_KERNEL);
+ if (!ioc->pfacts) {
+ r = -ENOMEM;
+ goto out_free_resources;
+ }
+
+ for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) {
+ r = _base_get_port_facts(ioc, i, CAN_SLEEP);
+ if (r)
+ goto out_free_resources;
+ }
+
+ r = _base_allocate_memory_pools(ioc, CAN_SLEEP);
+ if (r)
+ goto out_free_resources;
+
+ init_waitqueue_head(&ioc->reset_wq);
+
+ /* allocate memory pd handle bitmask list */
+ ioc->pd_handles_sz = (ioc->facts.MaxDevHandle / 8);
+ if (ioc->facts.MaxDevHandle % 8)
+ ioc->pd_handles_sz++;
+ ioc->pd_handles = kzalloc(ioc->pd_handles_sz,
+ GFP_KERNEL);
+ if (!ioc->pd_handles) {
+ r = -ENOMEM;
+ goto out_free_resources;
+ }
+ ioc->blocking_handles = kzalloc(ioc->pd_handles_sz,
+ GFP_KERNEL);
+ if (!ioc->blocking_handles) {
+ r = -ENOMEM;
+ goto out_free_resources;
+ }
+
+ ioc->fwfault_debug = mpt3sas_fwfault_debug;
+
+ /* base internal command bits */
+ mutex_init(&ioc->base_cmds.mutex);
+ ioc->base_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+ ioc->base_cmds.status = MPT3_CMD_NOT_USED;
+
+ /* port_enable command bits */
+ ioc->port_enable_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+ ioc->port_enable_cmds.status = MPT3_CMD_NOT_USED;
+
+ /* transport internal command bits */
+ ioc->transport_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+ ioc->transport_cmds.status = MPT3_CMD_NOT_USED;
+ mutex_init(&ioc->transport_cmds.mutex);
+
+ /* scsih internal command bits */
+ ioc->scsih_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED;
+ mutex_init(&ioc->scsih_cmds.mutex);
+
+ /* task management internal command bits */
+ ioc->tm_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+ ioc->tm_cmds.status = MPT3_CMD_NOT_USED;
+ mutex_init(&ioc->tm_cmds.mutex);
+
+ /* config page internal command bits */
+ ioc->config_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+ ioc->config_cmds.status = MPT3_CMD_NOT_USED;
+ mutex_init(&ioc->config_cmds.mutex);
+
+ /* ctl module internal command bits */
+ ioc->ctl_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
+ ioc->ctl_cmds.sense = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL);
+ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED;
+ mutex_init(&ioc->ctl_cmds.mutex);
+
+ if (!ioc->base_cmds.reply || !ioc->transport_cmds.reply ||
+ !ioc->scsih_cmds.reply || !ioc->tm_cmds.reply ||
+ !ioc->config_cmds.reply || !ioc->ctl_cmds.reply ||
+ !ioc->ctl_cmds.sense) {
+ r = -ENOMEM;
+ goto out_free_resources;
+ }
+
+ for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
+ ioc->event_masks[i] = -1;
+
+ /* here we enable the events we care about */
+ _base_unmask_events(ioc, MPI2_EVENT_SAS_DISCOVERY);
+ _base_unmask_events(ioc, MPI2_EVENT_SAS_BROADCAST_PRIMITIVE);
+ _base_unmask_events(ioc, MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST);
+ _base_unmask_events(ioc, MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE);
+ _base_unmask_events(ioc, MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE);
+ _base_unmask_events(ioc, MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST);
+ _base_unmask_events(ioc, MPI2_EVENT_IR_VOLUME);
+ _base_unmask_events(ioc, MPI2_EVENT_IR_PHYSICAL_DISK);
+ _base_unmask_events(ioc, MPI2_EVENT_IR_OPERATION_STATUS);
+ _base_unmask_events(ioc, MPI2_EVENT_LOG_ENTRY_ADDED);
+
+ r = _base_make_ioc_operational(ioc, CAN_SLEEP);
+ if (r)
+ goto out_free_resources;
+
+ return 0;
+
+ out_free_resources:
+
+ ioc->remove_host = 1;
+
+ mpt3sas_base_free_resources(ioc);
+ _base_release_memory_pools(ioc);
+ pci_set_drvdata(ioc->pdev, NULL);
+ kfree(ioc->cpu_msix_table);
+ kfree(ioc->pd_handles);
+ kfree(ioc->blocking_handles);
+ kfree(ioc->tm_cmds.reply);
+ kfree(ioc->transport_cmds.reply);
+ kfree(ioc->scsih_cmds.reply);
+ kfree(ioc->config_cmds.reply);
+ kfree(ioc->base_cmds.reply);
+ kfree(ioc->port_enable_cmds.reply);
+ kfree(ioc->ctl_cmds.reply);
+ kfree(ioc->ctl_cmds.sense);
+ kfree(ioc->pfacts);
+ ioc->ctl_cmds.reply = NULL;
+ ioc->base_cmds.reply = NULL;
+ ioc->tm_cmds.reply = NULL;
+ ioc->scsih_cmds.reply = NULL;
+ ioc->transport_cmds.reply = NULL;
+ ioc->config_cmds.reply = NULL;
+ ioc->pfacts = NULL;
+ return r;
+}
+
+
+/**
+ * mpt3sas_base_detach - remove controller instance
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_base_detach(struct MPT3SAS_ADAPTER *ioc)
+{
+ dexitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+ mpt3sas_base_stop_watchdog(ioc);
+ mpt3sas_base_free_resources(ioc);
+ _base_release_memory_pools(ioc);
+ pci_set_drvdata(ioc->pdev, NULL);
+ kfree(ioc->cpu_msix_table);
+ kfree(ioc->pd_handles);
+ kfree(ioc->blocking_handles);
+ kfree(ioc->pfacts);
+ kfree(ioc->ctl_cmds.reply);
+ kfree(ioc->ctl_cmds.sense);
+ kfree(ioc->base_cmds.reply);
+ kfree(ioc->port_enable_cmds.reply);
+ kfree(ioc->tm_cmds.reply);
+ kfree(ioc->transport_cmds.reply);
+ kfree(ioc->scsih_cmds.reply);
+ kfree(ioc->config_cmds.reply);
+}
+
+/**
+ * _base_reset_handler - reset callback handler (for base)
+ * @ioc: per adapter object
+ * @reset_phase: phase
+ *
+ * The handler for doing any required cleanup or initialization.
+ *
+ * The reset phase can be MPT3_IOC_PRE_RESET, MPT3_IOC_AFTER_RESET,
+ * MPT3_IOC_DONE_RESET
+ *
+ * Return nothing.
+ */
+static void
+_base_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase)
+{
+ mpt3sas_scsih_reset_handler(ioc, reset_phase);
+ mpt3sas_ctl_reset_handler(ioc, reset_phase);
+ switch (reset_phase) {
+ case MPT3_IOC_PRE_RESET:
+ dtmprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: MPT3_IOC_PRE_RESET\n", ioc->name, __func__));
+ break;
+ case MPT3_IOC_AFTER_RESET:
+ dtmprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: MPT3_IOC_AFTER_RESET\n", ioc->name, __func__));
+ if (ioc->transport_cmds.status & MPT3_CMD_PENDING) {
+ ioc->transport_cmds.status |= MPT3_CMD_RESET;
+ mpt3sas_base_free_smid(ioc, ioc->transport_cmds.smid);
+ complete(&ioc->transport_cmds.done);
+ }
+ if (ioc->base_cmds.status & MPT3_CMD_PENDING) {
+ ioc->base_cmds.status |= MPT3_CMD_RESET;
+ mpt3sas_base_free_smid(ioc, ioc->base_cmds.smid);
+ complete(&ioc->base_cmds.done);
+ }
+ if (ioc->port_enable_cmds.status & MPT3_CMD_PENDING) {
+ ioc->port_enable_failed = 1;
+ ioc->port_enable_cmds.status |= MPT3_CMD_RESET;
+ mpt3sas_base_free_smid(ioc, ioc->port_enable_cmds.smid);
+ if (ioc->is_driver_loading) {
+ ioc->start_scan_failed =
+ MPI2_IOCSTATUS_INTERNAL_ERROR;
+ ioc->start_scan = 0;
+ ioc->port_enable_cmds.status =
+ MPT3_CMD_NOT_USED;
+ } else
+ complete(&ioc->port_enable_cmds.done);
+ }
+ if (ioc->config_cmds.status & MPT3_CMD_PENDING) {
+ ioc->config_cmds.status |= MPT3_CMD_RESET;
+ mpt3sas_base_free_smid(ioc, ioc->config_cmds.smid);
+ ioc->config_cmds.smid = USHRT_MAX;
+ complete(&ioc->config_cmds.done);
+ }
+ break;
+ case MPT3_IOC_DONE_RESET:
+ dtmprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: MPT3_IOC_DONE_RESET\n", ioc->name, __func__));
+ break;
+ }
+}
+
+/**
+ * _wait_for_commands_to_complete - reset controller
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * This function waiting(3s) for all pending commands to complete
+ * prior to putting controller in reset.
+ */
+static void
+_wait_for_commands_to_complete(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
+{
+ u32 ioc_state;
+ unsigned long flags;
+ u16 i;
+
+ ioc->pending_io_count = 0;
+ if (sleep_flag != CAN_SLEEP)
+ return;
+
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 0);
+ if ((ioc_state & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_OPERATIONAL)
+ return;
+
+ /* pending command count */
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ for (i = 0; i < ioc->scsiio_depth; i++)
+ if (ioc->scsi_lookup[i].cb_idx != 0xFF)
+ ioc->pending_io_count++;
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+
+ if (!ioc->pending_io_count)
+ return;
+
+ /* wait for pending commands to complete */
+ wait_event_timeout(ioc->reset_wq, ioc->pending_io_count == 0, 10 * HZ);
+}
+
+/**
+ * mpt3sas_base_hard_reset_handler - reset controller
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ * @type: FORCE_BIG_HAMMER or SOFT_RESET
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, int sleep_flag,
+ enum reset_type type)
+{
+ int r;
+ unsigned long flags;
+ u32 ioc_state;
+ u8 is_fault = 0, is_trigger = 0;
+
+ dtmprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name,
+ __func__));
+
+ if (ioc->pci_error_recovery) {
+ pr_err(MPT3SAS_FMT "%s: pci error recovery reset\n",
+ ioc->name, __func__);
+ r = 0;
+ goto out_unlocked;
+ }
+
+ if (mpt3sas_fwfault_debug)
+ mpt3sas_halt_firmware(ioc);
+
+ /* TODO - What we really should be doing is pulling
+ * out all the code associated with NO_SLEEP; its never used.
+ * That is legacy code from mpt fusion driver, ported over.
+ * I will leave this BUG_ON here for now till its been resolved.
+ */
+ BUG_ON(sleep_flag == NO_SLEEP);
+
+ /* wait for an active reset in progress to complete */
+ if (!mutex_trylock(&ioc->reset_in_progress_mutex)) {
+ do {
+ ssleep(1);
+ } while (ioc->shost_recovery == 1);
+ dtmprintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name,
+ __func__));
+ return ioc->ioc_reset_in_progress_status;
+ }
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ ioc->shost_recovery = 1;
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) &&
+ (!(ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_RELEASED))) {
+ is_trigger = 1;
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 0);
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT)
+ is_fault = 1;
+ }
+ _base_reset_handler(ioc, MPT3_IOC_PRE_RESET);
+ _wait_for_commands_to_complete(ioc, sleep_flag);
+ _base_mask_interrupts(ioc);
+ r = _base_make_ioc_ready(ioc, sleep_flag, type);
+ if (r)
+ goto out;
+ _base_reset_handler(ioc, MPT3_IOC_AFTER_RESET);
+
+ /* If this hard reset is called while port enable is active, then
+ * there is no reason to call make_ioc_operational
+ */
+ if (ioc->is_driver_loading && ioc->port_enable_failed) {
+ ioc->remove_host = 1;
+ r = -EFAULT;
+ goto out;
+ }
+ r = _base_get_ioc_facts(ioc, CAN_SLEEP);
+ if (r)
+ goto out;
+ r = _base_make_ioc_operational(ioc, sleep_flag);
+ if (!r)
+ _base_reset_handler(ioc, MPT3_IOC_DONE_RESET);
+
+ out:
+ dtmprintk(ioc, pr_info(MPT3SAS_FMT "%s: %s\n",
+ ioc->name, __func__, ((r == 0) ? "SUCCESS" : "FAILED")));
+
+ spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
+ ioc->ioc_reset_in_progress_status = r;
+ ioc->shost_recovery = 0;
+ spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+ ioc->ioc_reset_count++;
+ mutex_unlock(&ioc->reset_in_progress_mutex);
+
+ out_unlocked:
+ if ((r == 0) && is_trigger) {
+ if (is_fault)
+ mpt3sas_trigger_master(ioc, MASTER_TRIGGER_FW_FAULT);
+ else
+ mpt3sas_trigger_master(ioc,
+ MASTER_TRIGGER_ADAPTER_RESET);
+ }
+ dtmprintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name,
+ __func__));
+ return r;
+}
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h
new file mode 100644
index 00000000000..994656cbfac
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.h
@@ -0,0 +1,1139 @@
+/*
+ * This is the Fusion MPT base driver providing common API layer interface
+ * for access to MPT (Message Passing Technology) firmware.
+ *
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_base.h
+ * Copyright (C) 2012 LSI Corporation
+ * (mailto:DL-MPTFusionLinux@lsi.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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef MPT3SAS_BASE_H_INCLUDED
+#define MPT3SAS_BASE_H_INCLUDED
+
+#include "mpi/mpi2_type.h"
+#include "mpi/mpi2.h"
+#include "mpi/mpi2_ioc.h"
+#include "mpi/mpi2_cnfg.h"
+#include "mpi/mpi2_init.h"
+#include "mpi/mpi2_raid.h"
+#include "mpi/mpi2_tool.h"
+#include "mpi/mpi2_sas.h"
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_transport_sas.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_eh.h>
+
+#include "mpt3sas_debug.h"
+#include "mpt3sas_trigger_diag.h"
+
+/* driver versioning info */
+#define MPT3SAS_DRIVER_NAME "mpt3sas"
+#define MPT3SAS_AUTHOR "LSI Corporation <DL-MPTFusionLinux@lsi.com>"
+#define MPT3SAS_DESCRIPTION "LSI MPT Fusion SAS 3.0 Device Driver"
+#define MPT3SAS_DRIVER_VERSION "01.100.01.00"
+#define MPT3SAS_MAJOR_VERSION 1
+#define MPT3SAS_MINOR_VERSION 100
+#define MPT3SAS_BUILD_VERSION 1
+#define MPT3SAS_RELEASE_VERSION 00
+
+/*
+ * Set MPT3SAS_SG_DEPTH value based on user input.
+ */
+#define MPT3SAS_MAX_PHYS_SEGMENTS SCSI_MAX_SG_SEGMENTS
+#define MPT3SAS_MIN_PHYS_SEGMENTS 16
+#ifdef CONFIG_SCSI_MPT3SAS_MAX_SGE
+#define MPT3SAS_SG_DEPTH CONFIG_SCSI_MPT3SAS_MAX_SGE
+#else
+#define MPT3SAS_SG_DEPTH MPT3SAS_MAX_PHYS_SEGMENTS
+#endif
+
+
+/*
+ * Generic Defines
+ */
+#define MPT3SAS_SATA_QUEUE_DEPTH 32
+#define MPT3SAS_SAS_QUEUE_DEPTH 254
+#define MPT3SAS_RAID_QUEUE_DEPTH 128
+
+#define MPT_NAME_LENGTH 32 /* generic length of strings */
+#define MPT_STRING_LENGTH 64
+
+#define MPT_MAX_CALLBACKS 32
+
+
+#define CAN_SLEEP 1
+#define NO_SLEEP 0
+
+#define INTERNAL_CMDS_COUNT 10 /* reserved cmds */
+
+#define MPI3_HIM_MASK 0xFFFFFFFF /* mask every bit*/
+
+#define MPT3SAS_INVALID_DEVICE_HANDLE 0xFFFF
+
+/*
+ * reset phases
+ */
+#define MPT3_IOC_PRE_RESET 1 /* prior to host reset */
+#define MPT3_IOC_AFTER_RESET 2 /* just after host reset */
+#define MPT3_IOC_DONE_RESET 3 /* links re-initialized */
+
+/*
+ * logging format
+ */
+#define MPT3SAS_FMT "%s: "
+
+/*
+ * per target private data
+ */
+#define MPT_TARGET_FLAGS_RAID_COMPONENT 0x01
+#define MPT_TARGET_FLAGS_VOLUME 0x02
+#define MPT_TARGET_FLAGS_DELETED 0x04
+#define MPT_TARGET_FASTPATH_IO 0x08
+
+
+
+/*
+ * status bits for ioc->diag_buffer_status
+ */
+#define MPT3_DIAG_BUFFER_IS_REGISTERED (0x01)
+#define MPT3_DIAG_BUFFER_IS_RELEASED (0x02)
+#define MPT3_DIAG_BUFFER_IS_DIAG_RESET (0x04)
+
+
+/* OEM Identifiers */
+#define MFG10_OEM_ID_INVALID (0x00000000)
+#define MFG10_OEM_ID_DELL (0x00000001)
+#define MFG10_OEM_ID_FSC (0x00000002)
+#define MFG10_OEM_ID_SUN (0x00000003)
+#define MFG10_OEM_ID_IBM (0x00000004)
+
+/* GENERIC Flags 0*/
+#define MFG10_GF0_OCE_DISABLED (0x00000001)
+#define MFG10_GF0_R1E_DRIVE_COUNT (0x00000002)
+#define MFG10_GF0_R10_DISPLAY (0x00000004)
+#define MFG10_GF0_SSD_DATA_SCRUB_DISABLE (0x00000008)
+#define MFG10_GF0_SINGLE_DRIVE_R0 (0x00000010)
+
+/* OEM Specific Flags will come from OEM specific header files */
+struct Mpi2ManufacturingPage10_t {
+ MPI2_CONFIG_PAGE_HEADER Header; /* 00h */
+ U8 OEMIdentifier; /* 04h */
+ U8 Reserved1; /* 05h */
+ U16 Reserved2; /* 08h */
+ U32 Reserved3; /* 0Ch */
+ U32 GenericFlags0; /* 10h */
+ U32 GenericFlags1; /* 14h */
+ U32 Reserved4; /* 18h */
+ U32 OEMSpecificFlags0; /* 1Ch */
+ U32 OEMSpecificFlags1; /* 20h */
+ U32 Reserved5[18]; /* 24h - 60h*/
+};
+
+
+/* Miscellaneous options */
+struct Mpi2ManufacturingPage11_t {
+ MPI2_CONFIG_PAGE_HEADER Header; /* 00h */
+ __le32 Reserved1; /* 04h */
+ u8 Reserved2; /* 08h */
+ u8 EEDPTagMode; /* 09h */
+ u8 Reserved3; /* 0Ah */
+ u8 Reserved4; /* 0Bh */
+ __le32 Reserved5[23]; /* 0Ch-60h*/
+};
+
+/**
+ * struct MPT3SAS_TARGET - starget private hostdata
+ * @starget: starget object
+ * @sas_address: target sas address
+ * @handle: device handle
+ * @num_luns: number luns
+ * @flags: MPT_TARGET_FLAGS_XXX flags
+ * @deleted: target flaged for deletion
+ * @tm_busy: target is busy with TM request.
+ */
+struct MPT3SAS_TARGET {
+ struct scsi_target *starget;
+ u64 sas_address;
+ u16 handle;
+ int num_luns;
+ u32 flags;
+ u8 deleted;
+ u8 tm_busy;
+};
+
+
+/*
+ * per device private data
+ */
+#define MPT_DEVICE_FLAGS_INIT 0x01
+#define MPT_DEVICE_TLR_ON 0x02
+
+/**
+ * struct MPT3SAS_DEVICE - sdev private hostdata
+ * @sas_target: starget private hostdata
+ * @lun: lun number
+ * @flags: MPT_DEVICE_XXX flags
+ * @configured_lun: lun is configured
+ * @block: device is in SDEV_BLOCK state
+ * @tlr_snoop_check: flag used in determining whether to disable TLR
+ * @eedp_enable: eedp support enable bit
+ * @eedp_type: 0(type_1), 1(type_2), 2(type_3)
+ * @eedp_block_length: block size
+ */
+struct MPT3SAS_DEVICE {
+ struct MPT3SAS_TARGET *sas_target;
+ unsigned int lun;
+ u32 flags;
+ u8 configured_lun;
+ u8 block;
+ u8 tlr_snoop_check;
+};
+
+#define MPT3_CMD_NOT_USED 0x8000 /* free */
+#define MPT3_CMD_COMPLETE 0x0001 /* completed */
+#define MPT3_CMD_PENDING 0x0002 /* pending */
+#define MPT3_CMD_REPLY_VALID 0x0004 /* reply is valid */
+#define MPT3_CMD_RESET 0x0008 /* host reset dropped the command */
+
+/**
+ * struct _internal_cmd - internal commands struct
+ * @mutex: mutex
+ * @done: completion
+ * @reply: reply message pointer
+ * @sense: sense data
+ * @status: MPT3_CMD_XXX status
+ * @smid: system message id
+ */
+struct _internal_cmd {
+ struct mutex mutex;
+ struct completion done;
+ void *reply;
+ void *sense;
+ u16 status;
+ u16 smid;
+};
+
+
+
+/**
+ * struct _sas_device - attached device information
+ * @list: sas device list
+ * @starget: starget object
+ * @sas_address: device sas address
+ * @device_name: retrieved from the SAS IDENTIFY frame.
+ * @handle: device handle
+ * @sas_address_parent: sas address of parent expander or sas host
+ * @enclosure_handle: enclosure handle
+ * @enclosure_logical_id: enclosure logical identifier
+ * @volume_handle: volume handle (valid when hidden raid member)
+ * @volume_wwid: volume unique identifier
+ * @device_info: bitfield provides detailed info about the device
+ * @id: target id
+ * @channel: target channel
+ * @slot: number number
+ * @phy: phy identifier provided in sas device page 0
+ * @fast_path: fast path feature enable bit
+ * @responding: used in _scsih_sas_device_mark_responding
+ */
+struct _sas_device {
+ struct list_head list;
+ struct scsi_target *starget;
+ u64 sas_address;
+ u64 device_name;
+ u16 handle;
+ u64 sas_address_parent;
+ u16 enclosure_handle;
+ u64 enclosure_logical_id;
+ u16 volume_handle;
+ u64 volume_wwid;
+ u32 device_info;
+ int id;
+ int channel;
+ u16 slot;
+ u8 phy;
+ u8 responding;
+ u8 fast_path;
+};
+
+/**
+ * struct _raid_device - raid volume link list
+ * @list: sas device list
+ * @starget: starget object
+ * @sdev: scsi device struct (volumes are single lun)
+ * @wwid: unique identifier for the volume
+ * @handle: device handle
+ * @id: target id
+ * @channel: target channel
+ * @volume_type: the raid level
+ * @device_info: bitfield provides detailed info about the hidden components
+ * @num_pds: number of hidden raid components
+ * @responding: used in _scsih_raid_device_mark_responding
+ * @percent_complete: resync percent complete
+ */
+#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;
+ int id;
+ int channel;
+ u8 volume_type;
+ u8 num_pds;
+ u8 responding;
+ u8 percent_complete;
+ u32 device_info;
+};
+
+/**
+ * struct _boot_device - boot device info
+ * @is_raid: flag to indicate whether this is volume
+ * @device: holds pointer for either struct _sas_device or
+ * struct _raid_device
+ */
+struct _boot_device {
+ u8 is_raid;
+ void *device;
+};
+
+/**
+ * struct _sas_port - wide/narrow sas port information
+ * @port_list: list of ports belonging to expander
+ * @num_phys: number of phys belonging to this port
+ * @remote_identify: attached device identification
+ * @rphy: sas transport rphy object
+ * @port: sas transport wide/narrow port object
+ * @phy_list: _sas_phy list objects belonging to this port
+ */
+struct _sas_port {
+ struct list_head port_list;
+ u8 num_phys;
+ struct sas_identify remote_identify;
+ struct sas_rphy *rphy;
+ struct sas_port *port;
+ struct list_head phy_list;
+};
+
+/**
+ * struct _sas_phy - phy information
+ * @port_siblings: list of phys belonging to a port
+ * @identify: phy identification
+ * @remote_identify: attached device identification
+ * @phy: sas transport phy object
+ * @phy_id: unique phy id
+ * @handle: device handle for this phy
+ * @attached_handle: device handle for attached device
+ * @phy_belongs_to_port: port has been created for this phy
+ */
+struct _sas_phy {
+ struct list_head port_siblings;
+ struct sas_identify identify;
+ struct sas_identify remote_identify;
+ struct sas_phy *phy;
+ u8 phy_id;
+ u16 handle;
+ u16 attached_handle;
+ u8 phy_belongs_to_port;
+};
+
+/**
+ * struct _sas_node - sas_host/expander information
+ * @list: list of expanders
+ * @parent_dev: parent device class
+ * @num_phys: number phys belonging to this sas_host/expander
+ * @sas_address: sas address of this sas_host/expander
+ * @handle: handle for this sas_host/expander
+ * @sas_address_parent: sas address of parent expander or sas host
+ * @enclosure_handle: handle for this a member of an enclosure
+ * @device_info: bitwise defining capabilities of this sas_host/expander
+ * @responding: used in _scsih_expander_device_mark_responding
+ * @phy: a list of phys that make up this sas_host/expander
+ * @sas_port_list: list of ports attached to this sas_host/expander
+ */
+struct _sas_node {
+ struct list_head list;
+ struct device *parent_dev;
+ u8 num_phys;
+ u64 sas_address;
+ u16 handle;
+ u64 sas_address_parent;
+ u16 enclosure_handle;
+ u64 enclosure_logical_id;
+ u8 responding;
+ struct _sas_phy *phy;
+ struct list_head sas_port_list;
+};
+
+/**
+ * enum reset_type - reset state
+ * @FORCE_BIG_HAMMER: issue diagnostic reset
+ * @SOFT_RESET: issue message_unit_reset, if fails to to big hammer
+ */
+enum reset_type {
+ FORCE_BIG_HAMMER,
+ SOFT_RESET,
+};
+
+/**
+ * struct chain_tracker - firmware chain tracker
+ * @chain_buffer: chain buffer
+ * @chain_buffer_dma: physical address
+ * @tracker_list: list of free request (ioc->free_chain_list)
+ */
+struct chain_tracker {
+ void *chain_buffer;
+ dma_addr_t chain_buffer_dma;
+ struct list_head tracker_list;
+};
+
+/**
+ * struct scsiio_tracker - scsi mf request tracker
+ * @smid: system message id
+ * @scmd: scsi request pointer
+ * @cb_idx: callback index
+ * @tracker_list: list of free request (ioc->free_list)
+ */
+struct scsiio_tracker {
+ u16 smid;
+ struct scsi_cmnd *scmd;
+ u8 cb_idx;
+ struct list_head chain_list;
+ struct list_head tracker_list;
+};
+
+/**
+ * struct request_tracker - firmware request tracker
+ * @smid: system message id
+ * @cb_idx: callback index
+ * @tracker_list: list of free request (ioc->free_list)
+ */
+struct request_tracker {
+ u16 smid;
+ u8 cb_idx;
+ struct list_head tracker_list;
+};
+
+/**
+ * struct _tr_list - target reset list
+ * @handle: device handle
+ * @state: state machine
+ */
+struct _tr_list {
+ struct list_head list;
+ u16 handle;
+ u16 state;
+};
+
+
+/**
+ * struct adapter_reply_queue - the reply queue struct
+ * @ioc: per adapter object
+ * @msix_index: msix index into vector table
+ * @vector: irq vector
+ * @reply_post_host_index: head index in the pool where FW completes IO
+ * @reply_post_free: reply post base virt address
+ * @name: the name registered to request_irq()
+ * @busy: isr is actively processing replies on another cpu
+ * @list: this list
+*/
+struct adapter_reply_queue {
+ struct MPT3SAS_ADAPTER *ioc;
+ u8 msix_index;
+ unsigned int vector;
+ u32 reply_post_host_index;
+ Mpi2ReplyDescriptorsUnion_t *reply_post_free;
+ char name[MPT_NAME_LENGTH];
+ atomic_t busy;
+ struct list_head list;
+};
+
+typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr);
+
+/* SAS3.0 support */
+typedef int (*MPT_BUILD_SG_SCMD)(struct MPT3SAS_ADAPTER *ioc,
+ struct scsi_cmnd *scmd, u16 smid);
+typedef void (*MPT_BUILD_SG)(struct MPT3SAS_ADAPTER *ioc, void *psge,
+ dma_addr_t data_out_dma, size_t data_out_sz,
+ dma_addr_t data_in_dma, size_t data_in_sz);
+typedef void (*MPT_BUILD_ZERO_LEN_SGE)(struct MPT3SAS_ADAPTER *ioc,
+ void *paddr);
+
+
+
+/* IOC Facts and Port Facts converted from little endian to cpu */
+union mpi3_version_union {
+ MPI2_VERSION_STRUCT Struct;
+ u32 Word;
+};
+
+struct mpt3sas_facts {
+ u16 MsgVersion;
+ u16 HeaderVersion;
+ u8 IOCNumber;
+ u8 VP_ID;
+ u8 VF_ID;
+ u16 IOCExceptions;
+ u16 IOCStatus;
+ u32 IOCLogInfo;
+ u8 MaxChainDepth;
+ u8 WhoInit;
+ u8 NumberOfPorts;
+ u8 MaxMSIxVectors;
+ u16 RequestCredit;
+ u16 ProductID;
+ u32 IOCCapabilities;
+ union mpi3_version_union FWVersion;
+ u16 IOCRequestFrameSize;
+ u16 Reserved3;
+ u16 MaxInitiators;
+ u16 MaxTargets;
+ u16 MaxSasExpanders;
+ u16 MaxEnclosures;
+ u16 ProtocolFlags;
+ u16 HighPriorityCredit;
+ u16 MaxReplyDescriptorPostQueueDepth;
+ u8 ReplyFrameSize;
+ u8 MaxVolumes;
+ u16 MaxDevHandle;
+ u16 MaxPersistentEntries;
+ u16 MinDevHandle;
+};
+
+struct mpt3sas_port_facts {
+ u8 PortNumber;
+ u8 VP_ID;
+ u8 VF_ID;
+ u8 PortType;
+ u16 MaxPostedCmdBuffers;
+};
+
+/**
+ * enum mutex_type - task management mutex type
+ * @TM_MUTEX_OFF: mutex is not required becuase calling function is acquiring it
+ * @TM_MUTEX_ON: mutex is required
+ */
+enum mutex_type {
+ TM_MUTEX_OFF = 0,
+ TM_MUTEX_ON = 1,
+};
+
+typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc);
+/**
+ * struct MPT3SAS_ADAPTER - per adapter struct
+ * @list: ioc_list
+ * @shost: shost object
+ * @id: unique adapter id
+ * @cpu_count: number online cpus
+ * @name: generic ioc string
+ * @tmp_string: tmp string used for logging
+ * @pdev: pci pdev object
+ * @pio_chip: physical io register space
+ * @chip: memory mapped register space
+ * @chip_phys: physical addrss prior to mapping
+ * @logging_level: see mpt3sas_debug.h
+ * @fwfault_debug: debuging FW timeouts
+ * @ir_firmware: IR firmware present
+ * @bars: bitmask of BAR's that must be configured
+ * @mask_interrupts: ignore interrupt
+ * @fault_reset_work_q_name: fw fault work queue
+ * @fault_reset_work_q: ""
+ * @fault_reset_work: ""
+ * @firmware_event_name: fw event work queue
+ * @firmware_event_thread: ""
+ * @fw_event_lock:
+ * @fw_event_list: list of fw events
+ * @aen_event_read_flag: event log was read
+ * @broadcast_aen_busy: broadcast aen waiting to be serviced
+ * @shost_recovery: host reset in progress
+ * @ioc_reset_in_progress_lock:
+ * @ioc_link_reset_in_progress: phy/hard reset in progress
+ * @ignore_loginfos: ignore loginfos during task management
+ * @remove_host: flag for when driver unloads, to avoid sending dev resets
+ * @pci_error_recovery: flag to prevent ioc access until slot reset completes
+ * @wait_for_discovery_to_complete: flag set at driver load time when
+ * waiting on reporting devices
+ * @is_driver_loading: flag set at driver load time
+ * @port_enable_failed: flag set when port enable has failed
+ * @start_scan: flag set from scan_start callback, cleared from _mpt3sas_fw_work
+ * @start_scan_failed: means port enable failed, return's the ioc_status
+ * @msix_enable: flag indicating msix is enabled
+ * @msix_vector_count: number msix vectors
+ * @cpu_msix_table: table for mapping cpus to msix index
+ * @cpu_msix_table_sz: table size
+ * @schedule_dead_ioc_flush_running_cmds: callback to flush pending commands
+ * @scsi_io_cb_idx: shost generated commands
+ * @tm_cb_idx: task management commands
+ * @scsih_cb_idx: scsih internal commands
+ * @transport_cb_idx: transport internal commands
+ * @ctl_cb_idx: clt internal commands
+ * @base_cb_idx: base internal commands
+ * @config_cb_idx: base internal commands
+ * @tm_tr_cb_idx : device removal target reset handshake
+ * @tm_tr_volume_cb_idx : volume removal target reset
+ * @base_cmds:
+ * @transport_cmds:
+ * @scsih_cmds:
+ * @tm_cmds:
+ * @ctl_cmds:
+ * @config_cmds:
+ * @base_add_sg_single: handler for either 32/64 bit sgl's
+ * @event_type: bits indicating which events to log
+ * @event_context: unique id for each logged event
+ * @event_log: event log pointer
+ * @event_masks: events that are masked
+ * @facts: static facts data
+ * @pfacts: static port facts data
+ * @manu_pg0: static manufacturing page 0
+ * @manu_pg10: static manufacturing page 10
+ * @manu_pg11: static manufacturing page 11
+ * @bios_pg2: static bios page 2
+ * @bios_pg3: static bios page 3
+ * @ioc_pg8: static ioc page 8
+ * @iounit_pg0: static iounit page 0
+ * @iounit_pg1: static iounit page 1
+ * @sas_hba: sas host object
+ * @sas_expander_list: expander object list
+ * @sas_node_lock:
+ * @sas_device_list: sas device object list
+ * @sas_device_init_list: sas device object list (used only at init time)
+ * @sas_device_lock:
+ * @io_missing_delay: time for IO completed by fw when PDR enabled
+ * @device_missing_delay: time for device missing by fw when PDR enabled
+ * @sas_id : used for setting volume target IDs
+ * @blocking_handles: bitmask used to identify which devices need blocking
+ * @pd_handles : bitmask for PD handles
+ * @pd_handles_sz : size of pd_handle bitmask
+ * @config_page_sz: config page size
+ * @config_page: reserve memory for config page payload
+ * @config_page_dma:
+ * @hba_queue_depth: hba request queue depth
+ * @sge_size: sg element size for either 32/64 bit
+ * @scsiio_depth: SCSI_IO queue depth
+ * @request_sz: per request frame size
+ * @request: pool of request frames
+ * @request_dma:
+ * @request_dma_sz:
+ * @scsi_lookup: firmware request tracker list
+ * @scsi_lookup_lock:
+ * @free_list: free list of request
+ * @pending_io_count:
+ * @reset_wq:
+ * @chain: pool of chains
+ * @chain_dma:
+ * @max_sges_in_main_message: number sg elements in main message
+ * @max_sges_in_chain_message: number sg elements per chain
+ * @chains_needed_per_io: max chains per io
+ * @chain_depth: total chains allocated
+ * @hi_priority_smid:
+ * @hi_priority:
+ * @hi_priority_dma:
+ * @hi_priority_depth:
+ * @hpr_lookup:
+ * @hpr_free_list:
+ * @internal_smid:
+ * @internal:
+ * @internal_dma:
+ * @internal_depth:
+ * @internal_lookup:
+ * @internal_free_list:
+ * @sense: pool of sense
+ * @sense_dma:
+ * @sense_dma_pool:
+ * @reply_depth: hba reply queue depth:
+ * @reply_sz: per reply frame size:
+ * @reply: pool of replys:
+ * @reply_dma:
+ * @reply_dma_pool:
+ * @reply_free_queue_depth: reply free depth
+ * @reply_free: pool for reply free queue (32 bit addr)
+ * @reply_free_dma:
+ * @reply_free_dma_pool:
+ * @reply_free_host_index: tail index in pool to insert free replys
+ * @reply_post_queue_depth: reply post queue depth
+ * @reply_post_free: pool for reply post (64bit descriptor)
+ * @reply_post_free_dma:
+ * @reply_queue_count: number of reply queue's
+ * @reply_queue_list: link list contaning the reply queue info
+ * @reply_post_host_index: head index in the pool where FW completes IO
+ * @delayed_tr_list: target reset link list
+ * @delayed_tr_volume_list: volume target reset link list
+ */
+struct MPT3SAS_ADAPTER {
+ struct list_head list;
+ struct Scsi_Host *shost;
+ u8 id;
+ int cpu_count;
+ char name[MPT_NAME_LENGTH];
+ char tmp_string[MPT_STRING_LENGTH];
+ struct pci_dev *pdev;
+ Mpi2SystemInterfaceRegs_t __iomem *chip;
+ resource_size_t chip_phys;
+ int logging_level;
+ int fwfault_debug;
+ u8 ir_firmware;
+ int bars;
+ u8 mask_interrupts;
+
+ /* fw fault handler */
+ char fault_reset_work_q_name[20];
+ struct workqueue_struct *fault_reset_work_q;
+ struct delayed_work fault_reset_work;
+
+ /* fw event handler */
+ char firmware_event_name[20];
+ struct workqueue_struct *firmware_event_thread;
+ spinlock_t fw_event_lock;
+ struct list_head fw_event_list;
+
+ /* misc flags */
+ int aen_event_read_flag;
+ u8 broadcast_aen_busy;
+ u16 broadcast_aen_pending;
+ u8 shost_recovery;
+
+ struct mutex reset_in_progress_mutex;
+ spinlock_t ioc_reset_in_progress_lock;
+ u8 ioc_link_reset_in_progress;
+ u8 ioc_reset_in_progress_status;
+
+ u8 ignore_loginfos;
+ u8 remove_host;
+ u8 pci_error_recovery;
+ u8 wait_for_discovery_to_complete;
+ u8 is_driver_loading;
+ u8 port_enable_failed;
+ u8 start_scan;
+ u16 start_scan_failed;
+
+ u8 msix_enable;
+ u16 msix_vector_count;
+ u8 *cpu_msix_table;
+ u16 cpu_msix_table_sz;
+ u32 ioc_reset_count;
+ MPT3SAS_FLUSH_RUNNING_CMDS schedule_dead_ioc_flush_running_cmds;
+
+ /* internal commands, callback index */
+ u8 scsi_io_cb_idx;
+ u8 tm_cb_idx;
+ u8 transport_cb_idx;
+ u8 scsih_cb_idx;
+ u8 ctl_cb_idx;
+ u8 base_cb_idx;
+ u8 port_enable_cb_idx;
+ u8 config_cb_idx;
+ u8 tm_tr_cb_idx;
+ u8 tm_tr_volume_cb_idx;
+ u8 tm_sas_control_cb_idx;
+ struct _internal_cmd base_cmds;
+ struct _internal_cmd port_enable_cmds;
+ struct _internal_cmd transport_cmds;
+ struct _internal_cmd scsih_cmds;
+ struct _internal_cmd tm_cmds;
+ struct _internal_cmd ctl_cmds;
+ struct _internal_cmd config_cmds;
+
+ MPT_ADD_SGE base_add_sg_single;
+
+ /* function ptr for either IEEE or MPI sg elements */
+ MPT_BUILD_SG_SCMD build_sg_scmd;
+ MPT_BUILD_SG build_sg;
+ MPT_BUILD_ZERO_LEN_SGE build_zero_len_sge;
+ u8 mpi25;
+ u16 sge_size_ieee;
+
+ /* function ptr for MPI sg elements only */
+ MPT_BUILD_SG build_sg_mpi;
+ MPT_BUILD_ZERO_LEN_SGE build_zero_len_sge_mpi;
+
+ /* event log */
+ u32 event_type[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS];
+ u32 event_context;
+ void *event_log;
+ u32 event_masks[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS];
+
+ /* static config pages */
+ struct mpt3sas_facts facts;
+ struct mpt3sas_port_facts *pfacts;
+ Mpi2ManufacturingPage0_t manu_pg0;
+ struct Mpi2ManufacturingPage10_t manu_pg10;
+ struct Mpi2ManufacturingPage11_t manu_pg11;
+ Mpi2BiosPage2_t bios_pg2;
+ Mpi2BiosPage3_t bios_pg3;
+ Mpi2IOCPage8_t ioc_pg8;
+ Mpi2IOUnitPage0_t iounit_pg0;
+ Mpi2IOUnitPage1_t iounit_pg1;
+
+ struct _boot_device req_boot_device;
+ struct _boot_device req_alt_boot_device;
+ struct _boot_device current_boot_device;
+
+ /* sas hba, expander, and device list */
+ struct _sas_node sas_hba;
+ struct list_head sas_expander_list;
+ spinlock_t sas_node_lock;
+ struct list_head sas_device_list;
+ struct list_head sas_device_init_list;
+ spinlock_t sas_device_lock;
+ struct list_head raid_device_list;
+ spinlock_t raid_device_lock;
+ u8 io_missing_delay;
+ u16 device_missing_delay;
+ int sas_id;
+
+ void *blocking_handles;
+ void *pd_handles;
+ u16 pd_handles_sz;
+
+ /* config page */
+ u16 config_page_sz;
+ void *config_page;
+ dma_addr_t config_page_dma;
+
+ /* scsiio request */
+ u16 hba_queue_depth;
+ u16 sge_size;
+ u16 scsiio_depth;
+ u16 request_sz;
+ u8 *request;
+ dma_addr_t request_dma;
+ u32 request_dma_sz;
+ struct scsiio_tracker *scsi_lookup;
+ ulong scsi_lookup_pages;
+ spinlock_t scsi_lookup_lock;
+ struct list_head free_list;
+ int pending_io_count;
+ wait_queue_head_t reset_wq;
+
+ /* chain */
+ struct chain_tracker *chain_lookup;
+ struct list_head free_chain_list;
+ struct dma_pool *chain_dma_pool;
+ ulong chain_pages;
+ u16 max_sges_in_main_message;
+ u16 max_sges_in_chain_message;
+ u16 chains_needed_per_io;
+ u32 chain_depth;
+
+ /* hi-priority queue */
+ u16 hi_priority_smid;
+ u8 *hi_priority;
+ dma_addr_t hi_priority_dma;
+ u16 hi_priority_depth;
+ struct request_tracker *hpr_lookup;
+ struct list_head hpr_free_list;
+
+ /* internal queue */
+ u16 internal_smid;
+ u8 *internal;
+ dma_addr_t internal_dma;
+ u16 internal_depth;
+ struct request_tracker *internal_lookup;
+ struct list_head internal_free_list;
+
+ /* sense */
+ u8 *sense;
+ dma_addr_t sense_dma;
+ struct dma_pool *sense_dma_pool;
+
+ /* reply */
+ u16 reply_sz;
+ u8 *reply;
+ dma_addr_t reply_dma;
+ u32 reply_dma_max_address;
+ u32 reply_dma_min_address;
+ struct dma_pool *reply_dma_pool;
+
+ /* reply free queue */
+ u16 reply_free_queue_depth;
+ __le32 *reply_free;
+ dma_addr_t reply_free_dma;
+ struct dma_pool *reply_free_dma_pool;
+ u32 reply_free_host_index;
+
+ /* reply post queue */
+ u16 reply_post_queue_depth;
+ Mpi2ReplyDescriptorsUnion_t *reply_post_free;
+ dma_addr_t reply_post_free_dma;
+ struct dma_pool *reply_post_free_dma_pool;
+ u8 reply_queue_count;
+ struct list_head reply_queue_list;
+
+ struct list_head delayed_tr_list;
+ struct list_head delayed_tr_volume_list;
+
+ /* diag buffer support */
+ u8 *diag_buffer[MPI2_DIAG_BUF_TYPE_COUNT];
+ u32 diag_buffer_sz[MPI2_DIAG_BUF_TYPE_COUNT];
+ dma_addr_t diag_buffer_dma[MPI2_DIAG_BUF_TYPE_COUNT];
+ u8 diag_buffer_status[MPI2_DIAG_BUF_TYPE_COUNT];
+ u32 unique_id[MPI2_DIAG_BUF_TYPE_COUNT];
+ u32 product_specific[MPI2_DIAG_BUF_TYPE_COUNT][23];
+ u32 diagnostic_flags[MPI2_DIAG_BUF_TYPE_COUNT];
+ u32 ring_buffer_offset;
+ u32 ring_buffer_sz;
+ spinlock_t diag_trigger_lock;
+ u8 diag_trigger_active;
+ struct SL_WH_MASTER_TRIGGER_T diag_trigger_master;
+ struct SL_WH_EVENT_TRIGGERS_T diag_trigger_event;
+ struct SL_WH_SCSI_TRIGGERS_T diag_trigger_scsi;
+ struct SL_WH_MPI_TRIGGERS_T diag_trigger_mpi;
+};
+
+typedef u8 (*MPT_CALLBACK)(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply);
+
+
+/* base shared API */
+extern struct list_head mpt3sas_ioc_list;
+void mpt3sas_base_start_watchdog(struct MPT3SAS_ADAPTER *ioc);
+void mpt3sas_base_stop_watchdog(struct MPT3SAS_ADAPTER *ioc);
+
+int mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc);
+void mpt3sas_base_detach(struct MPT3SAS_ADAPTER *ioc);
+int mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc);
+void mpt3sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc);
+int mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, int sleep_flag,
+ enum reset_type type);
+
+void *mpt3sas_base_get_msg_frame(struct MPT3SAS_ADAPTER *ioc, u16 smid);
+void *mpt3sas_base_get_sense_buffer(struct MPT3SAS_ADAPTER *ioc, u16 smid);
+__le32 mpt3sas_base_get_sense_buffer_dma(struct MPT3SAS_ADAPTER *ioc,
+ u16 smid);
+void mpt3sas_base_flush_reply_queues(struct MPT3SAS_ADAPTER *ioc);
+
+/* hi-priority queue */
+u16 mpt3sas_base_get_smid_hpr(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx);
+u16 mpt3sas_base_get_smid_scsiio(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx,
+ struct scsi_cmnd *scmd);
+
+u16 mpt3sas_base_get_smid(struct MPT3SAS_ADAPTER *ioc, u8 cb_idx);
+void mpt3sas_base_free_smid(struct MPT3SAS_ADAPTER *ioc, u16 smid);
+void mpt3sas_base_put_smid_scsi_io(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u16 handle);
+void mpt3sas_base_put_smid_fast_path(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u16 handle);
+void mpt3sas_base_put_smid_hi_priority(struct MPT3SAS_ADAPTER *ioc, u16 smid);
+void mpt3sas_base_put_smid_default(struct MPT3SAS_ADAPTER *ioc, u16 smid);
+void mpt3sas_base_initialize_callback_handler(void);
+u8 mpt3sas_base_register_callback_handler(MPT_CALLBACK cb_func);
+void mpt3sas_base_release_callback_handler(u8 cb_idx);
+
+u8 mpt3sas_base_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply);
+u8 mpt3sas_port_enable_done(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u8 msix_index, u32 reply);
+void *mpt3sas_base_get_reply_virt_addr(struct MPT3SAS_ADAPTER *ioc,
+ u32 phys_addr);
+
+u32 mpt3sas_base_get_iocstate(struct MPT3SAS_ADAPTER *ioc, int cooked);
+
+void mpt3sas_base_fault_info(struct MPT3SAS_ADAPTER *ioc , u16 fault_code);
+int mpt3sas_base_sas_iounit_control(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2SasIoUnitControlReply_t *mpi_reply,
+ Mpi2SasIoUnitControlRequest_t *mpi_request);
+int mpt3sas_base_scsi_enclosure_processor(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request);
+
+void mpt3sas_base_validate_event_type(struct MPT3SAS_ADAPTER *ioc,
+ u32 *event_type);
+
+void mpt3sas_halt_firmware(struct MPT3SAS_ADAPTER *ioc);
+
+void mpt3sas_base_update_missing_delay(struct MPT3SAS_ADAPTER *ioc,
+ u16 device_missing_delay, u8 io_missing_delay);
+
+int mpt3sas_port_enable(struct MPT3SAS_ADAPTER *ioc);
+
+
+/* scsih shared API */
+u8 mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index,
+ u32 reply);
+void mpt3sas_scsih_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase);
+
+int mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle,
+ uint channel, uint id, uint lun, u8 type, u16 smid_task,
+ ulong timeout, unsigned long serial_number, enum mutex_type m_type);
+void mpt3sas_scsih_set_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle);
+void mpt3sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle);
+void mpt3sas_expander_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address);
+void mpt3sas_device_remove_by_sas_address(struct MPT3SAS_ADAPTER *ioc,
+ u64 sas_address);
+
+struct _sas_node *mpt3sas_scsih_expander_find_by_handle(
+ struct MPT3SAS_ADAPTER *ioc, u16 handle);
+struct _sas_node *mpt3sas_scsih_expander_find_by_sas_address(
+ struct MPT3SAS_ADAPTER *ioc, u64 sas_address);
+struct _sas_device *mpt3sas_scsih_sas_device_find_by_sas_address(
+ struct MPT3SAS_ADAPTER *ioc, u64 sas_address);
+
+void mpt3sas_port_enable_complete(struct MPT3SAS_ADAPTER *ioc);
+
+/* config shared API */
+u8 mpt3sas_config_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply);
+int mpt3sas_config_get_number_hba_phys(struct MPT3SAS_ADAPTER *ioc,
+ u8 *num_phys);
+int mpt3sas_config_get_manufacturing_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page);
+int mpt3sas_config_get_manufacturing_pg7(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage7_t *config_page,
+ u16 sz);
+int mpt3sas_config_get_manufacturing_pg10(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply,
+ struct Mpi2ManufacturingPage10_t *config_page);
+
+int mpt3sas_config_get_manufacturing_pg11(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply,
+ struct Mpi2ManufacturingPage11_t *config_page);
+int mpt3sas_config_set_manufacturing_pg11(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply,
+ struct Mpi2ManufacturingPage11_t *config_page);
+
+int mpt3sas_config_get_bios_pg2(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2BiosPage2_t *config_page);
+int mpt3sas_config_get_bios_pg3(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2BiosPage3_t *config_page);
+int mpt3sas_config_get_iounit_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2IOUnitPage0_t *config_page);
+int mpt3sas_config_get_sas_device_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasDevicePage0_t *config_page,
+ u32 form, u32 handle);
+int mpt3sas_config_get_sas_device_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasDevicePage1_t *config_page,
+ u32 form, u32 handle);
+int mpt3sas_config_get_sas_iounit_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage0_t *config_page,
+ u16 sz);
+int mpt3sas_config_get_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2IOUnitPage1_t *config_page);
+int mpt3sas_config_set_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2IOUnitPage1_t *config_page);
+int mpt3sas_config_get_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page,
+ u16 sz);
+int mpt3sas_config_set_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page,
+ u16 sz);
+int mpt3sas_config_get_ioc_pg8(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2IOCPage8_t *config_page);
+int mpt3sas_config_get_expander_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2ExpanderPage0_t *config_page,
+ u32 form, u32 handle);
+int mpt3sas_config_get_expander_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2ExpanderPage1_t *config_page,
+ u32 phy_number, u16 handle);
+int mpt3sas_config_get_enclosure_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasEnclosurePage0_t *config_page,
+ u32 form, u32 handle);
+int mpt3sas_config_get_phy_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasPhyPage0_t *config_page, u32 phy_number);
+int mpt3sas_config_get_phy_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasPhyPage1_t *config_page, u32 phy_number);
+int mpt3sas_config_get_raid_volume_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form,
+ u32 handle);
+int mpt3sas_config_get_number_pds(struct MPT3SAS_ADAPTER *ioc, u16 handle,
+ u8 *num_pds);
+int mpt3sas_config_get_raid_volume_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 form,
+ u32 handle, u16 sz);
+int mpt3sas_config_get_phys_disk_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidPhysDiskPage0_t *config_page,
+ u32 form, u32 form_specific);
+int mpt3sas_config_get_volume_handle(struct MPT3SAS_ADAPTER *ioc, u16 pd_handle,
+ u16 *volume_handle);
+int mpt3sas_config_get_volume_wwid(struct MPT3SAS_ADAPTER *ioc,
+ u16 volume_handle, u64 *wwid);
+
+/* ctl shared API */
+extern struct device_attribute *mpt3sas_host_attrs[];
+extern struct device_attribute *mpt3sas_dev_attrs[];
+void mpt3sas_ctl_init(void);
+void mpt3sas_ctl_exit(void);
+u8 mpt3sas_ctl_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply);
+void mpt3sas_ctl_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase);
+u8 mpt3sas_ctl_event_callback(struct MPT3SAS_ADAPTER *ioc,
+ u8 msix_index, u32 reply);
+void mpt3sas_ctl_add_to_event_log(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventNotificationReply_t *mpi_reply);
+
+void mpt3sas_enable_diag_buffer(struct MPT3SAS_ADAPTER *ioc,
+ u8 bits_to_regsiter);
+int mpt3sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type,
+ u8 *issue_reset);
+
+/* transport shared API */
+u8 mpt3sas_transport_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply);
+struct _sas_port *mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc,
+ u16 handle, u64 sas_address);
+void mpt3sas_transport_port_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
+ u64 sas_address_parent);
+int mpt3sas_transport_add_host_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy
+ *mpt3sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev);
+int mpt3sas_transport_add_expander_phy(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_phy *mpt3sas_phy, Mpi2ExpanderPage1_t expander_pg1,
+ struct device *parent_dev);
+void mpt3sas_transport_update_links(struct MPT3SAS_ADAPTER *ioc,
+ u64 sas_address, u16 handle, u8 phy_number, u8 link_rate);
+extern struct sas_function_template mpt3sas_transport_functions;
+extern struct scsi_transport_template *mpt3sas_transport_template;
+extern int scsi_internal_device_block(struct scsi_device *sdev);
+extern int scsi_internal_device_unblock(struct scsi_device *sdev,
+ enum scsi_device_state new_state);
+/* trigger data externs */
+void mpt3sas_send_trigger_data_event(struct MPT3SAS_ADAPTER *ioc,
+ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data);
+void mpt3sas_process_trigger_data(struct MPT3SAS_ADAPTER *ioc,
+ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data);
+void mpt3sas_trigger_master(struct MPT3SAS_ADAPTER *ioc,
+ u32 tigger_bitmask);
+void mpt3sas_trigger_event(struct MPT3SAS_ADAPTER *ioc, u16 event,
+ u16 log_entry_qualifier);
+void mpt3sas_trigger_scsi(struct MPT3SAS_ADAPTER *ioc, u8 sense_key,
+ u8 asc, u8 ascq);
+void mpt3sas_trigger_mpi(struct MPT3SAS_ADAPTER *ioc, u16 ioc_status,
+ u32 loginfo);
+#endif /* MPT3SAS_BASE_H_INCLUDED */
diff --git a/drivers/scsi/mpt3sas/mpt3sas_config.c b/drivers/scsi/mpt3sas/mpt3sas_config.c
new file mode 100644
index 00000000000..ce7e59b2fc0
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_config.c
@@ -0,0 +1,1650 @@
+/*
+ * This module provides common API for accessing firmware configuration pages
+ *
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_base.c
+ * Copyright (C) 2012 LSI Corporation
+ * (mailto:DL-MPTFusionLinux@lsi.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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/blkdev.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include "mpt3sas_base.h"
+
+/* local definitions */
+
+/* Timeout for config page request (in seconds) */
+#define MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT 15
+
+/* Common sgl flags for READING a config page. */
+#define MPT3_CONFIG_COMMON_SGLFLAGS ((MPI2_SGE_FLAGS_SIMPLE_ELEMENT | \
+ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER \
+ | MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT)
+
+/* Common sgl flags for WRITING a config page. */
+#define MPT3_CONFIG_COMMON_WRITE_SGLFLAGS ((MPI2_SGE_FLAGS_SIMPLE_ELEMENT | \
+ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER \
+ | MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_HOST_TO_IOC) \
+ << MPI2_SGE_FLAGS_SHIFT)
+
+/**
+ * struct config_request - obtain dma memory via routine
+ * @sz: size
+ * @page: virt pointer
+ * @page_dma: phys pointer
+ *
+ */
+struct config_request {
+ u16 sz;
+ void *page;
+ dma_addr_t page_dma;
+};
+
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+/**
+ * _config_display_some_debug - debug routine
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @calling_function_name: string pass from calling function
+ * @mpi_reply: reply message frame
+ * Context: none.
+ *
+ * Function for displaying debug info helpful when debugging issues
+ * in this module.
+ */
+static void
+_config_display_some_debug(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ char *calling_function_name, MPI2DefaultReply_t *mpi_reply)
+{
+ Mpi2ConfigRequest_t *mpi_request;
+ char *desc = NULL;
+
+ if (!(ioc->logging_level & MPT_DEBUG_CONFIG))
+ return;
+
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ switch (mpi_request->Header.PageType & MPI2_CONFIG_PAGETYPE_MASK) {
+ case MPI2_CONFIG_PAGETYPE_IO_UNIT:
+ desc = "io_unit";
+ break;
+ case MPI2_CONFIG_PAGETYPE_IOC:
+ desc = "ioc";
+ break;
+ case MPI2_CONFIG_PAGETYPE_BIOS:
+ desc = "bios";
+ break;
+ case MPI2_CONFIG_PAGETYPE_RAID_VOLUME:
+ desc = "raid_volume";
+ break;
+ case MPI2_CONFIG_PAGETYPE_MANUFACTURING:
+ desc = "manufaucturing";
+ break;
+ case MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK:
+ desc = "physdisk";
+ break;
+ case MPI2_CONFIG_PAGETYPE_EXTENDED:
+ switch (mpi_request->ExtPageType) {
+ case MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT:
+ desc = "sas_io_unit";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER:
+ desc = "sas_expander";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE:
+ desc = "sas_device";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_SAS_PHY:
+ desc = "sas_phy";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_LOG:
+ desc = "log";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE:
+ desc = "enclosure";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG:
+ desc = "raid_config";
+ break;
+ case MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING:
+ desc = "driver_mappping";
+ break;
+ }
+ break;
+ }
+
+ if (!desc)
+ return;
+
+ pr_info(MPT3SAS_FMT
+ "%s: %s(%d), action(%d), form(0x%08x), smid(%d)\n",
+ ioc->name, calling_function_name, desc,
+ mpi_request->Header.PageNumber, mpi_request->Action,
+ le32_to_cpu(mpi_request->PageAddress), smid);
+
+ if (!mpi_reply)
+ return;
+
+ if (mpi_reply->IOCStatus || mpi_reply->IOCLogInfo)
+ pr_info(MPT3SAS_FMT
+ "\tiocstatus(0x%04x), loginfo(0x%08x)\n",
+ ioc->name, le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo));
+}
+#endif
+
+/**
+ * _config_alloc_config_dma_memory - obtain physical memory
+ * @ioc: per adapter object
+ * @mem: struct config_request
+ *
+ * A wrapper for obtaining dma-able memory for config page request.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_config_alloc_config_dma_memory(struct MPT3SAS_ADAPTER *ioc,
+ struct config_request *mem)
+{
+ int r = 0;
+
+ if (mem->sz > ioc->config_page_sz) {
+ mem->page = dma_alloc_coherent(&ioc->pdev->dev, mem->sz,
+ &mem->page_dma, GFP_KERNEL);
+ if (!mem->page) {
+ pr_err(MPT3SAS_FMT
+ "%s: dma_alloc_coherent failed asking for (%d) bytes!!\n",
+ ioc->name, __func__, mem->sz);
+ r = -ENOMEM;
+ }
+ } else { /* use tmp buffer if less than 512 bytes */
+ mem->page = ioc->config_page;
+ mem->page_dma = ioc->config_page_dma;
+ }
+ return r;
+}
+
+/**
+ * _config_free_config_dma_memory - wrapper to free the memory
+ * @ioc: per adapter object
+ * @mem: struct config_request
+ *
+ * A wrapper to free dma-able memory when using _config_alloc_config_dma_memory.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static void
+_config_free_config_dma_memory(struct MPT3SAS_ADAPTER *ioc,
+ struct config_request *mem)
+{
+ if (mem->sz > ioc->config_page_sz)
+ dma_free_coherent(&ioc->pdev->dev, mem->sz, mem->page,
+ mem->page_dma);
+}
+
+/**
+ * mpt3sas_config_done - config page completion routine
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: none.
+ *
+ * The callback handler when using _config_request.
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+u8
+mpt3sas_config_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+
+ if (ioc->config_cmds.status == MPT3_CMD_NOT_USED)
+ return 1;
+ if (ioc->config_cmds.smid != smid)
+ return 1;
+ ioc->config_cmds.status |= MPT3_CMD_COMPLETE;
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ if (mpi_reply) {
+ ioc->config_cmds.status |= MPT3_CMD_REPLY_VALID;
+ memcpy(ioc->config_cmds.reply, mpi_reply,
+ mpi_reply->MsgLength*4);
+ }
+ ioc->config_cmds.status &= ~MPT3_CMD_PENDING;
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+ _config_display_some_debug(ioc, smid, "config_done", mpi_reply);
+#endif
+ ioc->config_cmds.smid = USHRT_MAX;
+ complete(&ioc->config_cmds.done);
+ return 1;
+}
+
+/**
+ * _config_request - main routine for sending config page requests
+ * @ioc: per adapter object
+ * @mpi_request: request message frame
+ * @mpi_reply: reply mf payload returned from firmware
+ * @timeout: timeout in seconds
+ * @config_page: contents of the config page
+ * @config_page_sz: size of config page
+ * Context: sleep
+ *
+ * A generic API for config page requests to firmware.
+ *
+ * The ioc->config_cmds.status flag should be MPT3_CMD_NOT_USED before calling
+ * this API.
+ *
+ * The callback index is set inside `ioc->config_cb_idx.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_config_request(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigRequest_t
+ *mpi_request, Mpi2ConfigReply_t *mpi_reply, int timeout,
+ void *config_page, u16 config_page_sz)
+{
+ u16 smid;
+ u32 ioc_state;
+ unsigned long timeleft;
+ Mpi2ConfigRequest_t *config_request;
+ int r;
+ u8 retry_count, issue_host_reset = 0;
+ u16 wait_state_count;
+ struct config_request mem;
+ u32 ioc_status = UINT_MAX;
+
+ mutex_lock(&ioc->config_cmds.mutex);
+ if (ioc->config_cmds.status != MPT3_CMD_NOT_USED) {
+ pr_err(MPT3SAS_FMT "%s: config_cmd in use\n",
+ ioc->name, __func__);
+ mutex_unlock(&ioc->config_cmds.mutex);
+ return -EAGAIN;
+ }
+
+ retry_count = 0;
+ memset(&mem, 0, sizeof(struct config_request));
+
+ mpi_request->VF_ID = 0; /* TODO */
+ mpi_request->VP_ID = 0;
+
+ if (config_page) {
+ mpi_request->Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request->Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request->Header.PageType = mpi_reply->Header.PageType;
+ mpi_request->Header.PageLength = mpi_reply->Header.PageLength;
+ mpi_request->ExtPageLength = mpi_reply->ExtPageLength;
+ mpi_request->ExtPageType = mpi_reply->ExtPageType;
+ if (mpi_request->Header.PageLength)
+ mem.sz = mpi_request->Header.PageLength * 4;
+ else
+ mem.sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r != 0)
+ goto out;
+ if (mpi_request->Action ==
+ MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT ||
+ mpi_request->Action ==
+ MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM) {
+ ioc->base_add_sg_single(&mpi_request->PageBufferSGE,
+ MPT3_CONFIG_COMMON_WRITE_SGLFLAGS | mem.sz,
+ mem.page_dma);
+ memcpy(mem.page, config_page, min_t(u16, mem.sz,
+ config_page_sz));
+ } else {
+ memset(config_page, 0, config_page_sz);
+ ioc->base_add_sg_single(&mpi_request->PageBufferSGE,
+ MPT3_CONFIG_COMMON_SGLFLAGS | mem.sz, mem.page_dma);
+ memset(mem.page, 0, min_t(u16, mem.sz, config_page_sz));
+ }
+ }
+
+ retry_config:
+ if (retry_count) {
+ if (retry_count > 2) { /* attempt only 2 retries */
+ r = -EFAULT;
+ goto free_mem;
+ }
+ pr_info(MPT3SAS_FMT "%s: attempting retry (%d)\n",
+ ioc->name, __func__, retry_count);
+ }
+ wait_state_count = 0;
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ if (wait_state_count++ == MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT) {
+ pr_err(MPT3SAS_FMT
+ "%s: failed due to ioc not operational\n",
+ ioc->name, __func__);
+ ioc->config_cmds.status = MPT3_CMD_NOT_USED;
+ r = -EFAULT;
+ goto free_mem;
+ }
+ ssleep(1);
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ pr_info(MPT3SAS_FMT
+ "%s: waiting for operational state(count=%d)\n",
+ ioc->name, __func__, wait_state_count);
+ }
+ if (wait_state_count)
+ pr_info(MPT3SAS_FMT "%s: ioc is operational\n",
+ ioc->name, __func__);
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->config_cb_idx);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ ioc->config_cmds.status = MPT3_CMD_NOT_USED;
+ r = -EAGAIN;
+ goto free_mem;
+ }
+
+ r = 0;
+ memset(mpi_reply, 0, sizeof(Mpi2ConfigReply_t));
+ ioc->config_cmds.status = MPT3_CMD_PENDING;
+ config_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->config_cmds.smid = smid;
+ memcpy(config_request, mpi_request, sizeof(Mpi2ConfigRequest_t));
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+ _config_display_some_debug(ioc, smid, "config_request", NULL);
+#endif
+ init_completion(&ioc->config_cmds.done);
+ mpt3sas_base_put_smid_default(ioc, smid);
+ timeleft = wait_for_completion_timeout(&ioc->config_cmds.done,
+ timeout*HZ);
+ if (!(ioc->config_cmds.status & MPT3_CMD_COMPLETE)) {
+ pr_err(MPT3SAS_FMT "%s: timeout\n",
+ ioc->name, __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2ConfigRequest_t)/4);
+ retry_count++;
+ if (ioc->config_cmds.smid == smid)
+ mpt3sas_base_free_smid(ioc, smid);
+ if ((ioc->shost_recovery) || (ioc->config_cmds.status &
+ MPT3_CMD_RESET) || ioc->pci_error_recovery)
+ goto retry_config;
+ issue_host_reset = 1;
+ r = -EFAULT;
+ goto free_mem;
+ }
+
+ if (ioc->config_cmds.status & MPT3_CMD_REPLY_VALID) {
+ memcpy(mpi_reply, ioc->config_cmds.reply,
+ sizeof(Mpi2ConfigReply_t));
+
+ /* Reply Frame Sanity Checks to workaround FW issues */
+ if ((mpi_request->Header.PageType & 0xF) !=
+ (mpi_reply->Header.PageType & 0xF)) {
+ _debug_dump_mf(mpi_request, ioc->request_sz/4);
+ _debug_dump_reply(mpi_reply, ioc->request_sz/4);
+ panic(KERN_WARNING MPT3SAS_FMT "%s: Firmware BUG:" \
+ " mpi_reply mismatch: Requested PageType(0x%02x)" \
+ " Reply PageType(0x%02x)\n", \
+ ioc->name, __func__,
+ (mpi_request->Header.PageType & 0xF),
+ (mpi_reply->Header.PageType & 0xF));
+ }
+
+ if (((mpi_request->Header.PageType & 0xF) ==
+ MPI2_CONFIG_PAGETYPE_EXTENDED) &&
+ mpi_request->ExtPageType != mpi_reply->ExtPageType) {
+ _debug_dump_mf(mpi_request, ioc->request_sz/4);
+ _debug_dump_reply(mpi_reply, ioc->request_sz/4);
+ panic(KERN_WARNING MPT3SAS_FMT "%s: Firmware BUG:" \
+ " mpi_reply mismatch: Requested ExtPageType(0x%02x)"
+ " Reply ExtPageType(0x%02x)\n",
+ ioc->name, __func__, mpi_request->ExtPageType,
+ mpi_reply->ExtPageType);
+ }
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus)
+ & MPI2_IOCSTATUS_MASK;
+ }
+
+ if (retry_count)
+ pr_info(MPT3SAS_FMT "%s: retry (%d) completed!!\n", \
+ ioc->name, __func__, retry_count);
+
+ if ((ioc_status == MPI2_IOCSTATUS_SUCCESS) &&
+ config_page && mpi_request->Action ==
+ MPI2_CONFIG_ACTION_PAGE_READ_CURRENT) {
+ u8 *p = (u8 *)mem.page;
+
+ /* Config Page Sanity Checks to workaround FW issues */
+ if (p) {
+ if ((mpi_request->Header.PageType & 0xF) !=
+ (p[3] & 0xF)) {
+ _debug_dump_mf(mpi_request, ioc->request_sz/4);
+ _debug_dump_reply(mpi_reply, ioc->request_sz/4);
+ _debug_dump_config(p, min_t(u16, mem.sz,
+ config_page_sz)/4);
+ panic(KERN_WARNING MPT3SAS_FMT
+ "%s: Firmware BUG:" \
+ " config page mismatch:"
+ " Requested PageType(0x%02x)"
+ " Reply PageType(0x%02x)\n",
+ ioc->name, __func__,
+ (mpi_request->Header.PageType & 0xF),
+ (p[3] & 0xF));
+ }
+
+ if (((mpi_request->Header.PageType & 0xF) ==
+ MPI2_CONFIG_PAGETYPE_EXTENDED) &&
+ (mpi_request->ExtPageType != p[6])) {
+ _debug_dump_mf(mpi_request, ioc->request_sz/4);
+ _debug_dump_reply(mpi_reply, ioc->request_sz/4);
+ _debug_dump_config(p, min_t(u16, mem.sz,
+ config_page_sz)/4);
+ panic(KERN_WARNING MPT3SAS_FMT
+ "%s: Firmware BUG:" \
+ " config page mismatch:"
+ " Requested ExtPageType(0x%02x)"
+ " Reply ExtPageType(0x%02x)\n",
+ ioc->name, __func__,
+ mpi_request->ExtPageType, p[6]);
+ }
+ }
+ memcpy(config_page, mem.page, min_t(u16, mem.sz,
+ config_page_sz));
+ }
+
+ free_mem:
+ if (config_page)
+ _config_free_config_dma_memory(ioc, &mem);
+ out:
+ ioc->config_cmds.status = MPT3_CMD_NOT_USED;
+ mutex_unlock(&ioc->config_cmds.mutex);
+
+ if (issue_host_reset)
+ mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_manufacturing_pg0 - obtain manufacturing page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_manufacturing_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_manufacturing_pg7 - obtain manufacturing page 7
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @sz: size of buffer passed in config_page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_manufacturing_pg7(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage7_t *config_page,
+ u16 sz)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
+ mpi_request.Header.PageNumber = 7;
+ mpi_request.Header.PageVersion = MPI2_MANUFACTURING7_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sz);
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_manufacturing_pg10 - obtain manufacturing page 10
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_manufacturing_pg10(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply,
+ struct Mpi2ManufacturingPage10_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
+ mpi_request.Header.PageNumber = 10;
+ mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_manufacturing_pg11 - obtain manufacturing page 11
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_manufacturing_pg11(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply,
+ struct Mpi2ManufacturingPage11_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
+ mpi_request.Header.PageNumber = 11;
+ mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_set_manufacturing_pg11 - set manufacturing page 11
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_set_manufacturing_pg11(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply,
+ struct Mpi2ManufacturingPage11_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
+ mpi_request.Header.PageNumber = 11;
+ mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_bios_pg2 - obtain bios page 2
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_bios_pg2(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2BiosPage2_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS;
+ mpi_request.Header.PageNumber = 2;
+ mpi_request.Header.PageVersion = MPI2_BIOSPAGE2_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_bios_pg3 - obtain bios page 3
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_bios_pg3(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2BiosPage3_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS;
+ mpi_request.Header.PageNumber = 3;
+ mpi_request.Header.PageVersion = MPI2_BIOSPAGE3_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_iounit_pg0 - obtain iounit page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_iounit_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage0_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_iounit_pg1 - obtain iounit page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_iounit_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE1_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_set_iounit_pg1 - set iounit page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_set_iounit_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_IOUNITPAGE1_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_ioc_pg8 - obtain ioc page 8
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_ioc_pg8(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2IOCPage8_t *config_page)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IOC;
+ mpi_request.Header.PageNumber = 8;
+ mpi_request.Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_sas_device_pg0 - obtain sas device page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: device handle
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_sas_device_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasDevicePage0_t *config_page,
+ u32 form, u32 handle)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE;
+ mpi_request.Header.PageVersion = MPI2_SASDEVICE0_PAGEVERSION;
+ mpi_request.Header.PageNumber = 0;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_sas_device_pg1 - obtain sas device page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: device handle
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_sas_device_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasDevicePage1_t *config_page,
+ u32 form, u32 handle)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE;
+ mpi_request.Header.PageVersion = MPI2_SASDEVICE1_PAGEVERSION;
+ mpi_request.Header.PageNumber = 1;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_number_hba_phys - obtain number of phys on the host
+ * @ioc: per adapter object
+ * @num_phys: pointer returned with the number of phys
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_number_hba_phys(struct MPT3SAS_ADAPTER *ioc, u8 *num_phys)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+ u16 ioc_status;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasIOUnitPage0_t config_page;
+
+ *num_phys = 0;
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, &mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, &mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, &config_page,
+ sizeof(Mpi2SasIOUnitPage0_t));
+ if (!r) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS)
+ *num_phys = config_page.NumPhys;
+ }
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_sas_iounit_pg0 - obtain sas iounit page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @sz: size of buffer passed in config_page
+ * Context: sleep.
+ *
+ * Calling function should call config_get_number_hba_phys prior to
+ * this function, so enough memory is allocated for config_page.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_sas_iounit_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage0_t *config_page,
+ u16 sz)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_sas_iounit_pg1 - obtain sas iounit page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @sz: size of buffer passed in config_page
+ * Context: sleep.
+ *
+ * Calling function should call config_get_number_hba_phys prior to
+ * this function, so enough memory is allocated for config_page.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page,
+ u16 sz)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE1_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_set_sas_iounit_pg1 - send sas iounit page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @sz: size of buffer passed in config_page
+ * Context: sleep.
+ *
+ * Calling function should call config_get_number_hba_phys prior to
+ * this function, so enough memory is allocated for config_page.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_set_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page,
+ u16 sz)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE1_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_expander_pg0 - obtain expander page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: expander handle
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_expander_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2ExpanderPage0_t *config_page, u32 form, u32 handle)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_SASEXPANDER0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_expander_pg1 - obtain expander page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @phy_number: phy number
+ * @handle: expander handle
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_expander_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2ExpanderPage1_t *config_page, u32 phy_number,
+ u16 handle)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_SASEXPANDER1_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress =
+ cpu_to_le32(MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM |
+ (phy_number << MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_enclosure_pg0 - obtain enclosure page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: expander handle
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_enclosure_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasEnclosurePage0_t *config_page, u32 form, u32 handle)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_SASENCLOSURE0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_phy_pg0 - obtain phy page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @phy_number: phy number
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_phy_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasPhyPage0_t *config_page, u32 phy_number)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_PHY;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_SASPHY0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress =
+ cpu_to_le32(MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | phy_number);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_phy_pg1 - obtain phy page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @phy_number: phy number
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_phy_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2SasPhyPage1_t *config_page, u32 phy_number)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_PHY;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_SASPHY1_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress =
+ cpu_to_le32(MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | phy_number);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_raid_volume_pg1 - obtain raid volume page 1
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: volume handle
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_raid_volume_pg1(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form,
+ u32 handle)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
+ mpi_request.Header.PageNumber = 1;
+ mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE1_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_number_pds - obtain number of phys disk assigned to volume
+ * @ioc: per adapter object
+ * @handle: volume handle
+ * @num_pds: returns pds count
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_number_pds(struct MPT3SAS_ADAPTER *ioc, u16 handle,
+ u8 *num_pds)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ Mpi2RaidVolPage0_t config_page;
+ Mpi2ConfigReply_t mpi_reply;
+ int r;
+ u16 ioc_status;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ *num_pds = 0;
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, &mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress =
+ cpu_to_le32(MPI2_RAID_VOLUME_PGAD_FORM_HANDLE | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, &mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, &config_page,
+ sizeof(Mpi2RaidVolPage0_t));
+ if (!r) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS)
+ *num_pds = config_page.NumPhysDisks;
+ }
+
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_raid_volume_pg0 - obtain raid volume page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_HANDLE or HANDLE
+ * @handle: volume handle
+ * @sz: size of buffer passed in config_page
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_raid_volume_pg0(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 form,
+ u32 handle, u16 sz)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | handle);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_phys_disk_pg0 - obtain phys disk page 0
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * @form: GET_NEXT_PHYSDISKNUM, PHYSDISKNUM, DEVHANDLE
+ * @form_specific: specific to the form
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_phys_disk_pg0(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+ *mpi_reply, Mpi2RaidPhysDiskPage0_t *config_page, u32 form,
+ u32 form_specific)
+{
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
+
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.PageAddress = cpu_to_le32(form | form_specific);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_volume_handle - returns volume handle for give hidden
+ * raid components
+ * @ioc: per adapter object
+ * @pd_handle: phys disk handle
+ * @volume_handle: volume handle
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_volume_handle(struct MPT3SAS_ADAPTER *ioc, u16 pd_handle,
+ u16 *volume_handle)
+{
+ Mpi2RaidConfigurationPage0_t *config_page = NULL;
+ Mpi2ConfigRequest_t mpi_request;
+ Mpi2ConfigReply_t mpi_reply;
+ int r, i, config_page_sz;
+ u16 ioc_status;
+ int config_num;
+ u16 element_type;
+ u16 phys_disk_dev_handle;
+
+ *volume_handle = 0;
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
+ mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG;
+ mpi_request.Header.PageVersion = MPI2_RAIDCONFIG0_PAGEVERSION;
+ mpi_request.Header.PageNumber = 0;
+ ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, &mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
+
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ config_page_sz = (le16_to_cpu(mpi_reply.ExtPageLength) * 4);
+ config_page = kmalloc(config_page_sz, GFP_KERNEL);
+ if (!config_page) {
+ r = -1;
+ goto out;
+ }
+
+ config_num = 0xff;
+ while (1) {
+ mpi_request.PageAddress = cpu_to_le32(config_num +
+ MPI2_RAID_PGAD_FORM_GET_NEXT_CONFIGNUM);
+ r = _config_request(ioc, &mpi_request, &mpi_reply,
+ MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ config_page_sz);
+ if (r)
+ goto out;
+ r = -1;
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ goto out;
+ for (i = 0; i < config_page->NumElements; i++) {
+ element_type = le16_to_cpu(config_page->
+ ConfigElement[i].ElementFlags) &
+ MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE;
+ if (element_type ==
+ MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT ||
+ element_type ==
+ MPI2_RAIDCONFIG0_EFLAGS_OCE_ELEMENT) {
+ phys_disk_dev_handle =
+ le16_to_cpu(config_page->ConfigElement[i].
+ PhysDiskDevHandle);
+ if (phys_disk_dev_handle == pd_handle) {
+ *volume_handle =
+ le16_to_cpu(config_page->
+ ConfigElement[i].VolDevHandle);
+ r = 0;
+ goto out;
+ }
+ } else if (element_type ==
+ MPI2_RAIDCONFIG0_EFLAGS_HOT_SPARE_ELEMENT) {
+ *volume_handle = 0;
+ r = 0;
+ goto out;
+ }
+ }
+ config_num = config_page->ConfigNum;
+ }
+ out:
+ kfree(config_page);
+ return r;
+}
+
+/**
+ * mpt3sas_config_get_volume_wwid - returns wwid given the volume handle
+ * @ioc: per adapter object
+ * @volume_handle: volume handle
+ * @wwid: volume wwid
+ * Context: sleep.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_config_get_volume_wwid(struct MPT3SAS_ADAPTER *ioc, u16 volume_handle,
+ u64 *wwid)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2RaidVolPage1_t raid_vol_pg1;
+
+ *wwid = 0;
+ if (!(mpt3sas_config_get_raid_volume_pg1(ioc, &mpi_reply,
+ &raid_vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE,
+ volume_handle))) {
+ *wwid = le64_to_cpu(raid_vol_pg1.WWID);
+ return 0;
+ } else
+ return -1;
+}
diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.c b/drivers/scsi/mpt3sas/mpt3sas_ctl.c
new file mode 100644
index 00000000000..8af944d7d13
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.c
@@ -0,0 +1,3297 @@
+/*
+ * Management Module Support for MPT (Message Passing Technology) based
+ * controllers
+ *
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_ctl.c
+ * Copyright (C) 2012 LSI Corporation
+ * (mailto:DL-MPTFusionLinux@lsi.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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/compat.h>
+#include <linux/poll.h>
+
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include "mpt3sas_base.h"
+#include "mpt3sas_ctl.h"
+
+
+static struct fasync_struct *async_queue;
+static DECLARE_WAIT_QUEUE_HEAD(ctl_poll_wait);
+
+
+/**
+ * enum block_state - blocking state
+ * @NON_BLOCKING: non blocking
+ * @BLOCKING: blocking
+ *
+ * These states are for ioctls that need to wait for a response
+ * from firmware, so they probably require sleep.
+ */
+enum block_state {
+ NON_BLOCKING,
+ BLOCKING,
+};
+
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+/**
+ * _ctl_sas_device_find_by_handle - sas device search
+ * @ioc: per adapter object
+ * @handle: sas device handle (assigned by firmware)
+ * Context: Calling function should acquire ioc->sas_device_lock
+ *
+ * This searches for sas_device based on sas_address, then return sas_device
+ * object.
+ */
+static struct _sas_device *
+_ctl_sas_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _sas_device *sas_device, *r;
+
+ r = NULL;
+ list_for_each_entry(sas_device, &ioc->sas_device_list, list) {
+ if (sas_device->handle != handle)
+ continue;
+ r = sas_device;
+ goto out;
+ }
+
+ out:
+ return r;
+}
+
+/**
+ * _ctl_display_some_debug - debug routine
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @calling_function_name: string pass from calling function
+ * @mpi_reply: reply message frame
+ * Context: none.
+ *
+ * Function for displaying debug info helpful when debugging issues
+ * in this module.
+ */
+static void
+_ctl_display_some_debug(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ char *calling_function_name, MPI2DefaultReply_t *mpi_reply)
+{
+ Mpi2ConfigRequest_t *mpi_request;
+ char *desc = NULL;
+
+ if (!(ioc->logging_level & MPT_DEBUG_IOCTL))
+ return;
+
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ switch (mpi_request->Function) {
+ case MPI2_FUNCTION_SCSI_IO_REQUEST:
+ {
+ Mpi2SCSIIORequest_t *scsi_request =
+ (Mpi2SCSIIORequest_t *)mpi_request;
+
+ snprintf(ioc->tmp_string, MPT_STRING_LENGTH,
+ "scsi_io, cmd(0x%02x), cdb_len(%d)",
+ scsi_request->CDB.CDB32[0],
+ le16_to_cpu(scsi_request->IoFlags) & 0xF);
+ desc = ioc->tmp_string;
+ break;
+ }
+ case MPI2_FUNCTION_SCSI_TASK_MGMT:
+ desc = "task_mgmt";
+ break;
+ case MPI2_FUNCTION_IOC_INIT:
+ desc = "ioc_init";
+ break;
+ case MPI2_FUNCTION_IOC_FACTS:
+ desc = "ioc_facts";
+ break;
+ case MPI2_FUNCTION_CONFIG:
+ {
+ Mpi2ConfigRequest_t *config_request =
+ (Mpi2ConfigRequest_t *)mpi_request;
+
+ snprintf(ioc->tmp_string, MPT_STRING_LENGTH,
+ "config, type(0x%02x), ext_type(0x%02x), number(%d)",
+ (config_request->Header.PageType &
+ MPI2_CONFIG_PAGETYPE_MASK), config_request->ExtPageType,
+ config_request->Header.PageNumber);
+ desc = ioc->tmp_string;
+ break;
+ }
+ case MPI2_FUNCTION_PORT_FACTS:
+ desc = "port_facts";
+ break;
+ case MPI2_FUNCTION_PORT_ENABLE:
+ desc = "port_enable";
+ break;
+ case MPI2_FUNCTION_EVENT_NOTIFICATION:
+ desc = "event_notification";
+ break;
+ case MPI2_FUNCTION_FW_DOWNLOAD:
+ desc = "fw_download";
+ break;
+ case MPI2_FUNCTION_FW_UPLOAD:
+ desc = "fw_upload";
+ break;
+ case MPI2_FUNCTION_RAID_ACTION:
+ desc = "raid_action";
+ break;
+ case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH:
+ {
+ Mpi2SCSIIORequest_t *scsi_request =
+ (Mpi2SCSIIORequest_t *)mpi_request;
+
+ snprintf(ioc->tmp_string, MPT_STRING_LENGTH,
+ "raid_pass, cmd(0x%02x), cdb_len(%d)",
+ scsi_request->CDB.CDB32[0],
+ le16_to_cpu(scsi_request->IoFlags) & 0xF);
+ desc = ioc->tmp_string;
+ break;
+ }
+ case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL:
+ desc = "sas_iounit_cntl";
+ break;
+ case MPI2_FUNCTION_SATA_PASSTHROUGH:
+ desc = "sata_pass";
+ break;
+ case MPI2_FUNCTION_DIAG_BUFFER_POST:
+ desc = "diag_buffer_post";
+ break;
+ case MPI2_FUNCTION_DIAG_RELEASE:
+ desc = "diag_release";
+ break;
+ case MPI2_FUNCTION_SMP_PASSTHROUGH:
+ desc = "smp_passthrough";
+ break;
+ }
+
+ if (!desc)
+ return;
+
+ pr_info(MPT3SAS_FMT "%s: %s, smid(%d)\n",
+ ioc->name, calling_function_name, desc, smid);
+
+ if (!mpi_reply)
+ return;
+
+ if (mpi_reply->IOCStatus || mpi_reply->IOCLogInfo)
+ pr_info(MPT3SAS_FMT
+ "\tiocstatus(0x%04x), loginfo(0x%08x)\n",
+ ioc->name, le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo));
+
+ if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
+ mpi_request->Function ==
+ MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) {
+ Mpi2SCSIIOReply_t *scsi_reply =
+ (Mpi2SCSIIOReply_t *)mpi_reply;
+ struct _sas_device *sas_device = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _ctl_sas_device_find_by_handle(ioc,
+ le16_to_cpu(scsi_reply->DevHandle));
+ if (sas_device) {
+ pr_warn(MPT3SAS_FMT "\tsas_address(0x%016llx), phy(%d)\n",
+ ioc->name, (unsigned long long)
+ sas_device->sas_address, sas_device->phy);
+ pr_warn(MPT3SAS_FMT
+ "\tenclosure_logical_id(0x%016llx), slot(%d)\n",
+ ioc->name, (unsigned long long)
+ sas_device->enclosure_logical_id, sas_device->slot);
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (scsi_reply->SCSIState || scsi_reply->SCSIStatus)
+ pr_info(MPT3SAS_FMT
+ "\tscsi_state(0x%02x), scsi_status"
+ "(0x%02x)\n", ioc->name,
+ scsi_reply->SCSIState,
+ scsi_reply->SCSIStatus);
+ }
+}
+
+#endif
+
+/**
+ * mpt3sas_ctl_done - ctl module completion routine
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: none.
+ *
+ * The callback handler when using ioc->ctl_cb_idx.
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+u8
+mpt3sas_ctl_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+ Mpi2SCSIIOReply_t *scsiio_reply;
+ const void *sense_data;
+ u32 sz;
+
+ if (ioc->ctl_cmds.status == MPT3_CMD_NOT_USED)
+ return 1;
+ if (ioc->ctl_cmds.smid != smid)
+ return 1;
+ ioc->ctl_cmds.status |= MPT3_CMD_COMPLETE;
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ if (mpi_reply) {
+ memcpy(ioc->ctl_cmds.reply, mpi_reply, mpi_reply->MsgLength*4);
+ ioc->ctl_cmds.status |= MPT3_CMD_REPLY_VALID;
+ /* get sense data */
+ if (mpi_reply->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
+ mpi_reply->Function ==
+ MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) {
+ scsiio_reply = (Mpi2SCSIIOReply_t *)mpi_reply;
+ if (scsiio_reply->SCSIState &
+ MPI2_SCSI_STATE_AUTOSENSE_VALID) {
+ sz = min_t(u32, SCSI_SENSE_BUFFERSIZE,
+ le32_to_cpu(scsiio_reply->SenseCount));
+ sense_data = mpt3sas_base_get_sense_buffer(ioc,
+ smid);
+ memcpy(ioc->ctl_cmds.sense, sense_data, sz);
+ }
+ }
+ }
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+ _ctl_display_some_debug(ioc, smid, "ctl_done", mpi_reply);
+#endif
+ ioc->ctl_cmds.status &= ~MPT3_CMD_PENDING;
+ complete(&ioc->ctl_cmds.done);
+ return 1;
+}
+
+/**
+ * _ctl_check_event_type - determines when an event needs logging
+ * @ioc: per adapter object
+ * @event: firmware event
+ *
+ * The bitmask in ioc->event_type[] indicates which events should be
+ * be saved in the driver event_log. This bitmask is set by application.
+ *
+ * Returns 1 when event should be captured, or zero means no match.
+ */
+static int
+_ctl_check_event_type(struct MPT3SAS_ADAPTER *ioc, u16 event)
+{
+ u16 i;
+ u32 desired_event;
+
+ if (event >= 128 || !event || !ioc->event_log)
+ return 0;
+
+ desired_event = (1 << (event % 32));
+ if (!desired_event)
+ desired_event = 1;
+ i = event / 32;
+ return desired_event & ioc->event_type[i];
+}
+
+/**
+ * mpt3sas_ctl_add_to_event_log - add event
+ * @ioc: per adapter object
+ * @mpi_reply: reply message frame
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_ctl_add_to_event_log(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventNotificationReply_t *mpi_reply)
+{
+ struct MPT3_IOCTL_EVENTS *event_log;
+ u16 event;
+ int i;
+ u32 sz, event_data_sz;
+ u8 send_aen = 0;
+
+ if (!ioc->event_log)
+ return;
+
+ event = le16_to_cpu(mpi_reply->Event);
+
+ if (_ctl_check_event_type(ioc, event)) {
+
+ /* insert entry into circular event_log */
+ i = ioc->event_context % MPT3SAS_CTL_EVENT_LOG_SIZE;
+ event_log = ioc->event_log;
+ event_log[i].event = event;
+ event_log[i].context = ioc->event_context++;
+
+ event_data_sz = le16_to_cpu(mpi_reply->EventDataLength)*4;
+ sz = min_t(u32, event_data_sz, MPT3_EVENT_DATA_SIZE);
+ memset(event_log[i].data, 0, MPT3_EVENT_DATA_SIZE);
+ memcpy(event_log[i].data, mpi_reply->EventData, sz);
+ send_aen = 1;
+ }
+
+ /* This aen_event_read_flag flag is set until the
+ * application has read the event log.
+ * For MPI2_EVENT_LOG_ENTRY_ADDED, we always notify.
+ */
+ if (event == MPI2_EVENT_LOG_ENTRY_ADDED ||
+ (send_aen && !ioc->aen_event_read_flag)) {
+ ioc->aen_event_read_flag = 1;
+ wake_up_interruptible(&ctl_poll_wait);
+ if (async_queue)
+ kill_fasync(&async_queue, SIGIO, POLL_IN);
+ }
+}
+
+/**
+ * mpt3sas_ctl_event_callback - firmware event handler (called at ISR time)
+ * @ioc: per adapter object
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: interrupt.
+ *
+ * This function merely adds a new work task into ioc->firmware_event_thread.
+ * The tasks are worked from _firmware_event_work in user context.
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+u8
+mpt3sas_ctl_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index,
+ u32 reply)
+{
+ Mpi2EventNotificationReply_t *mpi_reply;
+
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ mpt3sas_ctl_add_to_event_log(ioc, mpi_reply);
+ return 1;
+}
+
+/**
+ * _ctl_verify_adapter - validates ioc_number passed from application
+ * @ioc: per adapter object
+ * @iocpp: The ioc pointer is returned in this.
+ *
+ * Return (-1) means error, else ioc_number.
+ */
+static int
+_ctl_verify_adapter(int ioc_number, struct MPT3SAS_ADAPTER **iocpp)
+{
+ struct MPT3SAS_ADAPTER *ioc;
+
+ list_for_each_entry(ioc, &mpt3sas_ioc_list, list) {
+ if (ioc->id != ioc_number)
+ continue;
+ *iocpp = ioc;
+ return ioc_number;
+ }
+ *iocpp = NULL;
+ return -1;
+}
+
+/**
+ * mpt3sas_ctl_reset_handler - reset callback handler (for ctl)
+ * @ioc: per adapter object
+ * @reset_phase: phase
+ *
+ * The handler for doing any required cleanup or initialization.
+ *
+ * The reset phase can be MPT3_IOC_PRE_RESET, MPT3_IOC_AFTER_RESET,
+ * MPT3_IOC_DONE_RESET
+ */
+void
+mpt3sas_ctl_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase)
+{
+ int i;
+ u8 issue_reset;
+
+ switch (reset_phase) {
+ case MPT3_IOC_PRE_RESET:
+ dtmprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: MPT3_IOC_PRE_RESET\n", ioc->name, __func__));
+ for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) {
+ if (!(ioc->diag_buffer_status[i] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED))
+ continue;
+ if ((ioc->diag_buffer_status[i] &
+ MPT3_DIAG_BUFFER_IS_RELEASED))
+ continue;
+ mpt3sas_send_diag_release(ioc, i, &issue_reset);
+ }
+ break;
+ case MPT3_IOC_AFTER_RESET:
+ dtmprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: MPT3_IOC_AFTER_RESET\n", ioc->name, __func__));
+ if (ioc->ctl_cmds.status & MPT3_CMD_PENDING) {
+ ioc->ctl_cmds.status |= MPT3_CMD_RESET;
+ mpt3sas_base_free_smid(ioc, ioc->ctl_cmds.smid);
+ complete(&ioc->ctl_cmds.done);
+ }
+ break;
+ case MPT3_IOC_DONE_RESET:
+ dtmprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: MPT3_IOC_DONE_RESET\n", ioc->name, __func__));
+
+ for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) {
+ if (!(ioc->diag_buffer_status[i] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED))
+ continue;
+ if ((ioc->diag_buffer_status[i] &
+ MPT3_DIAG_BUFFER_IS_RELEASED))
+ continue;
+ ioc->diag_buffer_status[i] |=
+ MPT3_DIAG_BUFFER_IS_DIAG_RESET;
+ }
+ break;
+ }
+}
+
+/**
+ * _ctl_fasync -
+ * @fd -
+ * @filep -
+ * @mode -
+ *
+ * Called when application request fasyn callback handler.
+ */
+static int
+_ctl_fasync(int fd, struct file *filep, int mode)
+{
+ return fasync_helper(fd, filep, mode, &async_queue);
+}
+
+/**
+ * _ctl_release -
+ * @inode -
+ * @filep -
+ *
+ * Called when application releases the fasyn callback handler.
+ */
+static int
+_ctl_release(struct inode *inode, struct file *filep)
+{
+ return fasync_helper(-1, filep, 0, &async_queue);
+}
+
+/**
+ * _ctl_poll -
+ * @file -
+ * @wait -
+ *
+ */
+static unsigned int
+_ctl_poll(struct file *filep, poll_table *wait)
+{
+ struct MPT3SAS_ADAPTER *ioc;
+
+ poll_wait(filep, &ctl_poll_wait, wait);
+
+ list_for_each_entry(ioc, &mpt3sas_ioc_list, list) {
+ if (ioc->aen_event_read_flag)
+ return POLLIN | POLLRDNORM;
+ }
+ return 0;
+}
+
+/**
+ * _ctl_set_task_mid - assign an active smid to tm request
+ * @ioc: per adapter object
+ * @karg - (struct mpt3_ioctl_command)
+ * @tm_request - pointer to mf from user space
+ *
+ * Returns 0 when an smid if found, else fail.
+ * during failure, the reply frame is filled.
+ */
+static int
+_ctl_set_task_mid(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command *karg,
+ Mpi2SCSITaskManagementRequest_t *tm_request)
+{
+ u8 found = 0;
+ u16 i;
+ u16 handle;
+ struct scsi_cmnd *scmd;
+ struct MPT3SAS_DEVICE *priv_data;
+ unsigned long flags;
+ Mpi2SCSITaskManagementReply_t *tm_reply;
+ u32 sz;
+ u32 lun;
+ char *desc = NULL;
+
+ if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK)
+ desc = "abort_task";
+ else if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK)
+ desc = "query_task";
+ else
+ return 0;
+
+ lun = scsilun_to_int((struct scsi_lun *)tm_request->LUN);
+
+ handle = le16_to_cpu(tm_request->DevHandle);
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ for (i = ioc->scsiio_depth; i && !found; i--) {
+ scmd = ioc->scsi_lookup[i - 1].scmd;
+ if (scmd == NULL || scmd->device == NULL ||
+ scmd->device->hostdata == NULL)
+ continue;
+ if (lun != scmd->device->lun)
+ continue;
+ priv_data = scmd->device->hostdata;
+ if (priv_data->sas_target == NULL)
+ continue;
+ if (priv_data->sas_target->handle != handle)
+ continue;
+ tm_request->TaskMID = cpu_to_le16(ioc->scsi_lookup[i - 1].smid);
+ found = 1;
+ }
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+
+ if (!found) {
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: handle(0x%04x), lun(%d), no active mid!!\n",
+ ioc->name,
+ desc, le16_to_cpu(tm_request->DevHandle), lun));
+ tm_reply = ioc->ctl_cmds.reply;
+ tm_reply->DevHandle = tm_request->DevHandle;
+ tm_reply->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
+ tm_reply->TaskType = tm_request->TaskType;
+ tm_reply->MsgLength = sizeof(Mpi2SCSITaskManagementReply_t)/4;
+ tm_reply->VP_ID = tm_request->VP_ID;
+ tm_reply->VF_ID = tm_request->VF_ID;
+ sz = min_t(u32, karg->max_reply_bytes, ioc->reply_sz);
+ if (copy_to_user(karg->reply_frame_buf_ptr, ioc->ctl_cmds.reply,
+ sz))
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ return 1;
+ }
+
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: handle(0x%04x), lun(%d), task_mid(%d)\n", ioc->name,
+ desc, le16_to_cpu(tm_request->DevHandle), lun,
+ le16_to_cpu(tm_request->TaskMID)));
+ return 0;
+}
+
+/**
+ * _ctl_do_mpt_command - main handler for MPT3COMMAND opcode
+ * @ioc: per adapter object
+ * @karg - (struct mpt3_ioctl_command)
+ * @mf - pointer to mf in user space
+ */
+static long
+_ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg,
+ void __user *mf)
+{
+ MPI2RequestHeader_t *mpi_request = NULL, *request;
+ MPI2DefaultReply_t *mpi_reply;
+ u32 ioc_state;
+ u16 ioc_status;
+ u16 smid;
+ unsigned long timeout, timeleft;
+ u8 issue_reset;
+ u32 sz;
+ void *psge;
+ void *data_out = NULL;
+ dma_addr_t data_out_dma = 0;
+ size_t data_out_sz = 0;
+ void *data_in = NULL;
+ dma_addr_t data_in_dma = 0;
+ size_t data_in_sz = 0;
+ long ret;
+ u16 wait_state_count;
+
+ issue_reset = 0;
+
+ if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) {
+ pr_err(MPT3SAS_FMT "%s: ctl_cmd in use\n",
+ ioc->name, __func__);
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ wait_state_count = 0;
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ if (wait_state_count++ == 10) {
+ pr_err(MPT3SAS_FMT
+ "%s: failed due to ioc not operational\n",
+ ioc->name, __func__);
+ ret = -EFAULT;
+ goto out;
+ }
+ ssleep(1);
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ pr_info(MPT3SAS_FMT
+ "%s: waiting for operational state(count=%d)\n",
+ ioc->name,
+ __func__, wait_state_count);
+ }
+ if (wait_state_count)
+ pr_info(MPT3SAS_FMT "%s: ioc is operational\n",
+ ioc->name, __func__);
+
+ mpi_request = kzalloc(ioc->request_sz, GFP_KERNEL);
+ if (!mpi_request) {
+ pr_err(MPT3SAS_FMT
+ "%s: failed obtaining a memory for mpi_request\n",
+ ioc->name, __func__);
+ ret = -ENOMEM;
+ 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)) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__, __LINE__,
+ __func__);
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) {
+ smid = mpt3sas_base_get_smid_hpr(ioc, ioc->ctl_cb_idx);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ ret = -EAGAIN;
+ goto out;
+ }
+ } else {
+
+ smid = mpt3sas_base_get_smid_scsiio(ioc, ioc->ctl_cb_idx, NULL);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ ret = -EAGAIN;
+ goto out;
+ }
+ }
+
+ ret = 0;
+ ioc->ctl_cmds.status = MPT3_CMD_PENDING;
+ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
+ request = mpt3sas_base_get_msg_frame(ioc, smid);
+ memcpy(request, mpi_request, karg.data_sge_offset*4);
+ ioc->ctl_cmds.smid = smid;
+ data_out_sz = karg.data_out_size;
+ data_in_sz = karg.data_in_size;
+
+ if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
+ mpi_request->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) {
+ if (!le16_to_cpu(mpi_request->FunctionDependent1) ||
+ le16_to_cpu(mpi_request->FunctionDependent1) >
+ ioc->facts.MaxDevHandle) {
+ ret = -EINVAL;
+ mpt3sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+ }
+
+ /* obtain dma-able memory for data transfer */
+ if (data_out_sz) /* WRITE */ {
+ data_out = pci_alloc_consistent(ioc->pdev, data_out_sz,
+ &data_out_dma);
+ if (!data_out) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ ret = -ENOMEM;
+ mpt3sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+ if (copy_from_user(data_out, karg.data_out_buf_ptr,
+ data_out_sz)) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ ret = -EFAULT;
+ mpt3sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+ }
+
+ if (data_in_sz) /* READ */ {
+ data_in = pci_alloc_consistent(ioc->pdev, data_in_sz,
+ &data_in_dma);
+ if (!data_in) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ ret = -ENOMEM;
+ mpt3sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+ }
+
+ psge = (void *)request + (karg.data_sge_offset*4);
+
+ /* send command to firmware */
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+ _ctl_display_some_debug(ioc, smid, "ctl_request", NULL);
+#endif
+
+ init_completion(&ioc->ctl_cmds.done);
+ switch (mpi_request->Function) {
+ case MPI2_FUNCTION_SCSI_IO_REQUEST:
+ case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH:
+ {
+ Mpi2SCSIIORequest_t *scsiio_request =
+ (Mpi2SCSIIORequest_t *)request;
+ scsiio_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE;
+ scsiio_request->SenseBufferLowAddress =
+ mpt3sas_base_get_sense_buffer_dma(ioc, smid);
+ memset(ioc->ctl_cmds.sense, 0, SCSI_SENSE_BUFFERSIZE);
+ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz,
+ data_in_dma, data_in_sz);
+
+ if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST)
+ mpt3sas_base_put_smid_scsi_io(ioc, smid,
+ le16_to_cpu(mpi_request->FunctionDependent1));
+ else
+ mpt3sas_base_put_smid_default(ioc, smid);
+ break;
+ }
+ case MPI2_FUNCTION_SCSI_TASK_MGMT:
+ {
+ Mpi2SCSITaskManagementRequest_t *tm_request =
+ (Mpi2SCSITaskManagementRequest_t *)request;
+
+ dtmprintk(ioc, pr_info(MPT3SAS_FMT
+ "TASK_MGMT: handle(0x%04x), task_type(0x%02x)\n",
+ ioc->name,
+ le16_to_cpu(tm_request->DevHandle), tm_request->TaskType));
+
+ if (tm_request->TaskType ==
+ MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK ||
+ tm_request->TaskType ==
+ MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK) {
+ if (_ctl_set_task_mid(ioc, &karg, tm_request)) {
+ mpt3sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+ }
+
+ mpt3sas_scsih_set_tm_flag(ioc, le16_to_cpu(
+ tm_request->DevHandle));
+ ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz,
+ data_in_dma, data_in_sz);
+ mpt3sas_base_put_smid_hi_priority(ioc, smid);
+ break;
+ }
+ case MPI2_FUNCTION_SMP_PASSTHROUGH:
+ {
+ Mpi2SmpPassthroughRequest_t *smp_request =
+ (Mpi2SmpPassthroughRequest_t *)mpi_request;
+ u8 *data;
+
+ /* ioc determines which port to use */
+ smp_request->PhysicalPort = 0xFF;
+ if (smp_request->PassthroughFlags &
+ MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE)
+ data = (u8 *)&smp_request->SGL;
+ else {
+ if (unlikely(data_out == NULL)) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ mpt3sas_base_free_smid(ioc, smid);
+ ret = -EINVAL;
+ goto out;
+ }
+ data = data_out;
+ }
+
+ if (data[1] == 0x91 && (data[10] == 1 || data[10] == 2)) {
+ ioc->ioc_link_reset_in_progress = 1;
+ ioc->ignore_loginfos = 1;
+ }
+ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma,
+ data_in_sz);
+ mpt3sas_base_put_smid_default(ioc, smid);
+ break;
+ }
+ case MPI2_FUNCTION_SATA_PASSTHROUGH:
+ case MPI2_FUNCTION_FW_DOWNLOAD:
+ case MPI2_FUNCTION_FW_UPLOAD:
+ {
+ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma,
+ data_in_sz);
+ mpt3sas_base_put_smid_default(ioc, smid);
+ break;
+ }
+ case MPI2_FUNCTION_TOOLBOX:
+ {
+ Mpi2ToolboxCleanRequest_t *toolbox_request =
+ (Mpi2ToolboxCleanRequest_t *)mpi_request;
+
+ if (toolbox_request->Tool == MPI2_TOOLBOX_DIAGNOSTIC_CLI_TOOL) {
+ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz,
+ data_in_dma, data_in_sz);
+ } else {
+ ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz,
+ data_in_dma, data_in_sz);
+ }
+ mpt3sas_base_put_smid_default(ioc, smid);
+ break;
+ }
+ case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL:
+ {
+ Mpi2SasIoUnitControlRequest_t *sasiounit_request =
+ (Mpi2SasIoUnitControlRequest_t *)mpi_request;
+
+ if (sasiounit_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET
+ || sasiounit_request->Operation ==
+ MPI2_SAS_OP_PHY_LINK_RESET) {
+ ioc->ioc_link_reset_in_progress = 1;
+ ioc->ignore_loginfos = 1;
+ }
+ /* drop to default case for posting the request */
+ }
+ default:
+ ioc->build_sg_mpi(ioc, psge, data_out_dma, data_out_sz,
+ data_in_dma, data_in_sz);
+ mpt3sas_base_put_smid_default(ioc, smid);
+ break;
+ }
+
+ if (karg.timeout < MPT3_IOCTL_DEFAULT_TIMEOUT)
+ timeout = MPT3_IOCTL_DEFAULT_TIMEOUT;
+ else
+ timeout = karg.timeout;
+ timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done,
+ timeout*HZ);
+ if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) {
+ Mpi2SCSITaskManagementRequest_t *tm_request =
+ (Mpi2SCSITaskManagementRequest_t *)mpi_request;
+ mpt3sas_scsih_clear_tm_flag(ioc, le16_to_cpu(
+ tm_request->DevHandle));
+ mpt3sas_trigger_master(ioc, MASTER_TRIGGER_TASK_MANAGMENT);
+ } else if ((mpi_request->Function == MPI2_FUNCTION_SMP_PASSTHROUGH ||
+ mpi_request->Function == MPI2_FUNCTION_SAS_IO_UNIT_CONTROL) &&
+ ioc->ioc_link_reset_in_progress) {
+ ioc->ioc_link_reset_in_progress = 0;
+ ioc->ignore_loginfos = 0;
+ }
+ if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) {
+ pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name,
+ __func__);
+ _debug_dump_mf(mpi_request, karg.data_sge_offset);
+ if (!(ioc->ctl_cmds.status & MPT3_CMD_RESET))
+ issue_reset = 1;
+ goto issue_host_reset;
+ }
+
+ mpi_reply = ioc->ctl_cmds.reply;
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+ if (mpi_reply->Function == MPI2_FUNCTION_SCSI_TASK_MGMT &&
+ (ioc->logging_level & MPT_DEBUG_TM)) {
+ Mpi2SCSITaskManagementReply_t *tm_reply =
+ (Mpi2SCSITaskManagementReply_t *)mpi_reply;
+
+ pr_info(MPT3SAS_FMT "TASK_MGMT: " \
+ "IOCStatus(0x%04x), IOCLogInfo(0x%08x), "
+ "TerminationCount(0x%08x)\n", ioc->name,
+ le16_to_cpu(tm_reply->IOCStatus),
+ le32_to_cpu(tm_reply->IOCLogInfo),
+ le32_to_cpu(tm_reply->TerminationCount));
+ }
+#endif
+ /* copy out xdata to user */
+ if (data_in_sz) {
+ if (copy_to_user(karg.data_in_buf_ptr, data_in,
+ data_in_sz)) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ ret = -ENODATA;
+ goto out;
+ }
+ }
+
+ /* copy out reply message frame to user */
+ if (karg.max_reply_bytes) {
+ sz = min_t(u32, karg.max_reply_bytes, ioc->reply_sz);
+ if (copy_to_user(karg.reply_frame_buf_ptr, ioc->ctl_cmds.reply,
+ sz)) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ ret = -ENODATA;
+ goto out;
+ }
+ }
+
+ /* copy out sense to user */
+ if (karg.max_sense_bytes && (mpi_request->Function ==
+ MPI2_FUNCTION_SCSI_IO_REQUEST || mpi_request->Function ==
+ MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) {
+ sz = min_t(u32, karg.max_sense_bytes, SCSI_SENSE_BUFFERSIZE);
+ if (copy_to_user(karg.sense_data_ptr, ioc->ctl_cmds.sense,
+ sz)) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ ret = -ENODATA;
+ goto out;
+ }
+ }
+
+ issue_host_reset:
+ if (issue_reset) {
+ ret = -ENODATA;
+ if ((mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST ||
+ mpi_request->Function ==
+ MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH ||
+ mpi_request->Function == MPI2_FUNCTION_SATA_PASSTHROUGH)) {
+ pr_info(MPT3SAS_FMT "issue target reset: handle = (0x%04x)\n",
+ ioc->name,
+ le16_to_cpu(mpi_request->FunctionDependent1));
+ mpt3sas_halt_firmware(ioc);
+ mpt3sas_scsih_issue_tm(ioc,
+ le16_to_cpu(mpi_request->FunctionDependent1), 0, 0,
+ 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 30,
+ 0, TM_MUTEX_ON);
+ } else
+ mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ }
+
+ out:
+
+ /* free memory associated with sg buffers */
+ if (data_in)
+ pci_free_consistent(ioc->pdev, data_in_sz, data_in,
+ data_in_dma);
+
+ if (data_out)
+ pci_free_consistent(ioc->pdev, data_out_sz, data_out,
+ data_out_dma);
+
+ kfree(mpi_request);
+ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED;
+ return ret;
+}
+
+/**
+ * _ctl_getiocinfo - main handler for MPT3IOCINFO opcode
+ * @ioc: per adapter object
+ * @arg - user space buffer containing ioctl content
+ */
+static long
+_ctl_getiocinfo(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_ioctl_iocinfo karg;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name,
+ __func__));
+
+ memset(&karg, 0 , sizeof(karg));
+ karg.adapter_type = MPT3_IOCTL_INTERFACE_SAS3;
+ if (ioc->pfacts)
+ karg.port_number = ioc->pfacts[0].PortNumber;
+ karg.hw_rev = ioc->pdev->revision;
+ karg.pci_id = ioc->pdev->device;
+ karg.subsystem_device = ioc->pdev->subsystem_device;
+ karg.subsystem_vendor = ioc->pdev->subsystem_vendor;
+ karg.pci_information.u.bits.bus = ioc->pdev->bus->number;
+ karg.pci_information.u.bits.device = PCI_SLOT(ioc->pdev->devfn);
+ karg.pci_information.u.bits.function = PCI_FUNC(ioc->pdev->devfn);
+ karg.pci_information.segment_id = pci_domain_nr(ioc->pdev->bus);
+ karg.firmware_version = ioc->facts.FWVersion.Word;
+ strcpy(karg.driver_version, MPT3SAS_DRIVER_NAME);
+ strcat(karg.driver_version, "-");
+ strcat(karg.driver_version, MPT3SAS_DRIVER_VERSION);
+ karg.bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion);
+
+ if (copy_to_user(arg, &karg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/**
+ * _ctl_eventquery - main handler for MPT3EVENTQUERY opcode
+ * @ioc: per adapter object
+ * @arg - user space buffer containing ioctl content
+ */
+static long
+_ctl_eventquery(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_ioctl_eventquery karg;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name,
+ __func__));
+
+ karg.event_entries = MPT3SAS_CTL_EVENT_LOG_SIZE;
+ memcpy(karg.event_types, ioc->event_type,
+ MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32));
+
+ if (copy_to_user(arg, &karg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/**
+ * _ctl_eventenable - main handler for MPT3EVENTENABLE opcode
+ * @ioc: per adapter object
+ * @arg - user space buffer containing ioctl content
+ */
+static long
+_ctl_eventenable(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_ioctl_eventenable karg;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name,
+ __func__));
+
+ memcpy(ioc->event_type, karg.event_types,
+ MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32));
+ mpt3sas_base_validate_event_type(ioc, ioc->event_type);
+
+ if (ioc->event_log)
+ return 0;
+ /* initialize event_log */
+ ioc->event_context = 0;
+ ioc->aen_event_read_flag = 0;
+ ioc->event_log = kcalloc(MPT3SAS_CTL_EVENT_LOG_SIZE,
+ sizeof(struct MPT3_IOCTL_EVENTS), GFP_KERNEL);
+ if (!ioc->event_log) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/**
+ * _ctl_eventreport - main handler for MPT3EVENTREPORT opcode
+ * @ioc: per adapter object
+ * @arg - user space buffer containing ioctl content
+ */
+static long
+_ctl_eventreport(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_ioctl_eventreport karg;
+ u32 number_bytes, max_events, max;
+ struct mpt3_ioctl_eventreport __user *uarg = arg;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name,
+ __func__));
+
+ number_bytes = karg.hdr.max_data_size -
+ sizeof(struct mpt3_ioctl_header);
+ max_events = number_bytes/sizeof(struct MPT3_IOCTL_EVENTS);
+ max = min_t(u32, MPT3SAS_CTL_EVENT_LOG_SIZE, max_events);
+
+ /* If fewer than 1 event is requested, there must have
+ * been some type of error.
+ */
+ if (!max || !ioc->event_log)
+ return -ENODATA;
+
+ number_bytes = max * sizeof(struct MPT3_IOCTL_EVENTS);
+ if (copy_to_user(uarg->event_data, ioc->event_log, number_bytes)) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ /* reset flag so SIGIO can restart */
+ ioc->aen_event_read_flag = 0;
+ return 0;
+}
+
+/**
+ * _ctl_do_reset - main handler for MPT3HARDRESET opcode
+ * @ioc: per adapter object
+ * @arg - user space buffer containing ioctl content
+ */
+static long
+_ctl_do_reset(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_ioctl_diag_reset karg;
+ int retval;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ if (ioc->shost_recovery || ioc->pci_error_recovery ||
+ ioc->is_driver_loading)
+ return -EAGAIN;
+
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n", ioc->name,
+ __func__));
+
+ retval = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ pr_info(MPT3SAS_FMT "host reset: %s\n",
+ ioc->name, ((!retval) ? "SUCCESS" : "FAILED"));
+ return 0;
+}
+
+/**
+ * _ctl_btdh_search_sas_device - searching for sas device
+ * @ioc: per adapter object
+ * @btdh: btdh ioctl payload
+ */
+static int
+_ctl_btdh_search_sas_device(struct MPT3SAS_ADAPTER *ioc,
+ struct mpt3_ioctl_btdh_mapping *btdh)
+{
+ struct _sas_device *sas_device;
+ unsigned long flags;
+ int rc = 0;
+
+ if (list_empty(&ioc->sas_device_list))
+ return rc;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ list_for_each_entry(sas_device, &ioc->sas_device_list, list) {
+ if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF &&
+ btdh->handle == sas_device->handle) {
+ btdh->bus = sas_device->channel;
+ btdh->id = sas_device->id;
+ rc = 1;
+ goto out;
+ } else if (btdh->bus == sas_device->channel && btdh->id ==
+ sas_device->id && btdh->handle == 0xFFFF) {
+ btdh->handle = sas_device->handle;
+ rc = 1;
+ goto out;
+ }
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ return rc;
+}
+
+/**
+ * _ctl_btdh_search_raid_device - searching for raid device
+ * @ioc: per adapter object
+ * @btdh: btdh ioctl payload
+ */
+static int
+_ctl_btdh_search_raid_device(struct MPT3SAS_ADAPTER *ioc,
+ struct mpt3_ioctl_btdh_mapping *btdh)
+{
+ struct _raid_device *raid_device;
+ unsigned long flags;
+ int rc = 0;
+
+ if (list_empty(&ioc->raid_device_list))
+ return rc;
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
+ if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF &&
+ btdh->handle == raid_device->handle) {
+ btdh->bus = raid_device->channel;
+ btdh->id = raid_device->id;
+ rc = 1;
+ goto out;
+ } else if (btdh->bus == raid_device->channel && btdh->id ==
+ raid_device->id && btdh->handle == 0xFFFF) {
+ btdh->handle = raid_device->handle;
+ rc = 1;
+ goto out;
+ }
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ return rc;
+}
+
+/**
+ * _ctl_btdh_mapping - main handler for MPT3BTDHMAPPING opcode
+ * @ioc: per adapter object
+ * @arg - user space buffer containing ioctl content
+ */
+static long
+_ctl_btdh_mapping(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_ioctl_btdh_mapping karg;
+ int rc;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+ rc = _ctl_btdh_search_sas_device(ioc, &karg);
+ if (!rc)
+ _ctl_btdh_search_raid_device(ioc, &karg);
+
+ if (copy_to_user(arg, &karg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/**
+ * _ctl_diag_capability - return diag buffer capability
+ * @ioc: per adapter object
+ * @buffer_type: specifies either TRACE, SNAPSHOT, or EXTENDED
+ *
+ * returns 1 when diag buffer support is enabled in firmware
+ */
+static u8
+_ctl_diag_capability(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type)
+{
+ u8 rc = 0;
+
+ switch (buffer_type) {
+ case MPI2_DIAG_BUF_TYPE_TRACE:
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER)
+ rc = 1;
+ break;
+ case MPI2_DIAG_BUF_TYPE_SNAPSHOT:
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER)
+ rc = 1;
+ break;
+ case MPI2_DIAG_BUF_TYPE_EXTENDED:
+ if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER)
+ rc = 1;
+ }
+
+ return rc;
+}
+
+
+/**
+ * _ctl_diag_register_2 - wrapper for registering diag buffer support
+ * @ioc: per adapter object
+ * @diag_register: the diag_register struct passed in from user space
+ *
+ */
+static long
+_ctl_diag_register_2(struct MPT3SAS_ADAPTER *ioc,
+ struct mpt3_diag_register *diag_register)
+{
+ int rc, i;
+ void *request_data = NULL;
+ dma_addr_t request_data_dma;
+ u32 request_data_sz = 0;
+ Mpi2DiagBufferPostRequest_t *mpi_request;
+ Mpi2DiagBufferPostReply_t *mpi_reply;
+ u8 buffer_type;
+ unsigned long timeleft;
+ u16 smid;
+ u16 ioc_status;
+ u32 ioc_state;
+ u8 issue_reset = 0;
+
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ pr_err(MPT3SAS_FMT
+ "%s: failed due to ioc not operational\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) {
+ pr_err(MPT3SAS_FMT "%s: ctl_cmd in use\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ buffer_type = diag_register->buffer_type;
+ if (!_ctl_diag_capability(ioc, buffer_type)) {
+ pr_err(MPT3SAS_FMT
+ "%s: doesn't have capability for buffer_type(0x%02x)\n",
+ ioc->name, __func__, buffer_type);
+ return -EPERM;
+ }
+
+ if (ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) {
+ pr_err(MPT3SAS_FMT
+ "%s: already has a registered buffer for buffer_type(0x%02x)\n",
+ ioc->name, __func__,
+ buffer_type);
+ return -EINVAL;
+ }
+
+ if (diag_register->requested_buffer_size % 4) {
+ pr_err(MPT3SAS_FMT
+ "%s: the requested_buffer_size is not 4 byte aligned\n",
+ ioc->name, __func__);
+ return -EINVAL;
+ }
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->ctl_cb_idx);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ rc = 0;
+ ioc->ctl_cmds.status = MPT3_CMD_PENDING;
+ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->ctl_cmds.smid = smid;
+
+ request_data = ioc->diag_buffer[buffer_type];
+ request_data_sz = diag_register->requested_buffer_size;
+ ioc->unique_id[buffer_type] = diag_register->unique_id;
+ ioc->diag_buffer_status[buffer_type] = 0;
+ memcpy(ioc->product_specific[buffer_type],
+ diag_register->product_specific, MPT3_PRODUCT_SPECIFIC_DWORDS);
+ ioc->diagnostic_flags[buffer_type] = diag_register->diagnostic_flags;
+
+ if (request_data) {
+ request_data_dma = ioc->diag_buffer_dma[buffer_type];
+ if (request_data_sz != ioc->diag_buffer_sz[buffer_type]) {
+ pci_free_consistent(ioc->pdev,
+ ioc->diag_buffer_sz[buffer_type],
+ request_data, request_data_dma);
+ request_data = NULL;
+ }
+ }
+
+ if (request_data == NULL) {
+ ioc->diag_buffer_sz[buffer_type] = 0;
+ ioc->diag_buffer_dma[buffer_type] = 0;
+ request_data = pci_alloc_consistent(
+ ioc->pdev, request_data_sz, &request_data_dma);
+ if (request_data == NULL) {
+ pr_err(MPT3SAS_FMT "%s: failed allocating memory" \
+ " for diag buffers, requested size(%d)\n",
+ ioc->name, __func__, request_data_sz);
+ mpt3sas_base_free_smid(ioc, smid);
+ return -ENOMEM;
+ }
+ ioc->diag_buffer[buffer_type] = request_data;
+ ioc->diag_buffer_sz[buffer_type] = request_data_sz;
+ ioc->diag_buffer_dma[buffer_type] = request_data_dma;
+ }
+
+ mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST;
+ mpi_request->BufferType = diag_register->buffer_type;
+ mpi_request->Flags = cpu_to_le32(diag_register->diagnostic_flags);
+ mpi_request->BufferAddress = cpu_to_le64(request_data_dma);
+ mpi_request->BufferLength = cpu_to_le32(request_data_sz);
+ mpi_request->VF_ID = 0; /* TODO */
+ mpi_request->VP_ID = 0;
+
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: diag_buffer(0x%p), dma(0x%llx), sz(%d)\n",
+ ioc->name, __func__, request_data,
+ (unsigned long long)request_data_dma,
+ le32_to_cpu(mpi_request->BufferLength)));
+
+ for (i = 0; i < MPT3_PRODUCT_SPECIFIC_DWORDS; i++)
+ mpi_request->ProductSpecific[i] =
+ cpu_to_le32(ioc->product_specific[buffer_type][i]);
+
+ init_completion(&ioc->ctl_cmds.done);
+ mpt3sas_base_put_smid_default(ioc, smid);
+ timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done,
+ MPT3_IOCTL_DEFAULT_TIMEOUT*HZ);
+
+ if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) {
+ pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name,
+ __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2DiagBufferPostRequest_t)/4);
+ if (!(ioc->ctl_cmds.status & MPT3_CMD_RESET))
+ issue_reset = 1;
+ goto issue_host_reset;
+ }
+
+ /* process the completed Reply Message Frame */
+ if ((ioc->ctl_cmds.status & MPT3_CMD_REPLY_VALID) == 0) {
+ pr_err(MPT3SAS_FMT "%s: no reply message\n",
+ ioc->name, __func__);
+ rc = -EFAULT;
+ goto out;
+ }
+
+ mpi_reply = ioc->ctl_cmds.reply;
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
+ ioc->diag_buffer_status[buffer_type] |=
+ MPT3_DIAG_BUFFER_IS_REGISTERED;
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: success\n",
+ ioc->name, __func__));
+ } else {
+ pr_info(MPT3SAS_FMT
+ "%s: ioc_status(0x%04x) log_info(0x%08x)\n",
+ ioc->name, __func__,
+ ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo));
+ rc = -EFAULT;
+ }
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+
+ out:
+
+ if (rc && request_data)
+ pci_free_consistent(ioc->pdev, request_data_sz,
+ request_data, request_data_dma);
+
+ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED;
+ return rc;
+}
+
+/**
+ * mpt3sas_enable_diag_buffer - enabling diag_buffers support driver load time
+ * @ioc: per adapter object
+ * @bits_to_register: bitwise field where trace is bit 0, and snapshot is bit 1
+ *
+ * This is called when command line option diag_buffer_enable is enabled
+ * at driver load time.
+ */
+void
+mpt3sas_enable_diag_buffer(struct MPT3SAS_ADAPTER *ioc, u8 bits_to_register)
+{
+ struct mpt3_diag_register diag_register;
+
+ memset(&diag_register, 0, sizeof(struct mpt3_diag_register));
+
+ if (bits_to_register & 1) {
+ pr_info(MPT3SAS_FMT "registering trace buffer support\n",
+ ioc->name);
+ ioc->diag_trigger_master.MasterData =
+ (MASTER_TRIGGER_FW_FAULT + MASTER_TRIGGER_ADAPTER_RESET);
+ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE;
+ /* register for 2MB buffers */
+ diag_register.requested_buffer_size = 2 * (1024 * 1024);
+ diag_register.unique_id = 0x7075900;
+ _ctl_diag_register_2(ioc, &diag_register);
+ }
+
+ if (bits_to_register & 2) {
+ pr_info(MPT3SAS_FMT "registering snapshot buffer support\n",
+ ioc->name);
+ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_SNAPSHOT;
+ /* register for 2MB buffers */
+ diag_register.requested_buffer_size = 2 * (1024 * 1024);
+ diag_register.unique_id = 0x7075901;
+ _ctl_diag_register_2(ioc, &diag_register);
+ }
+
+ if (bits_to_register & 4) {
+ pr_info(MPT3SAS_FMT "registering extended buffer support\n",
+ ioc->name);
+ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_EXTENDED;
+ /* register for 2MB buffers */
+ diag_register.requested_buffer_size = 2 * (1024 * 1024);
+ diag_register.unique_id = 0x7075901;
+ _ctl_diag_register_2(ioc, &diag_register);
+ }
+}
+
+/**
+ * _ctl_diag_register - application register with driver
+ * @ioc: per adapter object
+ * @arg - user space buffer containing ioctl content
+ *
+ * This will allow the driver to setup any required buffers that will be
+ * needed by firmware to communicate with the driver.
+ */
+static long
+_ctl_diag_register(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_diag_register karg;
+ long rc;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ rc = _ctl_diag_register_2(ioc, &karg);
+ return rc;
+}
+
+/**
+ * _ctl_diag_unregister - application unregister with driver
+ * @ioc: per adapter object
+ * @arg - user space buffer containing ioctl content
+ *
+ * This will allow the driver to cleanup any memory allocated for diag
+ * messages and to free up any resources.
+ */
+static long
+_ctl_diag_unregister(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_diag_unregister karg;
+ void *request_data;
+ dma_addr_t request_data_dma;
+ u32 request_data_sz;
+ u8 buffer_type;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+ buffer_type = karg.unique_id & 0x000000ff;
+ if (!_ctl_diag_capability(ioc, buffer_type)) {
+ pr_err(MPT3SAS_FMT
+ "%s: doesn't have capability for buffer_type(0x%02x)\n",
+ ioc->name, __func__, buffer_type);
+ return -EPERM;
+ }
+
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ pr_err(MPT3SAS_FMT
+ "%s: buffer_type(0x%02x) is not registered\n",
+ ioc->name, __func__, buffer_type);
+ return -EINVAL;
+ }
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_RELEASED) == 0) {
+ pr_err(MPT3SAS_FMT
+ "%s: buffer_type(0x%02x) has not been released\n",
+ ioc->name, __func__, buffer_type);
+ return -EINVAL;
+ }
+
+ if (karg.unique_id != ioc->unique_id[buffer_type]) {
+ pr_err(MPT3SAS_FMT
+ "%s: unique_id(0x%08x) is not registered\n",
+ ioc->name, __func__, karg.unique_id);
+ return -EINVAL;
+ }
+
+ request_data = ioc->diag_buffer[buffer_type];
+ if (!request_data) {
+ pr_err(MPT3SAS_FMT
+ "%s: doesn't have memory allocated for buffer_type(0x%02x)\n",
+ ioc->name, __func__, buffer_type);
+ return -ENOMEM;
+ }
+
+ request_data_sz = ioc->diag_buffer_sz[buffer_type];
+ request_data_dma = ioc->diag_buffer_dma[buffer_type];
+ pci_free_consistent(ioc->pdev, request_data_sz,
+ request_data, request_data_dma);
+ ioc->diag_buffer[buffer_type] = NULL;
+ ioc->diag_buffer_status[buffer_type] = 0;
+ return 0;
+}
+
+/**
+ * _ctl_diag_query - query relevant info associated with diag buffers
+ * @ioc: per adapter object
+ * @arg - user space buffer containing ioctl content
+ *
+ * The application will send only buffer_type and unique_id. Driver will
+ * inspect unique_id first, if valid, fill in all the info. If unique_id is
+ * 0x00, the driver will return info specified by Buffer Type.
+ */
+static long
+_ctl_diag_query(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_diag_query karg;
+ void *request_data;
+ int i;
+ u8 buffer_type;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+ karg.application_flags = 0;
+ buffer_type = karg.buffer_type;
+
+ if (!_ctl_diag_capability(ioc, buffer_type)) {
+ pr_err(MPT3SAS_FMT
+ "%s: doesn't have capability for buffer_type(0x%02x)\n",
+ ioc->name, __func__, buffer_type);
+ return -EPERM;
+ }
+
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ pr_err(MPT3SAS_FMT
+ "%s: buffer_type(0x%02x) is not registered\n",
+ ioc->name, __func__, buffer_type);
+ return -EINVAL;
+ }
+
+ if (karg.unique_id & 0xffffff00) {
+ if (karg.unique_id != ioc->unique_id[buffer_type]) {
+ pr_err(MPT3SAS_FMT
+ "%s: unique_id(0x%08x) is not registered\n",
+ ioc->name, __func__, karg.unique_id);
+ return -EINVAL;
+ }
+ }
+
+ request_data = ioc->diag_buffer[buffer_type];
+ if (!request_data) {
+ pr_err(MPT3SAS_FMT
+ "%s: doesn't have buffer for buffer_type(0x%02x)\n",
+ ioc->name, __func__, buffer_type);
+ return -ENOMEM;
+ }
+
+ if (ioc->diag_buffer_status[buffer_type] & MPT3_DIAG_BUFFER_IS_RELEASED)
+ karg.application_flags = (MPT3_APP_FLAGS_APP_OWNED |
+ MPT3_APP_FLAGS_BUFFER_VALID);
+ else
+ karg.application_flags = (MPT3_APP_FLAGS_APP_OWNED |
+ MPT3_APP_FLAGS_BUFFER_VALID |
+ MPT3_APP_FLAGS_FW_BUFFER_ACCESS);
+
+ for (i = 0; i < MPT3_PRODUCT_SPECIFIC_DWORDS; i++)
+ karg.product_specific[i] =
+ ioc->product_specific[buffer_type][i];
+
+ karg.total_buffer_size = ioc->diag_buffer_sz[buffer_type];
+ karg.driver_added_buffer_size = 0;
+ karg.unique_id = ioc->unique_id[buffer_type];
+ karg.diagnostic_flags = ioc->diagnostic_flags[buffer_type];
+
+ if (copy_to_user(arg, &karg, sizeof(struct mpt3_diag_query))) {
+ pr_err(MPT3SAS_FMT
+ "%s: unable to write mpt3_diag_query data @ %p\n",
+ ioc->name, __func__, arg);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/**
+ * mpt3sas_send_diag_release - Diag Release Message
+ * @ioc: per adapter object
+ * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED
+ * @issue_reset - specifies whether host reset is required.
+ *
+ */
+int
+mpt3sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type,
+ u8 *issue_reset)
+{
+ Mpi2DiagReleaseRequest_t *mpi_request;
+ Mpi2DiagReleaseReply_t *mpi_reply;
+ u16 smid;
+ u16 ioc_status;
+ u32 ioc_state;
+ int rc;
+ unsigned long timeleft;
+
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+ rc = 0;
+ *issue_reset = 0;
+
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ if (ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED)
+ ioc->diag_buffer_status[buffer_type] |=
+ MPT3_DIAG_BUFFER_IS_RELEASED;
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: skipping due to FAULT state\n", ioc->name,
+ __func__));
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) {
+ pr_err(MPT3SAS_FMT "%s: ctl_cmd in use\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->ctl_cb_idx);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ ioc->ctl_cmds.status = MPT3_CMD_PENDING;
+ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->ctl_cmds.smid = smid;
+
+ mpi_request->Function = MPI2_FUNCTION_DIAG_RELEASE;
+ mpi_request->BufferType = buffer_type;
+ mpi_request->VF_ID = 0; /* TODO */
+ mpi_request->VP_ID = 0;
+
+ init_completion(&ioc->ctl_cmds.done);
+ mpt3sas_base_put_smid_default(ioc, smid);
+ timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done,
+ MPT3_IOCTL_DEFAULT_TIMEOUT*HZ);
+
+ if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) {
+ pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name,
+ __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2DiagReleaseRequest_t)/4);
+ if (!(ioc->ctl_cmds.status & MPT3_CMD_RESET))
+ *issue_reset = 1;
+ rc = -EFAULT;
+ goto out;
+ }
+
+ /* process the completed Reply Message Frame */
+ if ((ioc->ctl_cmds.status & MPT3_CMD_REPLY_VALID) == 0) {
+ pr_err(MPT3SAS_FMT "%s: no reply message\n",
+ ioc->name, __func__);
+ rc = -EFAULT;
+ goto out;
+ }
+
+ mpi_reply = ioc->ctl_cmds.reply;
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
+ ioc->diag_buffer_status[buffer_type] |=
+ MPT3_DIAG_BUFFER_IS_RELEASED;
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: success\n",
+ ioc->name, __func__));
+ } else {
+ pr_info(MPT3SAS_FMT
+ "%s: ioc_status(0x%04x) log_info(0x%08x)\n",
+ ioc->name, __func__,
+ ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo));
+ rc = -EFAULT;
+ }
+
+ out:
+ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED;
+ return rc;
+}
+
+/**
+ * _ctl_diag_release - request to send Diag Release Message to firmware
+ * @arg - user space buffer containing ioctl content
+ *
+ * This allows ownership of the specified buffer to returned to the driver,
+ * allowing an application to read the buffer without fear that firmware is
+ * overwritting information in the buffer.
+ */
+static long
+_ctl_diag_release(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_diag_release karg;
+ void *request_data;
+ int rc;
+ u8 buffer_type;
+ u8 issue_reset = 0;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+ buffer_type = karg.unique_id & 0x000000ff;
+ if (!_ctl_diag_capability(ioc, buffer_type)) {
+ pr_err(MPT3SAS_FMT
+ "%s: doesn't have capability for buffer_type(0x%02x)\n",
+ ioc->name, __func__, buffer_type);
+ return -EPERM;
+ }
+
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ pr_err(MPT3SAS_FMT
+ "%s: buffer_type(0x%02x) is not registered\n",
+ ioc->name, __func__, buffer_type);
+ return -EINVAL;
+ }
+
+ if (karg.unique_id != ioc->unique_id[buffer_type]) {
+ pr_err(MPT3SAS_FMT
+ "%s: unique_id(0x%08x) is not registered\n",
+ ioc->name, __func__, karg.unique_id);
+ return -EINVAL;
+ }
+
+ if (ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_RELEASED) {
+ pr_err(MPT3SAS_FMT
+ "%s: buffer_type(0x%02x) is already released\n",
+ ioc->name, __func__,
+ buffer_type);
+ return 0;
+ }
+
+ request_data = ioc->diag_buffer[buffer_type];
+
+ if (!request_data) {
+ pr_err(MPT3SAS_FMT
+ "%s: doesn't have memory allocated for buffer_type(0x%02x)\n",
+ ioc->name, __func__, buffer_type);
+ return -ENOMEM;
+ }
+
+ /* buffers were released by due to host reset */
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_DIAG_RESET)) {
+ ioc->diag_buffer_status[buffer_type] |=
+ MPT3_DIAG_BUFFER_IS_RELEASED;
+ ioc->diag_buffer_status[buffer_type] &=
+ ~MPT3_DIAG_BUFFER_IS_DIAG_RESET;
+ pr_err(MPT3SAS_FMT
+ "%s: buffer_type(0x%02x) was released due to host reset\n",
+ ioc->name, __func__, buffer_type);
+ return 0;
+ }
+
+ rc = mpt3sas_send_diag_release(ioc, buffer_type, &issue_reset);
+
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+
+ return rc;
+}
+
+/**
+ * _ctl_diag_read_buffer - request for copy of the diag buffer
+ * @ioc: per adapter object
+ * @arg - user space buffer containing ioctl content
+ */
+static long
+_ctl_diag_read_buffer(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
+{
+ struct mpt3_diag_read_buffer karg;
+ struct mpt3_diag_read_buffer __user *uarg = arg;
+ void *request_data, *diag_data;
+ Mpi2DiagBufferPostRequest_t *mpi_request;
+ Mpi2DiagBufferPostReply_t *mpi_reply;
+ int rc, i;
+ u8 buffer_type;
+ unsigned long timeleft, request_size, copy_size;
+ u16 smid;
+ u16 ioc_status;
+ u8 issue_reset = 0;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+ __func__));
+
+ buffer_type = karg.unique_id & 0x000000ff;
+ if (!_ctl_diag_capability(ioc, buffer_type)) {
+ pr_err(MPT3SAS_FMT
+ "%s: doesn't have capability for buffer_type(0x%02x)\n",
+ ioc->name, __func__, buffer_type);
+ return -EPERM;
+ }
+
+ if (karg.unique_id != ioc->unique_id[buffer_type]) {
+ pr_err(MPT3SAS_FMT
+ "%s: unique_id(0x%08x) is not registered\n",
+ ioc->name, __func__, karg.unique_id);
+ return -EINVAL;
+ }
+
+ request_data = ioc->diag_buffer[buffer_type];
+ if (!request_data) {
+ pr_err(MPT3SAS_FMT
+ "%s: doesn't have buffer for buffer_type(0x%02x)\n",
+ ioc->name, __func__, buffer_type);
+ return -ENOMEM;
+ }
+
+ request_size = ioc->diag_buffer_sz[buffer_type];
+
+ if ((karg.starting_offset % 4) || (karg.bytes_to_read % 4)) {
+ pr_err(MPT3SAS_FMT "%s: either the starting_offset " \
+ "or bytes_to_read are not 4 byte aligned\n", ioc->name,
+ __func__);
+ return -EINVAL;
+ }
+
+ if (karg.starting_offset > request_size)
+ return -EINVAL;
+
+ diag_data = (void *)(request_data + karg.starting_offset);
+ dctlprintk(ioc, pr_info(MPT3SAS_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, copy_size)) {
+ pr_err(MPT3SAS_FMT
+ "%s: Unable to write mpt_diag_read_buffer_t data @ %p\n",
+ ioc->name, __func__, diag_data);
+ return -EFAULT;
+ }
+
+ if ((karg.flags & MPT3_FLAGS_REREGISTER) == 0)
+ return 0;
+
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: Reregister buffer_type(0x%02x)\n",
+ ioc->name, __func__, buffer_type));
+ if ((ioc->diag_buffer_status[buffer_type] &
+ MPT3_DIAG_BUFFER_IS_RELEASED) == 0) {
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: buffer_type(0x%02x) is still registered\n",
+ ioc->name, __func__, buffer_type));
+ return 0;
+ }
+ /* Get a free request frame and save the message context.
+ */
+
+ if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) {
+ pr_err(MPT3SAS_FMT "%s: ctl_cmd in use\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->ctl_cb_idx);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ rc = 0;
+ ioc->ctl_cmds.status = MPT3_CMD_PENDING;
+ memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz);
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->ctl_cmds.smid = smid;
+
+ mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST;
+ mpi_request->BufferType = buffer_type;
+ mpi_request->BufferLength =
+ cpu_to_le32(ioc->diag_buffer_sz[buffer_type]);
+ mpi_request->BufferAddress =
+ cpu_to_le64(ioc->diag_buffer_dma[buffer_type]);
+ for (i = 0; i < MPT3_PRODUCT_SPECIFIC_DWORDS; i++)
+ mpi_request->ProductSpecific[i] =
+ cpu_to_le32(ioc->product_specific[buffer_type][i]);
+ mpi_request->VF_ID = 0; /* TODO */
+ mpi_request->VP_ID = 0;
+
+ init_completion(&ioc->ctl_cmds.done);
+ mpt3sas_base_put_smid_default(ioc, smid);
+ timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done,
+ MPT3_IOCTL_DEFAULT_TIMEOUT*HZ);
+
+ if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) {
+ pr_err(MPT3SAS_FMT "%s: timeout\n", ioc->name,
+ __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2DiagBufferPostRequest_t)/4);
+ if (!(ioc->ctl_cmds.status & MPT3_CMD_RESET))
+ issue_reset = 1;
+ goto issue_host_reset;
+ }
+
+ /* process the completed Reply Message Frame */
+ if ((ioc->ctl_cmds.status & MPT3_CMD_REPLY_VALID) == 0) {
+ pr_err(MPT3SAS_FMT "%s: no reply message\n",
+ ioc->name, __func__);
+ rc = -EFAULT;
+ goto out;
+ }
+
+ mpi_reply = ioc->ctl_cmds.reply;
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
+
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
+ ioc->diag_buffer_status[buffer_type] |=
+ MPT3_DIAG_BUFFER_IS_REGISTERED;
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT "%s: success\n",
+ ioc->name, __func__));
+ } else {
+ pr_info(MPT3SAS_FMT
+ "%s: ioc_status(0x%04x) log_info(0x%08x)\n",
+ ioc->name, __func__,
+ ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo));
+ rc = -EFAULT;
+ }
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+
+ out:
+
+ ioc->ctl_cmds.status = MPT3_CMD_NOT_USED;
+ return rc;
+}
+
+
+
+#ifdef CONFIG_COMPAT
+/**
+ * _ctl_compat_mpt_command - convert 32bit pointers to 64bit.
+ * @ioc: per adapter object
+ * @cmd - ioctl opcode
+ * @arg - (struct mpt3_ioctl_command32)
+ *
+ * MPT3COMMAND32 - Handle 32bit applications running on 64bit os.
+ */
+static long
+_ctl_compat_mpt_command(struct MPT3SAS_ADAPTER *ioc, unsigned cmd,
+ void __user *arg)
+{
+ struct mpt3_ioctl_command32 karg32;
+ struct mpt3_ioctl_command32 __user *uarg;
+ struct mpt3_ioctl_command karg;
+
+ if (_IOC_SIZE(cmd) != sizeof(struct mpt3_ioctl_command32))
+ return -EINVAL;
+
+ uarg = (struct mpt3_ioctl_command32 __user *) arg;
+
+ if (copy_from_user(&karg32, (char __user *)arg, sizeof(karg32))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ memset(&karg, 0, sizeof(struct mpt3_ioctl_command));
+ karg.hdr.ioc_number = karg32.hdr.ioc_number;
+ karg.hdr.port_number = karg32.hdr.port_number;
+ karg.hdr.max_data_size = karg32.hdr.max_data_size;
+ karg.timeout = karg32.timeout;
+ karg.max_reply_bytes = karg32.max_reply_bytes;
+ karg.data_in_size = karg32.data_in_size;
+ karg.data_out_size = karg32.data_out_size;
+ karg.max_sense_bytes = karg32.max_sense_bytes;
+ karg.data_sge_offset = karg32.data_sge_offset;
+ karg.reply_frame_buf_ptr = compat_ptr(karg32.reply_frame_buf_ptr);
+ karg.data_in_buf_ptr = compat_ptr(karg32.data_in_buf_ptr);
+ karg.data_out_buf_ptr = compat_ptr(karg32.data_out_buf_ptr);
+ karg.sense_data_ptr = compat_ptr(karg32.sense_data_ptr);
+ return _ctl_do_mpt_command(ioc, karg, &uarg->mf);
+}
+#endif
+
+/**
+ * _ctl_ioctl_main - main ioctl entry point
+ * @file - (struct file)
+ * @cmd - ioctl opcode
+ * @arg -
+ * compat - handles 32 bit applications in 64bit os
+ */
+static long
+_ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg,
+ u8 compat)
+{
+ struct MPT3SAS_ADAPTER *ioc;
+ struct mpt3_ioctl_header ioctl_header;
+ enum block_state state;
+ long ret = -EINVAL;
+
+ /* get IOCTL header */
+ if (copy_from_user(&ioctl_header, (char __user *)arg,
+ sizeof(struct mpt3_ioctl_header))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+
+ if (_ctl_verify_adapter(ioctl_header.ioc_number, &ioc) == -1 || !ioc)
+ return -ENODEV;
+
+ if (ioc->shost_recovery || ioc->pci_error_recovery ||
+ ioc->is_driver_loading)
+ return -EAGAIN;
+
+ state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING : BLOCKING;
+ if (state == NON_BLOCKING) {
+ if (!mutex_trylock(&ioc->ctl_cmds.mutex))
+ return -EAGAIN;
+ } else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex))
+ return -ERESTARTSYS;
+
+
+ switch (cmd) {
+ case MPT3IOCINFO:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_iocinfo))
+ ret = _ctl_getiocinfo(ioc, arg);
+ break;
+#ifdef CONFIG_COMPAT
+ case MPT3COMMAND32:
+#endif
+ case MPT3COMMAND:
+ {
+ struct mpt3_ioctl_command __user *uarg;
+ struct mpt3_ioctl_command karg;
+
+#ifdef CONFIG_COMPAT
+ if (compat) {
+ ret = _ctl_compat_mpt_command(ioc, cmd, arg);
+ break;
+ }
+#endif
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ pr_err("failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ ret = -EFAULT;
+ break;
+ }
+
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_command)) {
+ uarg = arg;
+ ret = _ctl_do_mpt_command(ioc, karg, &uarg->mf);
+ }
+ break;
+ }
+ case MPT3EVENTQUERY:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_eventquery))
+ ret = _ctl_eventquery(ioc, arg);
+ break;
+ case MPT3EVENTENABLE:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_eventenable))
+ ret = _ctl_eventenable(ioc, arg);
+ break;
+ case MPT3EVENTREPORT:
+ ret = _ctl_eventreport(ioc, arg);
+ break;
+ case MPT3HARDRESET:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_diag_reset))
+ ret = _ctl_do_reset(ioc, arg);
+ break;
+ case MPT3BTDHMAPPING:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_ioctl_btdh_mapping))
+ ret = _ctl_btdh_mapping(ioc, arg);
+ break;
+ case MPT3DIAGREGISTER:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_register))
+ ret = _ctl_diag_register(ioc, arg);
+ break;
+ case MPT3DIAGUNREGISTER:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_unregister))
+ ret = _ctl_diag_unregister(ioc, arg);
+ break;
+ case MPT3DIAGQUERY:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_query))
+ ret = _ctl_diag_query(ioc, arg);
+ break;
+ case MPT3DIAGRELEASE:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_release))
+ ret = _ctl_diag_release(ioc, arg);
+ break;
+ case MPT3DIAGREADBUFFER:
+ if (_IOC_SIZE(cmd) == sizeof(struct mpt3_diag_read_buffer))
+ ret = _ctl_diag_read_buffer(ioc, arg);
+ break;
+ default:
+ dctlprintk(ioc, pr_info(MPT3SAS_FMT
+ "unsupported ioctl opcode(0x%08x)\n", ioc->name, cmd));
+ break;
+ }
+
+ mutex_unlock(&ioc->ctl_cmds.mutex);
+ return ret;
+}
+
+/**
+ * _ctl_ioctl - main ioctl entry point (unlocked)
+ * @file - (struct file)
+ * @cmd - ioctl opcode
+ * @arg -
+ */
+static long
+_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ long ret;
+
+ ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 0);
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+/**
+ * _ctl_ioctl_compat - main ioctl entry point (compat)
+ * @file -
+ * @cmd -
+ * @arg -
+ *
+ * This routine handles 32 bit applications in 64bit os.
+ */
+static long
+_ctl_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg)
+{
+ long ret;
+
+ ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 1);
+ return ret;
+}
+#endif
+
+/* scsi host attributes */
+/**
+ * _ctl_version_fw_show - firmware version
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_version_fw_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n",
+ (ioc->facts.FWVersion.Word & 0xFF000000) >> 24,
+ (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16,
+ (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8,
+ ioc->facts.FWVersion.Word & 0x000000FF);
+}
+static DEVICE_ATTR(version_fw, S_IRUGO, _ctl_version_fw_show, NULL);
+
+/**
+ * _ctl_version_bios_show - bios version
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_version_bios_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ u32 version = le32_to_cpu(ioc->bios_pg3.BiosVersion);
+
+ return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n",
+ (version & 0xFF000000) >> 24,
+ (version & 0x00FF0000) >> 16,
+ (version & 0x0000FF00) >> 8,
+ version & 0x000000FF);
+}
+static DEVICE_ATTR(version_bios, S_IRUGO, _ctl_version_bios_show, NULL);
+
+/**
+ * _ctl_version_mpi_show - MPI (message passing interface) version
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_version_mpi_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%03x.%02x\n",
+ ioc->facts.MsgVersion, ioc->facts.HeaderVersion >> 8);
+}
+static DEVICE_ATTR(version_mpi, S_IRUGO, _ctl_version_mpi_show, NULL);
+
+/**
+ * _ctl_version_product_show - product name
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_version_product_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.ChipName);
+}
+static DEVICE_ATTR(version_product, S_IRUGO, _ctl_version_product_show, NULL);
+
+/**
+ * _ctl_version_nvdata_persistent_show - ndvata persistent version
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_version_nvdata_persistent_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%08xh\n",
+ le32_to_cpu(ioc->iounit_pg0.NvdataVersionPersistent.Word));
+}
+static DEVICE_ATTR(version_nvdata_persistent, S_IRUGO,
+ _ctl_version_nvdata_persistent_show, NULL);
+
+/**
+ * _ctl_version_nvdata_default_show - nvdata default version
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_version_nvdata_default_show(struct device *cdev, struct device_attribute
+ *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%08xh\n",
+ le32_to_cpu(ioc->iounit_pg0.NvdataVersionDefault.Word));
+}
+static DEVICE_ATTR(version_nvdata_default, S_IRUGO,
+ _ctl_version_nvdata_default_show, NULL);
+
+/**
+ * _ctl_board_name_show - board name
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_board_name_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardName);
+}
+static DEVICE_ATTR(board_name, S_IRUGO, _ctl_board_name_show, NULL);
+
+/**
+ * _ctl_board_assembly_show - board assembly name
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_board_assembly_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardAssembly);
+}
+static DEVICE_ATTR(board_assembly, S_IRUGO, _ctl_board_assembly_show, NULL);
+
+/**
+ * _ctl_board_tracer_show - board tracer number
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_board_tracer_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardTracerNumber);
+}
+static DEVICE_ATTR(board_tracer, S_IRUGO, _ctl_board_tracer_show, NULL);
+
+/**
+ * _ctl_io_delay_show - io missing delay
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * This is for firmware implemention for deboucing device
+ * removal events.
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_io_delay_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->io_missing_delay);
+}
+static DEVICE_ATTR(io_delay, S_IRUGO, _ctl_io_delay_show, NULL);
+
+/**
+ * _ctl_device_delay_show - device missing delay
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * This is for firmware implemention for deboucing device
+ * removal events.
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_device_delay_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->device_missing_delay);
+}
+static DEVICE_ATTR(device_delay, S_IRUGO, _ctl_device_delay_show, NULL);
+
+/**
+ * _ctl_fw_queue_depth_show - global credits
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * This is firmware queue depth limit
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_fw_queue_depth_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->facts.RequestCredit);
+}
+static DEVICE_ATTR(fw_queue_depth, S_IRUGO, _ctl_fw_queue_depth_show, NULL);
+
+/**
+ * _ctl_sas_address_show - sas address
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * This is the controller sas address
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_host_sas_address_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "0x%016llx\n",
+ (unsigned long long)ioc->sas_hba.sas_address);
+}
+static DEVICE_ATTR(host_sas_address, S_IRUGO,
+ _ctl_host_sas_address_show, NULL);
+
+/**
+ * _ctl_logging_level_show - logging level
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+_ctl_logging_level_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%08xh\n", ioc->logging_level);
+}
+static ssize_t
+_ctl_logging_level_store(struct device *cdev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ int val = 0;
+
+ if (sscanf(buf, "%x", &val) != 1)
+ return -EINVAL;
+
+ ioc->logging_level = val;
+ pr_info(MPT3SAS_FMT "logging_level=%08xh\n", ioc->name,
+ ioc->logging_level);
+ return strlen(buf);
+}
+static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR, _ctl_logging_level_show,
+ _ctl_logging_level_store);
+
+/**
+ * _ctl_fwfault_debug_show - show/store fwfault_debug
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * mpt3sas_fwfault_debug is command line option
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+_ctl_fwfault_debug_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ioc->fwfault_debug);
+}
+static ssize_t
+_ctl_fwfault_debug_store(struct device *cdev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ int val = 0;
+
+ if (sscanf(buf, "%d", &val) != 1)
+ return -EINVAL;
+
+ ioc->fwfault_debug = val;
+ pr_info(MPT3SAS_FMT "fwfault_debug=%d\n", ioc->name,
+ ioc->fwfault_debug);
+ return strlen(buf);
+}
+static DEVICE_ATTR(fwfault_debug, S_IRUGO | S_IWUSR,
+ _ctl_fwfault_debug_show, _ctl_fwfault_debug_store);
+
+/**
+ * _ctl_ioc_reset_count_show - ioc reset count
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * This is firmware queue depth limit
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_ioc_reset_count_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ioc->ioc_reset_count);
+}
+static DEVICE_ATTR(ioc_reset_count, S_IRUGO, _ctl_ioc_reset_count_show, NULL);
+
+/**
+ * _ctl_ioc_reply_queue_count_show - number of reply queues
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * This is number of reply queues
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_ioc_reply_queue_count_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ u8 reply_queue_count;
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ if ((ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX) && ioc->msix_enable)
+ reply_queue_count = ioc->reply_queue_count;
+ else
+ reply_queue_count = 1;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", reply_queue_count);
+}
+static DEVICE_ATTR(reply_queue_count, S_IRUGO, _ctl_ioc_reply_queue_count_show,
+ NULL);
+
+struct DIAG_BUFFER_START {
+ __le32 Size;
+ __le32 DiagVersion;
+ u8 BufferType;
+ u8 Reserved[3];
+ __le32 Reserved1;
+ __le32 Reserved2;
+ __le32 Reserved3;
+};
+
+/**
+ * _ctl_host_trace_buffer_size_show - host buffer size (trace only)
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_host_trace_buffer_size_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ u32 size = 0;
+ struct DIAG_BUFFER_START *request_data;
+
+ if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) {
+ pr_err(MPT3SAS_FMT
+ "%s: host_trace_buffer is not registered\n",
+ ioc->name, __func__);
+ return 0;
+ }
+
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ pr_err(MPT3SAS_FMT
+ "%s: host_trace_buffer is not registered\n",
+ ioc->name, __func__);
+ return 0;
+ }
+
+ request_data = (struct DIAG_BUFFER_START *)
+ ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE];
+ if ((le32_to_cpu(request_data->DiagVersion) == 0x00000000 ||
+ le32_to_cpu(request_data->DiagVersion) == 0x01000000 ||
+ le32_to_cpu(request_data->DiagVersion) == 0x01010000) &&
+ le32_to_cpu(request_data->Reserved3) == 0x4742444c)
+ size = le32_to_cpu(request_data->Size);
+
+ ioc->ring_buffer_sz = size;
+ return snprintf(buf, PAGE_SIZE, "%d\n", size);
+}
+static DEVICE_ATTR(host_trace_buffer_size, S_IRUGO,
+ _ctl_host_trace_buffer_size_show, NULL);
+
+/**
+ * _ctl_host_trace_buffer_show - firmware ring buffer (trace only)
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ *
+ * You will only be able to read 4k bytes of ring buffer at a time.
+ * In order to read beyond 4k bytes, you will have to write out the
+ * offset to the same attribute, it will move the pointer.
+ */
+static ssize_t
+_ctl_host_trace_buffer_show(struct device *cdev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ void *request_data;
+ u32 size;
+
+ if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) {
+ pr_err(MPT3SAS_FMT
+ "%s: host_trace_buffer is not registered\n",
+ ioc->name, __func__);
+ return 0;
+ }
+
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ pr_err(MPT3SAS_FMT
+ "%s: host_trace_buffer is not registered\n",
+ ioc->name, __func__);
+ return 0;
+ }
+
+ if (ioc->ring_buffer_offset > ioc->ring_buffer_sz)
+ return 0;
+
+ size = ioc->ring_buffer_sz - ioc->ring_buffer_offset;
+ size = (size >= PAGE_SIZE) ? (PAGE_SIZE - 1) : size;
+ request_data = ioc->diag_buffer[0] + ioc->ring_buffer_offset;
+ memcpy(buf, request_data, size);
+ return size;
+}
+
+static ssize_t
+_ctl_host_trace_buffer_store(struct device *cdev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ int val = 0;
+
+ if (sscanf(buf, "%d", &val) != 1)
+ return -EINVAL;
+
+ ioc->ring_buffer_offset = val;
+ return strlen(buf);
+}
+static DEVICE_ATTR(host_trace_buffer, S_IRUGO | S_IWUSR,
+ _ctl_host_trace_buffer_show, _ctl_host_trace_buffer_store);
+
+
+/*****************************************/
+
+/**
+ * _ctl_host_trace_buffer_enable_show - firmware ring buffer (trace only)
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ *
+ * This is a mechnism to post/release host_trace_buffers
+ */
+static ssize_t
+_ctl_host_trace_buffer_enable_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ if ((!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) ||
+ ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0))
+ return snprintf(buf, PAGE_SIZE, "off\n");
+ else if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_RELEASED))
+ return snprintf(buf, PAGE_SIZE, "release\n");
+ else
+ return snprintf(buf, PAGE_SIZE, "post\n");
+}
+
+static ssize_t
+_ctl_host_trace_buffer_enable_store(struct device *cdev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ char str[10] = "";
+ struct mpt3_diag_register diag_register;
+ u8 issue_reset = 0;
+
+ /* don't allow post/release occurr while recovery is active */
+ if (ioc->shost_recovery || ioc->remove_host ||
+ ioc->pci_error_recovery || ioc->is_driver_loading)
+ return -EBUSY;
+
+ if (sscanf(buf, "%9s", str) != 1)
+ return -EINVAL;
+
+ if (!strcmp(str, "post")) {
+ /* exit out if host buffers are already posted */
+ if ((ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) &&
+ (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) &&
+ ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_RELEASED) == 0))
+ goto out;
+ memset(&diag_register, 0, sizeof(struct mpt3_diag_register));
+ pr_info(MPT3SAS_FMT "posting host trace buffers\n",
+ ioc->name);
+ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE;
+ diag_register.requested_buffer_size = (1024 * 1024);
+ diag_register.unique_id = 0x7075900;
+ ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] = 0;
+ _ctl_diag_register_2(ioc, &diag_register);
+ } else if (!strcmp(str, "release")) {
+ /* exit out if host buffers are already released */
+ if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE])
+ goto out;
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0)
+ goto out;
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_RELEASED))
+ goto out;
+ pr_info(MPT3SAS_FMT "releasing host trace buffer\n",
+ ioc->name);
+ mpt3sas_send_diag_release(ioc, MPI2_DIAG_BUF_TYPE_TRACE,
+ &issue_reset);
+ }
+
+ out:
+ return strlen(buf);
+}
+static DEVICE_ATTR(host_trace_buffer_enable, S_IRUGO | S_IWUSR,
+ _ctl_host_trace_buffer_enable_show,
+ _ctl_host_trace_buffer_enable_store);
+
+/*********** diagnostic trigger suppport *********************************/
+
+/**
+ * _ctl_diag_trigger_master_show - show the diag_trigger_master attribute
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+_ctl_diag_trigger_master_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ unsigned long flags;
+ ssize_t rc;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+ rc = sizeof(struct SL_WH_MASTER_TRIGGER_T);
+ memcpy(buf, &ioc->diag_trigger_master, rc);
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return rc;
+}
+
+/**
+ * _ctl_diag_trigger_master_store - store the diag_trigger_master attribute
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+_ctl_diag_trigger_master_store(struct device *cdev,
+ struct device_attribute *attr, const char *buf, size_t count)
+
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ unsigned long flags;
+ ssize_t rc;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+ rc = min(sizeof(struct SL_WH_MASTER_TRIGGER_T), count);
+ memset(&ioc->diag_trigger_master, 0,
+ sizeof(struct SL_WH_MASTER_TRIGGER_T));
+ memcpy(&ioc->diag_trigger_master, buf, rc);
+ ioc->diag_trigger_master.MasterData |=
+ (MASTER_TRIGGER_FW_FAULT + MASTER_TRIGGER_ADAPTER_RESET);
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return rc;
+}
+static DEVICE_ATTR(diag_trigger_master, S_IRUGO | S_IWUSR,
+ _ctl_diag_trigger_master_show, _ctl_diag_trigger_master_store);
+
+
+/**
+ * _ctl_diag_trigger_event_show - show the diag_trigger_event attribute
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+_ctl_diag_trigger_event_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ unsigned long flags;
+ ssize_t rc;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+ rc = sizeof(struct SL_WH_EVENT_TRIGGERS_T);
+ memcpy(buf, &ioc->diag_trigger_event, rc);
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return rc;
+}
+
+/**
+ * _ctl_diag_trigger_event_store - store the diag_trigger_event attribute
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+_ctl_diag_trigger_event_store(struct device *cdev,
+ struct device_attribute *attr, const char *buf, size_t count)
+
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ unsigned long flags;
+ ssize_t sz;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+ sz = min(sizeof(struct SL_WH_EVENT_TRIGGERS_T), count);
+ memset(&ioc->diag_trigger_event, 0,
+ sizeof(struct SL_WH_EVENT_TRIGGERS_T));
+ memcpy(&ioc->diag_trigger_event, buf, sz);
+ if (ioc->diag_trigger_event.ValidEntries > NUM_VALID_ENTRIES)
+ ioc->diag_trigger_event.ValidEntries = NUM_VALID_ENTRIES;
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return sz;
+}
+static DEVICE_ATTR(diag_trigger_event, S_IRUGO | S_IWUSR,
+ _ctl_diag_trigger_event_show, _ctl_diag_trigger_event_store);
+
+
+/**
+ * _ctl_diag_trigger_scsi_show - show the diag_trigger_scsi attribute
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+_ctl_diag_trigger_scsi_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ unsigned long flags;
+ ssize_t rc;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+ rc = sizeof(struct SL_WH_SCSI_TRIGGERS_T);
+ memcpy(buf, &ioc->diag_trigger_scsi, rc);
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return rc;
+}
+
+/**
+ * _ctl_diag_trigger_scsi_store - store the diag_trigger_scsi attribute
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+_ctl_diag_trigger_scsi_store(struct device *cdev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ unsigned long flags;
+ ssize_t sz;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+ sz = min(sizeof(struct SL_WH_SCSI_TRIGGERS_T), count);
+ memset(&ioc->diag_trigger_scsi, 0,
+ sizeof(struct SL_WH_EVENT_TRIGGERS_T));
+ memcpy(&ioc->diag_trigger_scsi, buf, sz);
+ if (ioc->diag_trigger_scsi.ValidEntries > NUM_VALID_ENTRIES)
+ ioc->diag_trigger_scsi.ValidEntries = NUM_VALID_ENTRIES;
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return sz;
+}
+static DEVICE_ATTR(diag_trigger_scsi, S_IRUGO | S_IWUSR,
+ _ctl_diag_trigger_scsi_show, _ctl_diag_trigger_scsi_store);
+
+
+/**
+ * _ctl_diag_trigger_scsi_show - show the diag_trigger_mpi attribute
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+_ctl_diag_trigger_mpi_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ unsigned long flags;
+ ssize_t rc;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+ rc = sizeof(struct SL_WH_MPI_TRIGGERS_T);
+ memcpy(buf, &ioc->diag_trigger_mpi, rc);
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return rc;
+}
+
+/**
+ * _ctl_diag_trigger_mpi_store - store the diag_trigger_mpi attribute
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+_ctl_diag_trigger_mpi_store(struct device *cdev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ unsigned long flags;
+ ssize_t sz;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+ sz = min(sizeof(struct SL_WH_MPI_TRIGGERS_T), count);
+ memset(&ioc->diag_trigger_mpi, 0,
+ sizeof(struct SL_WH_EVENT_TRIGGERS_T));
+ memcpy(&ioc->diag_trigger_mpi, buf, sz);
+ if (ioc->diag_trigger_mpi.ValidEntries > NUM_VALID_ENTRIES)
+ ioc->diag_trigger_mpi.ValidEntries = NUM_VALID_ENTRIES;
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return sz;
+}
+
+static DEVICE_ATTR(diag_trigger_mpi, S_IRUGO | S_IWUSR,
+ _ctl_diag_trigger_mpi_show, _ctl_diag_trigger_mpi_store);
+
+/*********** diagnostic trigger suppport *** END ****************************/
+
+
+
+/*****************************************/
+
+struct device_attribute *mpt3sas_host_attrs[] = {
+ &dev_attr_version_fw,
+ &dev_attr_version_bios,
+ &dev_attr_version_mpi,
+ &dev_attr_version_product,
+ &dev_attr_version_nvdata_persistent,
+ &dev_attr_version_nvdata_default,
+ &dev_attr_board_name,
+ &dev_attr_board_assembly,
+ &dev_attr_board_tracer,
+ &dev_attr_io_delay,
+ &dev_attr_device_delay,
+ &dev_attr_logging_level,
+ &dev_attr_fwfault_debug,
+ &dev_attr_fw_queue_depth,
+ &dev_attr_host_sas_address,
+ &dev_attr_ioc_reset_count,
+ &dev_attr_host_trace_buffer_size,
+ &dev_attr_host_trace_buffer,
+ &dev_attr_host_trace_buffer_enable,
+ &dev_attr_reply_queue_count,
+ &dev_attr_diag_trigger_master,
+ &dev_attr_diag_trigger_event,
+ &dev_attr_diag_trigger_scsi,
+ &dev_attr_diag_trigger_mpi,
+ NULL,
+};
+
+/* device attributes */
+
+/**
+ * _ctl_device_sas_address_show - sas address
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * This is the sas address for the target
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_device_sas_address_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct MPT3SAS_DEVICE *sas_device_priv_data = sdev->hostdata;
+
+ return snprintf(buf, PAGE_SIZE, "0x%016llx\n",
+ (unsigned long long)sas_device_priv_data->sas_target->sas_address);
+}
+static DEVICE_ATTR(sas_address, S_IRUGO, _ctl_device_sas_address_show, NULL);
+
+/**
+ * _ctl_device_handle_show - device handle
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * This is the firmware assigned device handle
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t
+_ctl_device_handle_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct MPT3SAS_DEVICE *sas_device_priv_data = sdev->hostdata;
+
+ return snprintf(buf, PAGE_SIZE, "0x%04x\n",
+ sas_device_priv_data->sas_target->handle);
+}
+static DEVICE_ATTR(sas_device_handle, S_IRUGO, _ctl_device_handle_show, NULL);
+
+struct device_attribute *mpt3sas_dev_attrs[] = {
+ &dev_attr_sas_address,
+ &dev_attr_sas_device_handle,
+ NULL,
+};
+
+static const struct file_operations ctl_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = _ctl_ioctl,
+ .release = _ctl_release,
+ .poll = _ctl_poll,
+ .fasync = _ctl_fasync,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = _ctl_ioctl_compat,
+#endif
+};
+
+static struct miscdevice ctl_dev = {
+ .minor = MPT3SAS_MINOR,
+ .name = MPT3SAS_DEV_NAME,
+ .fops = &ctl_fops,
+};
+
+/**
+ * mpt3sas_ctl_init - main entry point for ctl.
+ *
+ */
+void
+mpt3sas_ctl_init(void)
+{
+ async_queue = NULL;
+ if (misc_register(&ctl_dev) < 0)
+ pr_err("%s can't register misc device [minor=%d]\n",
+ MPT3SAS_DRIVER_NAME, MPT3SAS_MINOR);
+
+ init_waitqueue_head(&ctl_poll_wait);
+}
+
+/**
+ * mpt3sas_ctl_exit - exit point for ctl
+ *
+ */
+void
+mpt3sas_ctl_exit(void)
+{
+ struct MPT3SAS_ADAPTER *ioc;
+ int i;
+
+ list_for_each_entry(ioc, &mpt3sas_ioc_list, list) {
+
+ /* free memory associated to diag buffers */
+ for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) {
+ if (!ioc->diag_buffer[i])
+ continue;
+ if (!(ioc->diag_buffer_status[i] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED))
+ continue;
+ if ((ioc->diag_buffer_status[i] &
+ MPT3_DIAG_BUFFER_IS_RELEASED))
+ continue;
+ pci_free_consistent(ioc->pdev, ioc->diag_buffer_sz[i],
+ ioc->diag_buffer[i], ioc->diag_buffer_dma[i]);
+ ioc->diag_buffer[i] = NULL;
+ ioc->diag_buffer_status[i] = 0;
+ }
+
+ kfree(ioc->event_log);
+ }
+ misc_deregister(&ctl_dev);
+}
diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.h b/drivers/scsi/mpt3sas/mpt3sas_ctl.h
new file mode 100644
index 00000000000..bd89f4f0055
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.h
@@ -0,0 +1,418 @@
+/*
+ * Management Module Support for MPT (Message Passing Technology) based
+ * controllers
+ *
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_ctl.h
+ * Copyright (C) 2012 LSI Corporation
+ * (mailto:DL-MPTFusionLinux@lsi.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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef MPT3SAS_CTL_H_INCLUDED
+#define MPT3SAS_CTL_H_INCLUDED
+
+#ifdef __KERNEL__
+#include <linux/miscdevice.h>
+#endif
+
+
+#ifndef MPT3SAS_MINOR
+#define MPT3SAS_MINOR (MPT_MINOR + 2)
+#endif
+#define MPT3SAS_DEV_NAME "mpt3ctl"
+#define MPT3_MAGIC_NUMBER 'L'
+#define MPT3_IOCTL_DEFAULT_TIMEOUT (10) /* in seconds */
+
+/**
+ * IOCTL opcodes
+ */
+#define MPT3IOCINFO _IOWR(MPT3_MAGIC_NUMBER, 17, \
+ struct mpt3_ioctl_iocinfo)
+#define MPT3COMMAND _IOWR(MPT3_MAGIC_NUMBER, 20, \
+ struct mpt3_ioctl_command)
+#ifdef CONFIG_COMPAT
+#define MPT3COMMAND32 _IOWR(MPT3_MAGIC_NUMBER, 20, \
+ struct mpt3_ioctl_command32)
+#endif
+#define MPT3EVENTQUERY _IOWR(MPT3_MAGIC_NUMBER, 21, \
+ struct mpt3_ioctl_eventquery)
+#define MPT3EVENTENABLE _IOWR(MPT3_MAGIC_NUMBER, 22, \
+ struct mpt3_ioctl_eventenable)
+#define MPT3EVENTREPORT _IOWR(MPT3_MAGIC_NUMBER, 23, \
+ struct mpt3_ioctl_eventreport)
+#define MPT3HARDRESET _IOWR(MPT3_MAGIC_NUMBER, 24, \
+ struct mpt3_ioctl_diag_reset)
+#define MPT3BTDHMAPPING _IOWR(MPT3_MAGIC_NUMBER, 31, \
+ struct mpt3_ioctl_btdh_mapping)
+
+/* diag buffer support */
+#define MPT3DIAGREGISTER _IOWR(MPT3_MAGIC_NUMBER, 26, \
+ struct mpt3_diag_register)
+#define MPT3DIAGRELEASE _IOWR(MPT3_MAGIC_NUMBER, 27, \
+ struct mpt3_diag_release)
+#define MPT3DIAGUNREGISTER _IOWR(MPT3_MAGIC_NUMBER, 28, \
+ struct mpt3_diag_unregister)
+#define MPT3DIAGQUERY _IOWR(MPT3_MAGIC_NUMBER, 29, \
+ struct mpt3_diag_query)
+#define MPT3DIAGREADBUFFER _IOWR(MPT3_MAGIC_NUMBER, 30, \
+ struct mpt3_diag_read_buffer)
+
+/**
+ * struct mpt3_ioctl_header - main header structure
+ * @ioc_number - IOC unit number
+ * @port_number - IOC port number
+ * @max_data_size - maximum number bytes to transfer on read
+ */
+struct mpt3_ioctl_header {
+ uint32_t ioc_number;
+ uint32_t port_number;
+ uint32_t max_data_size;
+};
+
+/**
+ * struct mpt3_ioctl_diag_reset - diagnostic reset
+ * @hdr - generic header
+ */
+struct mpt3_ioctl_diag_reset {
+ struct mpt3_ioctl_header hdr;
+};
+
+
+/**
+ * struct mpt3_ioctl_pci_info - pci device info
+ * @device - pci device id
+ * @function - pci function id
+ * @bus - pci bus id
+ * @segment_id - pci segment id
+ */
+struct mpt3_ioctl_pci_info {
+ union {
+ struct {
+ uint32_t device:5;
+ uint32_t function:3;
+ uint32_t bus:24;
+ } bits;
+ uint32_t word;
+ } u;
+ uint32_t segment_id;
+};
+
+
+#define MPT2_IOCTL_INTERFACE_SCSI (0x00)
+#define MPT2_IOCTL_INTERFACE_FC (0x01)
+#define MPT2_IOCTL_INTERFACE_FC_IP (0x02)
+#define MPT2_IOCTL_INTERFACE_SAS (0x03)
+#define MPT2_IOCTL_INTERFACE_SAS2 (0x04)
+#define MPT3_IOCTL_INTERFACE_SAS3 (0x06)
+#define MPT2_IOCTL_VERSION_LENGTH (32)
+
+/**
+ * struct mpt3_ioctl_iocinfo - generic controller info
+ * @hdr - generic header
+ * @adapter_type - type of adapter (spi, fc, sas)
+ * @port_number - port number
+ * @pci_id - PCI Id
+ * @hw_rev - hardware revision
+ * @sub_system_device - PCI subsystem Device ID
+ * @sub_system_vendor - PCI subsystem Vendor ID
+ * @rsvd0 - reserved
+ * @firmware_version - firmware version
+ * @bios_version - BIOS version
+ * @driver_version - driver version - 32 ASCII characters
+ * @rsvd1 - reserved
+ * @scsi_id - scsi id of adapter 0
+ * @rsvd2 - reserved
+ * @pci_information - pci info (2nd revision)
+ */
+struct mpt3_ioctl_iocinfo {
+ struct mpt3_ioctl_header hdr;
+ uint32_t adapter_type;
+ uint32_t port_number;
+ uint32_t pci_id;
+ uint32_t hw_rev;
+ uint32_t subsystem_device;
+ uint32_t subsystem_vendor;
+ uint32_t rsvd0;
+ uint32_t firmware_version;
+ uint32_t bios_version;
+ uint8_t driver_version[MPT2_IOCTL_VERSION_LENGTH];
+ uint8_t rsvd1;
+ uint8_t scsi_id;
+ uint16_t rsvd2;
+ struct mpt3_ioctl_pci_info pci_information;
+};
+
+
+/* number of event log entries */
+#define MPT3SAS_CTL_EVENT_LOG_SIZE (50)
+
+/**
+ * struct mpt3_ioctl_eventquery - query event count and type
+ * @hdr - generic header
+ * @event_entries - number of events returned by get_event_report
+ * @rsvd - reserved
+ * @event_types - type of events currently being captured
+ */
+struct mpt3_ioctl_eventquery {
+ struct mpt3_ioctl_header hdr;
+ uint16_t event_entries;
+ uint16_t rsvd;
+ uint32_t event_types[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS];
+};
+
+/**
+ * struct mpt3_ioctl_eventenable - enable/disable event capturing
+ * @hdr - generic header
+ * @event_types - toggle off/on type of events to be captured
+ */
+struct mpt3_ioctl_eventenable {
+ struct mpt3_ioctl_header hdr;
+ uint32_t event_types[4];
+};
+
+#define MPT3_EVENT_DATA_SIZE (192)
+/**
+ * struct MPT3_IOCTL_EVENTS -
+ * @event - the event that was reported
+ * @context - unique value for each event assigned by driver
+ * @data - event data returned in fw reply message
+ */
+struct MPT3_IOCTL_EVENTS {
+ uint32_t event;
+ uint32_t context;
+ uint8_t data[MPT3_EVENT_DATA_SIZE];
+};
+
+/**
+ * struct mpt3_ioctl_eventreport - returing event log
+ * @hdr - generic header
+ * @event_data - (see struct MPT3_IOCTL_EVENTS)
+ */
+struct mpt3_ioctl_eventreport {
+ struct mpt3_ioctl_header hdr;
+ struct MPT3_IOCTL_EVENTS event_data[1];
+};
+
+/**
+ * struct mpt3_ioctl_command - generic mpt firmware passthru ioctl
+ * @hdr - generic header
+ * @timeout - command timeout in seconds. (if zero then use driver default
+ * value).
+ * @reply_frame_buf_ptr - reply location
+ * @data_in_buf_ptr - destination for read
+ * @data_out_buf_ptr - data source for write
+ * @sense_data_ptr - sense data location
+ * @max_reply_bytes - maximum number of reply bytes to be sent to app.
+ * @data_in_size - number bytes for data transfer in (read)
+ * @data_out_size - number bytes for data transfer out (write)
+ * @max_sense_bytes - maximum number of bytes for auto sense buffers
+ * @data_sge_offset - offset in words from the start of the request message to
+ * the first SGL
+ * @mf[1];
+ */
+struct mpt3_ioctl_command {
+ struct mpt3_ioctl_header hdr;
+ uint32_t timeout;
+ void __user *reply_frame_buf_ptr;
+ void __user *data_in_buf_ptr;
+ void __user *data_out_buf_ptr;
+ void __user *sense_data_ptr;
+ uint32_t max_reply_bytes;
+ uint32_t data_in_size;
+ uint32_t data_out_size;
+ uint32_t max_sense_bytes;
+ uint32_t data_sge_offset;
+ uint8_t mf[1];
+};
+
+#ifdef CONFIG_COMPAT
+struct mpt3_ioctl_command32 {
+ struct mpt3_ioctl_header hdr;
+ uint32_t timeout;
+ uint32_t reply_frame_buf_ptr;
+ uint32_t data_in_buf_ptr;
+ uint32_t data_out_buf_ptr;
+ uint32_t sense_data_ptr;
+ uint32_t max_reply_bytes;
+ uint32_t data_in_size;
+ uint32_t data_out_size;
+ uint32_t max_sense_bytes;
+ uint32_t data_sge_offset;
+ uint8_t mf[1];
+};
+#endif
+
+/**
+ * struct mpt3_ioctl_btdh_mapping - mapping info
+ * @hdr - generic header
+ * @id - target device identification number
+ * @bus - SCSI bus number that the target device exists on
+ * @handle - device handle for the target device
+ * @rsvd - reserved
+ *
+ * To obtain a bus/id the application sets
+ * handle to valid handle, and bus/id to 0xFFFF.
+ *
+ * To obtain the device handle the application sets
+ * bus/id valid value, and the handle to 0xFFFF.
+ */
+struct mpt3_ioctl_btdh_mapping {
+ struct mpt3_ioctl_header hdr;
+ uint32_t id;
+ uint32_t bus;
+ uint16_t handle;
+ uint16_t rsvd;
+};
+
+
+
+/* application flags for mpt3_diag_register, mpt3_diag_query */
+#define MPT3_APP_FLAGS_APP_OWNED (0x0001)
+#define MPT3_APP_FLAGS_BUFFER_VALID (0x0002)
+#define MPT3_APP_FLAGS_FW_BUFFER_ACCESS (0x0004)
+
+/* flags for mpt3_diag_read_buffer */
+#define MPT3_FLAGS_REREGISTER (0x0001)
+
+#define MPT3_PRODUCT_SPECIFIC_DWORDS 23
+
+/**
+ * struct mpt3_diag_register - application register with driver
+ * @hdr - generic header
+ * @reserved -
+ * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED
+ * @application_flags - misc flags
+ * @diagnostic_flags - specifies flags affecting command processing
+ * @product_specific - product specific information
+ * @requested_buffer_size - buffers size in bytes
+ * @unique_id - tag specified by application that is used to signal ownership
+ * of the buffer.
+ *
+ * This will allow the driver to setup any required buffers that will be
+ * needed by firmware to communicate with the driver.
+ */
+struct mpt3_diag_register {
+ struct mpt3_ioctl_header hdr;
+ uint8_t reserved;
+ uint8_t buffer_type;
+ uint16_t application_flags;
+ uint32_t diagnostic_flags;
+ uint32_t product_specific[MPT3_PRODUCT_SPECIFIC_DWORDS];
+ uint32_t requested_buffer_size;
+ uint32_t unique_id;
+};
+
+/**
+ * struct mpt3_diag_unregister - application unregister with driver
+ * @hdr - generic header
+ * @unique_id - tag uniquely identifies the buffer to be unregistered
+ *
+ * This will allow the driver to cleanup any memory allocated for diag
+ * messages and to free up any resources.
+ */
+struct mpt3_diag_unregister {
+ struct mpt3_ioctl_header hdr;
+ uint32_t unique_id;
+};
+
+/**
+ * struct mpt3_diag_query - query relevant info associated with diag buffers
+ * @hdr - generic header
+ * @reserved -
+ * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED
+ * @application_flags - misc flags
+ * @diagnostic_flags - specifies flags affecting command processing
+ * @product_specific - product specific information
+ * @total_buffer_size - diag buffer size in bytes
+ * @driver_added_buffer_size - size of extra space appended to end of buffer
+ * @unique_id - unique id associated with this buffer.
+ *
+ * The application will send only buffer_type and unique_id. Driver will
+ * inspect unique_id first, if valid, fill in all the info. If unique_id is
+ * 0x00, the driver will return info specified by Buffer Type.
+ */
+struct mpt3_diag_query {
+ struct mpt3_ioctl_header hdr;
+ uint8_t reserved;
+ uint8_t buffer_type;
+ uint16_t application_flags;
+ uint32_t diagnostic_flags;
+ uint32_t product_specific[MPT3_PRODUCT_SPECIFIC_DWORDS];
+ uint32_t total_buffer_size;
+ uint32_t driver_added_buffer_size;
+ uint32_t unique_id;
+};
+
+/**
+ * struct mpt3_diag_release - request to send Diag Release Message to firmware
+ * @hdr - generic header
+ * @unique_id - tag uniquely identifies the buffer to be released
+ *
+ * This allows ownership of the specified buffer to returned to the driver,
+ * allowing an application to read the buffer without fear that firmware is
+ * overwritting information in the buffer.
+ */
+struct mpt3_diag_release {
+ struct mpt3_ioctl_header hdr;
+ uint32_t unique_id;
+};
+
+/**
+ * struct mpt3_diag_read_buffer - request for copy of the diag buffer
+ * @hdr - generic header
+ * @status -
+ * @reserved -
+ * @flags - misc flags
+ * @starting_offset - starting offset within drivers buffer where to start
+ * reading data at into the specified application buffer
+ * @bytes_to_read - number of bytes to copy from the drivers buffer into the
+ * application buffer starting at starting_offset.
+ * @unique_id - unique id associated with this buffer.
+ * @diagnostic_data - data payload
+ */
+struct mpt3_diag_read_buffer {
+ struct mpt3_ioctl_header hdr;
+ uint8_t status;
+ uint8_t reserved;
+ uint16_t flags;
+ uint32_t starting_offset;
+ uint32_t bytes_to_read;
+ uint32_t unique_id;
+ uint32_t diagnostic_data[1];
+};
+
+#endif /* MPT3SAS_CTL_H_INCLUDED */
diff --git a/drivers/scsi/mpt3sas/mpt3sas_debug.h b/drivers/scsi/mpt3sas/mpt3sas_debug.h
new file mode 100644
index 00000000000..35405e7044f
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_debug.h
@@ -0,0 +1,219 @@
+/*
+ * Logging Support for MPT (Message Passing Technology) based controllers
+ *
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_debug.c
+ * Copyright (C) 2012 LSI Corporation
+ * (mailto:DL-MPTFusionLinux@lsi.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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef MPT3SAS_DEBUG_H_INCLUDED
+#define MPT3SAS_DEBUG_H_INCLUDED
+
+#define MPT_DEBUG 0x00000001
+#define MPT_DEBUG_MSG_FRAME 0x00000002
+#define MPT_DEBUG_SG 0x00000004
+#define MPT_DEBUG_EVENTS 0x00000008
+#define MPT_DEBUG_EVENT_WORK_TASK 0x00000010
+#define MPT_DEBUG_INIT 0x00000020
+#define MPT_DEBUG_EXIT 0x00000040
+#define MPT_DEBUG_FAIL 0x00000080
+#define MPT_DEBUG_TM 0x00000100
+#define MPT_DEBUG_REPLY 0x00000200
+#define MPT_DEBUG_HANDSHAKE 0x00000400
+#define MPT_DEBUG_CONFIG 0x00000800
+#define MPT_DEBUG_DL 0x00001000
+#define MPT_DEBUG_RESET 0x00002000
+#define MPT_DEBUG_SCSI 0x00004000
+#define MPT_DEBUG_IOCTL 0x00008000
+#define MPT_DEBUG_SAS 0x00020000
+#define MPT_DEBUG_TRANSPORT 0x00040000
+#define MPT_DEBUG_TASK_SET_FULL 0x00080000
+
+#define MPT_DEBUG_TRIGGER_DIAG 0x00200000
+
+
+/*
+ * CONFIG_SCSI_MPT3SAS_LOGGING - enabled in Kconfig
+ */
+
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+#define MPT_CHECK_LOGGING(IOC, CMD, BITS) \
+{ \
+ if (IOC->logging_level & BITS) \
+ CMD; \
+}
+#else
+#define MPT_CHECK_LOGGING(IOC, CMD, BITS)
+#endif /* CONFIG_SCSI_MPT3SAS_LOGGING */
+
+
+/*
+ * debug macros
+ */
+
+#define dprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG)
+
+#define dsgprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SG)
+
+#define devtprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_EVENTS)
+
+#define dewtprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_EVENT_WORK_TASK)
+
+#define dinitprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_INIT)
+
+#define dexitprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_EXIT)
+
+#define dfailprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_FAIL)
+
+#define dtmprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TM)
+
+#define dreplyprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_REPLY)
+
+#define dhsprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_HANDSHAKE)
+
+#define dcprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_CONFIG)
+
+#define ddlprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_DL)
+
+#define drsprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_RESET)
+
+#define dsprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SCSI)
+
+#define dctlprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_IOCTL)
+
+#define dsasprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SAS)
+
+#define dsastransport(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SAS_WIDE)
+
+#define dmfprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_MSG_FRAME)
+
+#define dtsfprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TASK_SET_FULL)
+
+#define dtransportprintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TRANSPORT)
+
+#define dTriggerDiagPrintk(IOC, CMD) \
+ MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TRIGGER_DIAG)
+
+
+
+/* inline functions for dumping debug data*/
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+/**
+ * _debug_dump_mf - print message frame contents
+ * @mpi_request: pointer to message frame
+ * @sz: number of dwords
+ */
+static inline void
+_debug_dump_mf(void *mpi_request, int sz)
+{
+ int i;
+ __le32 *mfp = (__le32 *)mpi_request;
+
+ pr_info("mf:\n\t");
+ for (i = 0; i < sz; i++) {
+ if (i && ((i % 8) == 0))
+ pr_info("\n\t");
+ pr_info("%08x ", le32_to_cpu(mfp[i]));
+ }
+ pr_info("\n");
+}
+/**
+ * _debug_dump_reply - print message frame contents
+ * @mpi_request: pointer to message frame
+ * @sz: number of dwords
+ */
+static inline void
+_debug_dump_reply(void *mpi_request, int sz)
+{
+ int i;
+ __le32 *mfp = (__le32 *)mpi_request;
+
+ pr_info("reply:\n\t");
+ for (i = 0; i < sz; i++) {
+ if (i && ((i % 8) == 0))
+ pr_info("\n\t");
+ pr_info("%08x ", le32_to_cpu(mfp[i]));
+ }
+ pr_info("\n");
+}
+/**
+ * _debug_dump_config - print config page contents
+ * @mpi_request: pointer to message frame
+ * @sz: number of dwords
+ */
+static inline void
+_debug_dump_config(void *mpi_request, int sz)
+{
+ int i;
+ __le32 *mfp = (__le32 *)mpi_request;
+
+ pr_info("config:\n\t");
+ for (i = 0; i < sz; i++) {
+ if (i && ((i % 8) == 0))
+ pr_info("\n\t");
+ pr_info("%08x ", le32_to_cpu(mfp[i]));
+ }
+ pr_info("\n");
+}
+#else
+#define _debug_dump_mf(mpi_request, sz)
+#define _debug_dump_reply(mpi_request, sz)
+#define _debug_dump_config(mpi_request, sz)
+#endif /* CONFIG_SCSI_MPT3SAS_LOGGING */
+
+#endif /* MPT3SAS_DEBUG_H_INCLUDED */
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
new file mode 100644
index 00000000000..05f80450ac7
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -0,0 +1,8167 @@
+/*
+ * Scsi Host Layer for MPT (Message Passing Technology) based controllers
+ *
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_scsih.c
+ * Copyright (C) 2012 LSI Corporation
+ * (mailto:DL-MPTFusionLinux@lsi.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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/blkdev.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/aer.h>
+#include <linux/raid_class.h>
+
+#include "mpt3sas_base.h"
+
+MODULE_AUTHOR(MPT3SAS_AUTHOR);
+MODULE_DESCRIPTION(MPT3SAS_DESCRIPTION);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(MPT3SAS_DRIVER_VERSION);
+
+#define RAID_CHANNEL 1
+/* forward proto's */
+static void _scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_node *sas_expander);
+static void _firmware_event_work(struct work_struct *work);
+
+static void _scsih_remove_device(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_device *sas_device);
+static int _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle,
+ u8 retry_count, u8 is_pd);
+
+static u8 _scsih_check_for_pending_tm(struct MPT3SAS_ADAPTER *ioc, u16 smid);
+
+static void _scsih_scan_start(struct Scsi_Host *shost);
+static int _scsih_scan_finished(struct Scsi_Host *shost, unsigned long time);
+
+/* global parameters */
+LIST_HEAD(mpt3sas_ioc_list);
+
+/* local parameters */
+static u8 scsi_io_cb_idx = -1;
+static u8 tm_cb_idx = -1;
+static u8 ctl_cb_idx = -1;
+static u8 base_cb_idx = -1;
+static u8 port_enable_cb_idx = -1;
+static u8 transport_cb_idx = -1;
+static u8 scsih_cb_idx = -1;
+static u8 config_cb_idx = -1;
+static int mpt_ids;
+
+static u8 tm_tr_cb_idx = -1 ;
+static u8 tm_tr_volume_cb_idx = -1 ;
+static u8 tm_sas_control_cb_idx = -1;
+
+/* command line options */
+static u32 logging_level;
+MODULE_PARM_DESC(logging_level,
+ " bits for enabling additional logging info (default=0)");
+
+
+static ushort max_sectors = 0xFFFF;
+module_param(max_sectors, ushort, 0);
+MODULE_PARM_DESC(max_sectors, "max sectors, range 64 to 32767 default=32767");
+
+
+static int missing_delay[2] = {-1, -1};
+module_param_array(missing_delay, int, NULL, 0);
+MODULE_PARM_DESC(missing_delay, " device missing delay , io missing delay");
+
+/* scsi-mid layer global parmeter is max_report_luns, which is 511 */
+#define MPT3SAS_MAX_LUN (16895)
+static int max_lun = MPT3SAS_MAX_LUN;
+module_param(max_lun, int, 0);
+MODULE_PARM_DESC(max_lun, " max lun, default=16895 ");
+
+
+
+
+/* diag_buffer_enable is bitwise
+ * bit 0 set = TRACE
+ * bit 1 set = SNAPSHOT
+ * bit 2 set = EXTENDED
+ *
+ * Either bit can be set, or both
+ */
+static int diag_buffer_enable = -1;
+module_param(diag_buffer_enable, int, 0);
+MODULE_PARM_DESC(diag_buffer_enable,
+ " post diag buffers (TRACE=1/SNAPSHOT=2/EXTENDED=4/default=0)");
+static int disable_discovery = -1;
+module_param(disable_discovery, int, 0);
+MODULE_PARM_DESC(disable_discovery, " disable discovery ");
+
+
+/* permit overriding the host protection capabilities mask (EEDP/T10 PI) */
+static int prot_mask = -1;
+module_param(prot_mask, int, 0);
+MODULE_PARM_DESC(prot_mask, " host protection capabilities mask, def=7 ");
+
+
+/* raid transport support */
+
+static struct raid_template *mpt3sas_raid_template;
+
+
+/**
+ * struct sense_info - common structure for obtaining sense keys
+ * @skey: sense key
+ * @asc: additional sense code
+ * @ascq: additional sense code qualifier
+ */
+struct sense_info {
+ u8 skey;
+ u8 asc;
+ u8 ascq;
+};
+
+#define MPT3SAS_PROCESS_TRIGGER_DIAG (0xFFFB)
+#define MPT3SAS_TURN_ON_FAULT_LED (0xFFFC)
+#define MPT3SAS_PORT_ENABLE_COMPLETE (0xFFFD)
+#define MPT3SAS_ABRT_TASK_SET (0xFFFE)
+#define MPT3SAS_REMOVE_UNRESPONDING_DEVICES (0xFFFF)
+/**
+ * struct fw_event_work - firmware event struct
+ * @list: link list framework
+ * @work: work object (ioc->fault_reset_work_q)
+ * @cancel_pending_work: flag set during reset handling
+ * @ioc: per adapter object
+ * @device_handle: device handle
+ * @VF_ID: virtual function id
+ * @VP_ID: virtual port id
+ * @ignore: flag meaning this event has been marked to ignore
+ * @event: firmware event MPI2_EVENT_XXX defined in mpt2_ioc.h
+ * @event_data: reply event data payload follows
+ *
+ * This object stored on ioc->fw_event_list.
+ */
+struct fw_event_work {
+ struct list_head list;
+ struct work_struct work;
+ u8 cancel_pending_work;
+ struct delayed_work delayed_work;
+
+ struct MPT3SAS_ADAPTER *ioc;
+ u16 device_handle;
+ u8 VF_ID;
+ u8 VP_ID;
+ u8 ignore;
+ u16 event;
+ void *event_data;
+};
+
+/* raid transport support */
+static struct raid_template *mpt3sas_raid_template;
+
+/**
+ * struct _scsi_io_transfer - scsi io transfer
+ * @handle: sas device handle (assigned by firmware)
+ * @is_raid: flag set for hidden raid components
+ * @dir: DMA_TO_DEVICE, DMA_FROM_DEVICE,
+ * @data_length: data transfer length
+ * @data_dma: dma pointer to data
+ * @sense: sense data
+ * @lun: lun number
+ * @cdb_length: cdb length
+ * @cdb: cdb contents
+ * @timeout: timeout for this command
+ * @VF_ID: virtual function id
+ * @VP_ID: virtual port id
+ * @valid_reply: flag set for reply message
+ * @sense_length: sense length
+ * @ioc_status: ioc status
+ * @scsi_state: scsi state
+ * @scsi_status: scsi staus
+ * @log_info: log information
+ * @transfer_length: data length transfer when there is a reply message
+ *
+ * Used for sending internal scsi commands to devices within this module.
+ * Refer to _scsi_send_scsi_io().
+ */
+struct _scsi_io_transfer {
+ u16 handle;
+ u8 is_raid;
+ enum dma_data_direction dir;
+ u32 data_length;
+ dma_addr_t data_dma;
+ u8 sense[SCSI_SENSE_BUFFERSIZE];
+ u32 lun;
+ u8 cdb_length;
+ u8 cdb[32];
+ u8 timeout;
+ u8 VF_ID;
+ u8 VP_ID;
+ u8 valid_reply;
+ /* the following bits are only valid when 'valid_reply = 1' */
+ u32 sense_length;
+ u16 ioc_status;
+ u8 scsi_state;
+ u8 scsi_status;
+ u32 log_info;
+ u32 transfer_length;
+};
+
+/*
+ * The pci device ids are defined in mpi/mpi2_cnfg.h.
+ */
+static DEFINE_PCI_DEVICE_TABLE(scsih_pci_table) = {
+ /* Fury ~ 3004 and 3008 */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3004,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3008,
+ PCI_ANY_ID, PCI_ANY_ID },
+ /* Invader ~ 3108 */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_1,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_2,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_5,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_6,
+ PCI_ANY_ID, PCI_ANY_ID },
+ {0} /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(pci, scsih_pci_table);
+
+/**
+ * _scsih_set_debug_level - global setting of ioc->logging_level.
+ *
+ * Note: The logging levels are defined in mpt3sas_debug.h.
+ */
+static int
+_scsih_set_debug_level(const char *val, struct kernel_param *kp)
+{
+ int ret = param_set_int(val, kp);
+ struct MPT3SAS_ADAPTER *ioc;
+
+ if (ret)
+ return ret;
+
+ pr_info("setting logging_level(0x%08x)\n", logging_level);
+ list_for_each_entry(ioc, &mpt3sas_ioc_list, list)
+ ioc->logging_level = logging_level;
+ return 0;
+}
+module_param_call(logging_level, _scsih_set_debug_level, param_get_int,
+ &logging_level, 0644);
+
+/**
+ * _scsih_srch_boot_sas_address - search based on sas_address
+ * @sas_address: sas address
+ * @boot_device: boot device object from bios page 2
+ *
+ * Returns 1 when there's a match, 0 means no match.
+ */
+static inline int
+_scsih_srch_boot_sas_address(u64 sas_address,
+ Mpi2BootDeviceSasWwid_t *boot_device)
+{
+ return (sas_address == le64_to_cpu(boot_device->SASAddress)) ? 1 : 0;
+}
+
+/**
+ * _scsih_srch_boot_device_name - search based on device name
+ * @device_name: device name specified in INDENTIFY fram
+ * @boot_device: boot device object from bios page 2
+ *
+ * Returns 1 when there's a match, 0 means no match.
+ */
+static inline int
+_scsih_srch_boot_device_name(u64 device_name,
+ Mpi2BootDeviceDeviceName_t *boot_device)
+{
+ return (device_name == le64_to_cpu(boot_device->DeviceName)) ? 1 : 0;
+}
+
+/**
+ * _scsih_srch_boot_encl_slot - search based on enclosure_logical_id/slot
+ * @enclosure_logical_id: enclosure logical id
+ * @slot_number: slot number
+ * @boot_device: boot device object from bios page 2
+ *
+ * Returns 1 when there's a match, 0 means no match.
+ */
+static inline int
+_scsih_srch_boot_encl_slot(u64 enclosure_logical_id, u16 slot_number,
+ Mpi2BootDeviceEnclosureSlot_t *boot_device)
+{
+ return (enclosure_logical_id == le64_to_cpu(boot_device->
+ EnclosureLogicalID) && slot_number == le16_to_cpu(boot_device->
+ SlotNumber)) ? 1 : 0;
+}
+
+/**
+ * _scsih_is_boot_device - search for matching boot device.
+ * @sas_address: sas address
+ * @device_name: device name specified in INDENTIFY fram
+ * @enclosure_logical_id: enclosure logical id
+ * @slot_number: slot number
+ * @form: specifies boot device form
+ * @boot_device: boot device object from bios page 2
+ *
+ * Returns 1 when there's a match, 0 means no match.
+ */
+static int
+_scsih_is_boot_device(u64 sas_address, u64 device_name,
+ u64 enclosure_logical_id, u16 slot, u8 form,
+ Mpi2BiosPage2BootDevice_t *boot_device)
+{
+ int rc = 0;
+
+ switch (form) {
+ case MPI2_BIOSPAGE2_FORM_SAS_WWID:
+ if (!sas_address)
+ break;
+ rc = _scsih_srch_boot_sas_address(
+ sas_address, &boot_device->SasWwid);
+ break;
+ case MPI2_BIOSPAGE2_FORM_ENCLOSURE_SLOT:
+ if (!enclosure_logical_id)
+ break;
+ rc = _scsih_srch_boot_encl_slot(
+ enclosure_logical_id,
+ slot, &boot_device->EnclosureSlot);
+ break;
+ case MPI2_BIOSPAGE2_FORM_DEVICE_NAME:
+ if (!device_name)
+ break;
+ rc = _scsih_srch_boot_device_name(
+ device_name, &boot_device->DeviceName);
+ break;
+ case MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED:
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * _scsih_get_sas_address - set the sas_address for given device handle
+ * @handle: device handle
+ * @sas_address: sas address
+ *
+ * Returns 0 success, non-zero when failure
+ */
+static int
+_scsih_get_sas_address(struct MPT3SAS_ADAPTER *ioc, u16 handle,
+ u64 *sas_address)
+{
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u32 ioc_status;
+
+ *sas_address = 0;
+
+ if (handle <= ioc->sas_hba.num_phys) {
+ *sas_address = ioc->sas_hba.sas_address;
+ return 0;
+ }
+
+ if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name,
+ __FILE__, __LINE__, __func__);
+ return -ENXIO;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
+ *sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
+ return 0;
+ }
+
+ /* we hit this becuase the given parent handle doesn't exist */
+ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ return -ENXIO;
+
+ /* else error case */
+ pr_err(MPT3SAS_FMT
+ "handle(0x%04x), ioc_status(0x%04x), failure at %s:%d/%s()!\n",
+ ioc->name, handle, ioc_status,
+ __FILE__, __LINE__, __func__);
+ return -EIO;
+}
+
+/**
+ * _scsih_determine_boot_device - determine boot device.
+ * @ioc: per adapter object
+ * @device: either sas_device or raid_device object
+ * @is_raid: [flag] 1 = raid object, 0 = sas object
+ *
+ * Determines whether this device should be first reported device to
+ * to scsi-ml or sas transport, this purpose is for persistent boot device.
+ * There are primary, alternate, and current entries in bios page 2. The order
+ * priority is primary, alternate, then current. This routine saves
+ * the corresponding device object and is_raid flag in the ioc object.
+ * The saved data to be used later in _scsih_probe_boot_devices().
+ */
+static void
+_scsih_determine_boot_device(struct MPT3SAS_ADAPTER *ioc,
+ void *device, u8 is_raid)
+{
+ struct _sas_device *sas_device;
+ struct _raid_device *raid_device;
+ u64 sas_address;
+ u64 device_name;
+ u64 enclosure_logical_id;
+ u16 slot;
+
+ /* only process this function when driver loads */
+ if (!ioc->is_driver_loading)
+ return;
+
+ /* no Bios, return immediately */
+ if (!ioc->bios_pg3.BiosVersion)
+ return;
+
+ if (!is_raid) {
+ sas_device = device;
+ sas_address = sas_device->sas_address;
+ device_name = sas_device->device_name;
+ enclosure_logical_id = sas_device->enclosure_logical_id;
+ slot = sas_device->slot;
+ } else {
+ raid_device = device;
+ sas_address = raid_device->wwid;
+ device_name = 0;
+ enclosure_logical_id = 0;
+ slot = 0;
+ }
+
+ if (!ioc->req_boot_device.device) {
+ if (_scsih_is_boot_device(sas_address, device_name,
+ enclosure_logical_id, slot,
+ (ioc->bios_pg2.ReqBootDeviceForm &
+ MPI2_BIOSPAGE2_FORM_MASK),
+ &ioc->bios_pg2.RequestedBootDevice)) {
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: req_boot_device(0x%016llx)\n",
+ ioc->name, __func__,
+ (unsigned long long)sas_address));
+ ioc->req_boot_device.device = device;
+ ioc->req_boot_device.is_raid = is_raid;
+ }
+ }
+
+ if (!ioc->req_alt_boot_device.device) {
+ if (_scsih_is_boot_device(sas_address, device_name,
+ enclosure_logical_id, slot,
+ (ioc->bios_pg2.ReqAltBootDeviceForm &
+ MPI2_BIOSPAGE2_FORM_MASK),
+ &ioc->bios_pg2.RequestedAltBootDevice)) {
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: req_alt_boot_device(0x%016llx)\n",
+ ioc->name, __func__,
+ (unsigned long long)sas_address));
+ ioc->req_alt_boot_device.device = device;
+ ioc->req_alt_boot_device.is_raid = is_raid;
+ }
+ }
+
+ if (!ioc->current_boot_device.device) {
+ if (_scsih_is_boot_device(sas_address, device_name,
+ enclosure_logical_id, slot,
+ (ioc->bios_pg2.CurrentBootDeviceForm &
+ MPI2_BIOSPAGE2_FORM_MASK),
+ &ioc->bios_pg2.CurrentBootDevice)) {
+ dinitprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: current_boot_device(0x%016llx)\n",
+ ioc->name, __func__,
+ (unsigned long long)sas_address));
+ ioc->current_boot_device.device = device;
+ ioc->current_boot_device.is_raid = is_raid;
+ }
+ }
+}
+
+/**
+ * mpt3sas_scsih_sas_device_find_by_sas_address - sas device search
+ * @ioc: per adapter object
+ * @sas_address: sas address
+ * Context: Calling function should acquire ioc->sas_device_lock
+ *
+ * This searches for sas_device based on sas_address, then return sas_device
+ * object.
+ */
+struct _sas_device *
+mpt3sas_scsih_sas_device_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc,
+ u64 sas_address)
+{
+ struct _sas_device *sas_device;
+
+ list_for_each_entry(sas_device, &ioc->sas_device_list, list)
+ if (sas_device->sas_address == sas_address)
+ return sas_device;
+
+ list_for_each_entry(sas_device, &ioc->sas_device_init_list, list)
+ if (sas_device->sas_address == sas_address)
+ return sas_device;
+
+ return NULL;
+}
+
+/**
+ * _scsih_sas_device_find_by_handle - sas device search
+ * @ioc: per adapter object
+ * @handle: sas device handle (assigned by firmware)
+ * Context: Calling function should acquire ioc->sas_device_lock
+ *
+ * This searches for sas_device based on sas_address, then return sas_device
+ * object.
+ */
+static struct _sas_device *
+_scsih_sas_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _sas_device *sas_device;
+
+ list_for_each_entry(sas_device, &ioc->sas_device_list, list)
+ if (sas_device->handle == handle)
+ return sas_device;
+
+ list_for_each_entry(sas_device, &ioc->sas_device_init_list, list)
+ if (sas_device->handle == handle)
+ return sas_device;
+
+ return NULL;
+}
+
+/**
+ * _scsih_sas_device_remove - remove sas_device from list.
+ * @ioc: per adapter object
+ * @sas_device: the sas_device object
+ * Context: This function will acquire ioc->sas_device_lock.
+ *
+ * Removing object and freeing associated memory from the ioc->sas_device_list.
+ */
+static void
+_scsih_sas_device_remove(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_device *sas_device)
+{
+ unsigned long flags;
+
+ if (!sas_device)
+ return;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ list_del(&sas_device->list);
+ kfree(sas_device);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+}
+
+/**
+ * _scsih_device_remove_by_handle - removing device object by handle
+ * @ioc: per adapter object
+ * @handle: device handle
+ *
+ * Return nothing.
+ */
+static void
+_scsih_device_remove_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _sas_device *sas_device;
+ unsigned long flags;
+
+ if (ioc->shost_recovery)
+ return;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+ if (sas_device)
+ list_del(&sas_device->list);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (sas_device)
+ _scsih_remove_device(ioc, sas_device);
+}
+
+/**
+ * mpt3sas_device_remove_by_sas_address - removing device object by sas address
+ * @ioc: per adapter object
+ * @sas_address: device sas_address
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_device_remove_by_sas_address(struct MPT3SAS_ADAPTER *ioc,
+ u64 sas_address)
+{
+ struct _sas_device *sas_device;
+ unsigned long flags;
+
+ if (ioc->shost_recovery)
+ return;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+ sas_address);
+ if (sas_device)
+ list_del(&sas_device->list);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (sas_device)
+ _scsih_remove_device(ioc, sas_device);
+}
+
+/**
+ * _scsih_sas_device_add - insert sas_device to the list.
+ * @ioc: per adapter object
+ * @sas_device: the sas_device object
+ * Context: This function will acquire ioc->sas_device_lock.
+ *
+ * Adding new object to the ioc->sas_device_list.
+ */
+static void
+_scsih_sas_device_add(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_device *sas_device)
+{
+ unsigned long flags;
+
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: handle(0x%04x), sas_addr(0x%016llx)\n",
+ ioc->name, __func__, sas_device->handle,
+ (unsigned long long)sas_device->sas_address));
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ list_add_tail(&sas_device->list, &ioc->sas_device_list);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ if (!mpt3sas_transport_port_add(ioc, sas_device->handle,
+ sas_device->sas_address_parent)) {
+ _scsih_sas_device_remove(ioc, sas_device);
+ } else if (!sas_device->starget) {
+ /*
+ * When asyn scanning is enabled, its not possible to remove
+ * devices while scanning is turned on due to an oops in
+ * scsi_sysfs_add_sdev()->add_device()->sysfs_addrm_start()
+ */
+ if (!ioc->is_driver_loading)
+ mpt3sas_transport_port_remove(ioc,
+ sas_device->sas_address,
+ sas_device->sas_address_parent);
+ _scsih_sas_device_remove(ioc, sas_device);
+ }
+}
+
+/**
+ * _scsih_sas_device_init_add - insert sas_device to the list.
+ * @ioc: per adapter object
+ * @sas_device: the sas_device object
+ * Context: This function will acquire ioc->sas_device_lock.
+ *
+ * Adding new object at driver load time to the ioc->sas_device_init_list.
+ */
+static void
+_scsih_sas_device_init_add(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_device *sas_device)
+{
+ unsigned long flags;
+
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name,
+ __func__, sas_device->handle,
+ (unsigned long long)sas_device->sas_address));
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ list_add_tail(&sas_device->list, &ioc->sas_device_init_list);
+ _scsih_determine_boot_device(ioc, sas_device, 0);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+}
+
+/**
+ * _scsih_raid_device_find_by_id - raid device search
+ * @ioc: per adapter object
+ * @id: sas device target id
+ * @channel: sas device channel
+ * Context: Calling function should acquire ioc->raid_device_lock
+ *
+ * This searches for raid_device based on target id, then return raid_device
+ * object.
+ */
+static struct _raid_device *
+_scsih_raid_device_find_by_id(struct MPT3SAS_ADAPTER *ioc, int id, int channel)
+{
+ struct _raid_device *raid_device, *r;
+
+ r = NULL;
+ list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
+ if (raid_device->id == id && raid_device->channel == channel) {
+ r = raid_device;
+ goto out;
+ }
+ }
+
+ out:
+ return r;
+}
+
+/**
+ * _scsih_raid_device_find_by_handle - raid device search
+ * @ioc: per adapter object
+ * @handle: sas device handle (assigned by firmware)
+ * Context: Calling function should acquire ioc->raid_device_lock
+ *
+ * This searches for raid_device based on handle, then return raid_device
+ * object.
+ */
+static struct _raid_device *
+_scsih_raid_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _raid_device *raid_device, *r;
+
+ r = NULL;
+ list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
+ if (raid_device->handle != handle)
+ continue;
+ r = raid_device;
+ goto out;
+ }
+
+ out:
+ return r;
+}
+
+/**
+ * _scsih_raid_device_find_by_wwid - raid device search
+ * @ioc: per adapter object
+ * @handle: sas device handle (assigned by firmware)
+ * Context: Calling function should acquire ioc->raid_device_lock
+ *
+ * This searches for raid_device based on wwid, then return raid_device
+ * object.
+ */
+static struct _raid_device *
+_scsih_raid_device_find_by_wwid(struct MPT3SAS_ADAPTER *ioc, u64 wwid)
+{
+ struct _raid_device *raid_device, *r;
+
+ r = NULL;
+ list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
+ if (raid_device->wwid != wwid)
+ continue;
+ r = raid_device;
+ goto out;
+ }
+
+ out:
+ return r;
+}
+
+/**
+ * _scsih_raid_device_add - add raid_device object
+ * @ioc: per adapter object
+ * @raid_device: raid_device object
+ *
+ * This is added to the raid_device_list link list.
+ */
+static void
+_scsih_raid_device_add(struct MPT3SAS_ADAPTER *ioc,
+ struct _raid_device *raid_device)
+{
+ unsigned long flags;
+
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: handle(0x%04x), wwid(0x%016llx)\n", ioc->name, __func__,
+ raid_device->handle, (unsigned long long)raid_device->wwid));
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ list_add_tail(&raid_device->list, &ioc->raid_device_list);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+}
+
+/**
+ * _scsih_raid_device_remove - delete raid_device object
+ * @ioc: per adapter object
+ * @raid_device: raid_device object
+ *
+ */
+static void
+_scsih_raid_device_remove(struct MPT3SAS_ADAPTER *ioc,
+ struct _raid_device *raid_device)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ list_del(&raid_device->list);
+ kfree(raid_device);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+}
+
+/**
+ * mpt3sas_scsih_expander_find_by_handle - expander device search
+ * @ioc: per adapter object
+ * @handle: expander handle (assigned by firmware)
+ * Context: Calling function should acquire ioc->sas_device_lock
+ *
+ * This searches for expander device based on handle, then returns the
+ * sas_node object.
+ */
+struct _sas_node *
+mpt3sas_scsih_expander_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _sas_node *sas_expander, *r;
+
+ r = NULL;
+ list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
+ if (sas_expander->handle != handle)
+ continue;
+ r = sas_expander;
+ goto out;
+ }
+ out:
+ return r;
+}
+
+/**
+ * mpt3sas_scsih_expander_find_by_sas_address - expander device search
+ * @ioc: per adapter object
+ * @sas_address: sas address
+ * Context: Calling function should acquire ioc->sas_node_lock.
+ *
+ * This searches for expander device based on sas_address, then returns the
+ * sas_node object.
+ */
+struct _sas_node *
+mpt3sas_scsih_expander_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc,
+ u64 sas_address)
+{
+ struct _sas_node *sas_expander, *r;
+
+ r = NULL;
+ list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
+ if (sas_expander->sas_address != sas_address)
+ continue;
+ r = sas_expander;
+ goto out;
+ }
+ out:
+ return r;
+}
+
+/**
+ * _scsih_expander_node_add - insert expander device to the list.
+ * @ioc: per adapter object
+ * @sas_expander: the sas_device object
+ * Context: This function will acquire ioc->sas_node_lock.
+ *
+ * Adding new object to the ioc->sas_expander_list.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_expander_node_add(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_node *sas_expander)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ list_add_tail(&sas_expander->list, &ioc->sas_expander_list);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+}
+
+/**
+ * _scsih_is_end_device - determines if device is an end device
+ * @device_info: bitfield providing information about the device.
+ * Context: none
+ *
+ * Returns 1 if end device.
+ */
+static int
+_scsih_is_end_device(u32 device_info)
+{
+ if (device_info & MPI2_SAS_DEVICE_INFO_END_DEVICE &&
+ ((device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) |
+ (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) |
+ (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE)))
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * _scsih_scsi_lookup_get - returns scmd entry
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Returns the smid stored scmd pointer.
+ */
+static struct scsi_cmnd *
+_scsih_scsi_lookup_get(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ return ioc->scsi_lookup[smid - 1].scmd;
+}
+
+/**
+ * _scsih_scsi_lookup_get_clear - returns scmd entry
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * Returns the smid stored scmd pointer.
+ * Then will derefrence the stored scmd pointer.
+ */
+static inline struct scsi_cmnd *
+_scsih_scsi_lookup_get_clear(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ unsigned long flags;
+ struct scsi_cmnd *scmd;
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ scmd = ioc->scsi_lookup[smid - 1].scmd;
+ ioc->scsi_lookup[smid - 1].scmd = NULL;
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+
+ return scmd;
+}
+
+/**
+ * _scsih_scsi_lookup_find_by_scmd - scmd lookup
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @scmd: pointer to scsi command object
+ * Context: This function will acquire ioc->scsi_lookup_lock.
+ *
+ * This will search for a scmd pointer in the scsi_lookup array,
+ * returning the revelent smid. A returned value of zero means invalid.
+ */
+static u16
+_scsih_scsi_lookup_find_by_scmd(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd
+ *scmd)
+{
+ u16 smid;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ smid = 0;
+ for (i = 0; i < ioc->scsiio_depth; i++) {
+ if (ioc->scsi_lookup[i].scmd == scmd) {
+ smid = ioc->scsi_lookup[i].smid;
+ goto out;
+ }
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ return smid;
+}
+
+/**
+ * _scsih_scsi_lookup_find_by_target - search for matching channel:id
+ * @ioc: per adapter object
+ * @id: target id
+ * @channel: channel
+ * Context: This function will acquire ioc->scsi_lookup_lock.
+ *
+ * This will search for a matching channel:id in the scsi_lookup array,
+ * returning 1 if found.
+ */
+static u8
+_scsih_scsi_lookup_find_by_target(struct MPT3SAS_ADAPTER *ioc, int id,
+ int channel)
+{
+ u8 found;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ found = 0;
+ for (i = 0 ; i < ioc->scsiio_depth; i++) {
+ if (ioc->scsi_lookup[i].scmd &&
+ (ioc->scsi_lookup[i].scmd->device->id == id &&
+ ioc->scsi_lookup[i].scmd->device->channel == channel)) {
+ found = 1;
+ goto out;
+ }
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ return found;
+}
+
+/**
+ * _scsih_scsi_lookup_find_by_lun - search for matching channel:id:lun
+ * @ioc: per adapter object
+ * @id: target id
+ * @lun: lun number
+ * @channel: channel
+ * Context: This function will acquire ioc->scsi_lookup_lock.
+ *
+ * This will search for a matching channel:id:lun in the scsi_lookup array,
+ * returning 1 if found.
+ */
+static u8
+_scsih_scsi_lookup_find_by_lun(struct MPT3SAS_ADAPTER *ioc, int id,
+ unsigned int lun, int channel)
+{
+ u8 found;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ found = 0;
+ for (i = 0 ; i < ioc->scsiio_depth; i++) {
+ if (ioc->scsi_lookup[i].scmd &&
+ (ioc->scsi_lookup[i].scmd->device->id == id &&
+ ioc->scsi_lookup[i].scmd->device->channel == channel &&
+ ioc->scsi_lookup[i].scmd->device->lun == lun)) {
+ found = 1;
+ goto out;
+ }
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ return found;
+}
+
+
+static void
+_scsih_adjust_queue_depth(struct scsi_device *sdev, int qdepth)
+{
+ struct Scsi_Host *shost = sdev->host;
+ int max_depth;
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ struct _sas_device *sas_device;
+ unsigned long flags;
+
+ max_depth = shost->can_queue;
+
+ /* limit max device queue for SATA to 32 */
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data)
+ goto not_sata;
+ sas_target_priv_data = sas_device_priv_data->sas_target;
+ if (!sas_target_priv_data)
+ goto not_sata;
+ if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))
+ goto not_sata;
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+ sas_device_priv_data->sas_target->sas_address);
+ if (sas_device && sas_device->device_info &
+ MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
+ max_depth = MPT3SAS_SATA_QUEUE_DEPTH;
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ not_sata:
+
+ if (!sdev->tagged_supported)
+ max_depth = 1;
+ if (qdepth > max_depth)
+ qdepth = max_depth;
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+}
+
+/**
+ * _scsih_change_queue_depth - setting device queue depth
+ * @sdev: scsi device struct
+ * @qdepth: requested queue depth
+ * @reason: SCSI_QDEPTH_DEFAULT/SCSI_QDEPTH_QFULL/SCSI_QDEPTH_RAMP_UP
+ * (see include/scsi/scsi_host.h for definition)
+ *
+ * Returns queue depth.
+ */
+static int
+_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
+{
+ if (reason == SCSI_QDEPTH_DEFAULT || reason == SCSI_QDEPTH_RAMP_UP)
+ _scsih_adjust_queue_depth(sdev, qdepth);
+ else if (reason == SCSI_QDEPTH_QFULL)
+ scsi_track_queue_full(sdev, qdepth);
+ else
+ return -EOPNOTSUPP;
+
+ if (sdev->inquiry_len > 7)
+ sdev_printk(KERN_INFO, sdev, "qdepth(%d), tagged(%d), " \
+ "simple(%d), ordered(%d), scsi_level(%d), cmd_que(%d)\n",
+ sdev->queue_depth, sdev->tagged_supported, sdev->simple_tags,
+ sdev->ordered_tags, sdev->scsi_level,
+ (sdev->inquiry[7] & 2) >> 1);
+
+ return sdev->queue_depth;
+}
+
+/**
+ * _scsih_change_queue_type - changing device queue tag type
+ * @sdev: scsi device struct
+ * @tag_type: requested tag type
+ *
+ * Returns queue tag type.
+ */
+static int
+_scsih_change_queue_type(struct scsi_device *sdev, int tag_type)
+{
+ if (sdev->tagged_supported) {
+ scsi_set_tag_type(sdev, tag_type);
+ if (tag_type)
+ scsi_activate_tcq(sdev, sdev->queue_depth);
+ else
+ scsi_deactivate_tcq(sdev, sdev->queue_depth);
+ } else
+ tag_type = 0;
+
+ return tag_type;
+}
+
+
+/**
+ * _scsih_target_alloc - target add routine
+ * @starget: scsi target struct
+ *
+ * Returns 0 if ok. Any other return is assumed to be an error and
+ * the device is ignored.
+ */
+static int
+_scsih_target_alloc(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(&starget->dev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ struct _sas_device *sas_device;
+ struct _raid_device *raid_device;
+ unsigned long flags;
+ struct sas_rphy *rphy;
+
+ sas_target_priv_data = kzalloc(sizeof(struct scsi_target), GFP_KERNEL);
+ if (!sas_target_priv_data)
+ return -ENOMEM;
+
+ starget->hostdata = sas_target_priv_data;
+ sas_target_priv_data->starget = starget;
+ sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE;
+
+ /* RAID volumes */
+ if (starget->channel == RAID_CHANNEL) {
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_id(ioc, starget->id,
+ starget->channel);
+ if (raid_device) {
+ 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;
+ raid_device->starget = starget;
+ }
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ return 0;
+ }
+
+ /* sas/sata devices */
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ rphy = dev_to_rphy(starget->dev.parent);
+ sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+ rphy->identify.sas_address);
+
+ if (sas_device) {
+ sas_target_priv_data->handle = sas_device->handle;
+ sas_target_priv_data->sas_address = sas_device->sas_address;
+ sas_device->starget = starget;
+ sas_device->id = starget->id;
+ sas_device->channel = starget->channel;
+ if (test_bit(sas_device->handle, ioc->pd_handles))
+ sas_target_priv_data->flags |=
+ MPT_TARGET_FLAGS_RAID_COMPONENT;
+ if (sas_device->fast_path)
+ sas_target_priv_data->flags |= MPT_TARGET_FASTPATH_IO;
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ return 0;
+}
+
+/**
+ * _scsih_target_destroy - target destroy routine
+ * @starget: scsi target struct
+ *
+ * Returns nothing.
+ */
+static void
+_scsih_target_destroy(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(&starget->dev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ struct _sas_device *sas_device;
+ struct _raid_device *raid_device;
+ unsigned long flags;
+ struct sas_rphy *rphy;
+
+ sas_target_priv_data = starget->hostdata;
+ if (!sas_target_priv_data)
+ return;
+
+ if (starget->channel == RAID_CHANNEL) {
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_id(ioc, starget->id,
+ starget->channel);
+ if (raid_device) {
+ raid_device->starget = NULL;
+ raid_device->sdev = NULL;
+ }
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ goto out;
+ }
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ rphy = dev_to_rphy(starget->dev.parent);
+ sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+ rphy->identify.sas_address);
+ if (sas_device && (sas_device->starget == starget) &&
+ (sas_device->id == starget->id) &&
+ (sas_device->channel == starget->channel))
+ sas_device->starget = NULL;
+
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ out:
+ kfree(sas_target_priv_data);
+ starget->hostdata = NULL;
+}
+
+/**
+ * _scsih_slave_alloc - device add routine
+ * @sdev: scsi device struct
+ *
+ * Returns 0 if ok. Any other return is assumed to be an error and
+ * the device is ignored.
+ */
+static int
+_scsih_slave_alloc(struct scsi_device *sdev)
+{
+ struct Scsi_Host *shost;
+ struct MPT3SAS_ADAPTER *ioc;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct scsi_target *starget;
+ struct _raid_device *raid_device;
+ unsigned long flags;
+
+ sas_device_priv_data = kzalloc(sizeof(struct scsi_device), GFP_KERNEL);
+ if (!sas_device_priv_data)
+ return -ENOMEM;
+
+ sas_device_priv_data->lun = sdev->lun;
+ sas_device_priv_data->flags = MPT_DEVICE_FLAGS_INIT;
+
+ starget = scsi_target(sdev);
+ sas_target_priv_data = starget->hostdata;
+ sas_target_priv_data->num_luns++;
+ sas_device_priv_data->sas_target = sas_target_priv_data;
+ sdev->hostdata = sas_device_priv_data;
+ if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT))
+ sdev->no_uld_attach = 1;
+
+ shost = dev_to_shost(&starget->dev);
+ ioc = shost_priv(shost);
+ if (starget->channel == RAID_CHANNEL) {
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_id(ioc,
+ starget->id, starget->channel);
+ if (raid_device)
+ raid_device->sdev = sdev; /* raid is single lun */
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ }
+
+ return 0;
+}
+
+/**
+ * _scsih_slave_destroy - device destroy routine
+ * @sdev: scsi device struct
+ *
+ * Returns nothing.
+ */
+static void
+_scsih_slave_destroy(struct scsi_device *sdev)
+{
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ struct scsi_target *starget;
+ struct Scsi_Host *shost;
+ struct MPT3SAS_ADAPTER *ioc;
+ struct _sas_device *sas_device;
+ unsigned long flags;
+
+ if (!sdev->hostdata)
+ return;
+
+ starget = scsi_target(sdev);
+ sas_target_priv_data = starget->hostdata;
+ sas_target_priv_data->num_luns--;
+
+ shost = dev_to_shost(&starget->dev);
+ ioc = shost_priv(shost);
+
+ if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) {
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+ sas_target_priv_data->sas_address);
+ if (sas_device && !sas_target_priv_data->num_luns)
+ sas_device->starget = NULL;
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ }
+
+ kfree(sdev->hostdata);
+ sdev->hostdata = NULL;
+}
+
+/**
+ * _scsih_display_sata_capabilities - sata capabilities
+ * @ioc: per adapter object
+ * @handle: device handle
+ * @sdev: scsi device struct
+ */
+static void
+_scsih_display_sata_capabilities(struct MPT3SAS_ADAPTER *ioc,
+ u16 handle, struct scsi_device *sdev)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ u32 ioc_status;
+ u16 flags;
+ u32 device_info;
+
+ if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ flags = le16_to_cpu(sas_device_pg0.Flags);
+ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
+
+ sdev_printk(KERN_INFO, sdev,
+ "atapi(%s), ncq(%s), asyn_notify(%s), smart(%s), fua(%s), "
+ "sw_preserve(%s)\n",
+ (device_info & MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE) ? "y" : "n",
+ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED) ? "y" : "n",
+ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY) ? "y" :
+ "n",
+ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_SMART_SUPPORTED) ? "y" : "n",
+ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED) ? "y" : "n",
+ (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE) ? "y" : "n");
+}
+
+/*
+ * raid transport support -
+ * Enabled for SLES11 and newer, in older kernels the driver will panic when
+ * unloading the driver followed by a load - I beleive that the subroutine
+ * raid_class_release() is not cleaning up properly.
+ */
+
+/**
+ * _scsih_is_raid - return boolean indicating device is raid volume
+ * @dev the device struct object
+ */
+static int
+_scsih_is_raid(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+
+ return (sdev->channel == RAID_CHANNEL) ? 1 : 0;
+}
+
+/**
+ * _scsih_get_resync - get raid volume resync percent complete
+ * @dev the device struct object
+ */
+static void
+_scsih_get_resync(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(sdev->host);
+ static struct _raid_device *raid_device;
+ unsigned long flags;
+ Mpi2RaidVolPage0_t vol_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u32 volume_status_flags;
+ u8 percent_complete;
+ u16 handle;
+
+ percent_complete = 0;
+ handle = 0;
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id,
+ sdev->channel);
+ if (raid_device) {
+ handle = raid_device->handle;
+ percent_complete = raid_device->percent_complete;
+ }
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+
+ if (!handle)
+ goto out;
+
+ if (mpt3sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0,
+ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle,
+ sizeof(Mpi2RaidVolPage0_t))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ percent_complete = 0;
+ goto out;
+ }
+
+ volume_status_flags = le32_to_cpu(vol_pg0.VolumeStatusFlags);
+ if (!(volume_status_flags &
+ MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS))
+ percent_complete = 0;
+
+ out:
+ raid_set_resync(mpt3sas_raid_template, dev, percent_complete);
+}
+
+/**
+ * _scsih_get_state - get raid volume level
+ * @dev the device struct object
+ */
+static void
+_scsih_get_state(struct device *dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(sdev->host);
+ static struct _raid_device *raid_device;
+ unsigned long flags;
+ Mpi2RaidVolPage0_t vol_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u32 volstate;
+ enum raid_state state = RAID_STATE_UNKNOWN;
+ u16 handle = 0;
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id,
+ sdev->channel);
+ if (raid_device)
+ handle = raid_device->handle;
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+
+ if (!raid_device)
+ goto out;
+
+ if (mpt3sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0,
+ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle,
+ sizeof(Mpi2RaidVolPage0_t))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ volstate = le32_to_cpu(vol_pg0.VolumeStatusFlags);
+ if (volstate & MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS) {
+ state = RAID_STATE_RESYNCING;
+ goto out;
+ }
+
+ switch (vol_pg0.VolumeState) {
+ case MPI2_RAID_VOL_STATE_OPTIMAL:
+ case MPI2_RAID_VOL_STATE_ONLINE:
+ state = RAID_STATE_ACTIVE;
+ break;
+ case MPI2_RAID_VOL_STATE_DEGRADED:
+ state = RAID_STATE_DEGRADED;
+ break;
+ case MPI2_RAID_VOL_STATE_FAILED:
+ case MPI2_RAID_VOL_STATE_MISSING:
+ state = RAID_STATE_OFFLINE;
+ break;
+ }
+ out:
+ raid_set_state(mpt3sas_raid_template, dev, state);
+}
+
+/**
+ * _scsih_set_level - set raid level
+ * @sdev: scsi device struct
+ * @volume_type: volume type
+ */
+static void
+_scsih_set_level(struct scsi_device *sdev, u8 volume_type)
+{
+ enum raid_level level = RAID_LEVEL_UNKNOWN;
+
+ switch (volume_type) {
+ case MPI2_RAID_VOL_TYPE_RAID0:
+ level = RAID_LEVEL_0;
+ break;
+ case MPI2_RAID_VOL_TYPE_RAID10:
+ level = RAID_LEVEL_10;
+ break;
+ case MPI2_RAID_VOL_TYPE_RAID1E:
+ level = RAID_LEVEL_1E;
+ break;
+ case MPI2_RAID_VOL_TYPE_RAID1:
+ level = RAID_LEVEL_1;
+ break;
+ }
+
+ raid_set_level(mpt3sas_raid_template, &sdev->sdev_gendev, level);
+}
+
+
+/**
+ * _scsih_get_volume_capabilities - volume capabilities
+ * @ioc: per adapter object
+ * @sas_device: the raid_device object
+ *
+ * Returns 0 for success, else 1
+ */
+static int
+_scsih_get_volume_capabilities(struct MPT3SAS_ADAPTER *ioc,
+ struct _raid_device *raid_device)
+{
+ Mpi2RaidVolPage0_t *vol_pg0;
+ Mpi2RaidPhysDiskPage0_t pd_pg0;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 sz;
+ u8 num_pds;
+
+ if ((mpt3sas_config_get_number_pds(ioc, raid_device->handle,
+ &num_pds)) || !num_pds) {
+ dfailprintk(ioc, pr_warn(MPT3SAS_FMT
+ "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__,
+ __func__));
+ return 1;
+ }
+
+ raid_device->num_pds = num_pds;
+ sz = offsetof(Mpi2RaidVolPage0_t, PhysDisk) + (num_pds *
+ sizeof(Mpi2RaidVol0PhysDisk_t));
+ vol_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!vol_pg0) {
+ dfailprintk(ioc, pr_warn(MPT3SAS_FMT
+ "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__,
+ __func__));
+ return 1;
+ }
+
+ if ((mpt3sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0,
+ MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) {
+ dfailprintk(ioc, pr_warn(MPT3SAS_FMT
+ "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__,
+ __func__));
+ kfree(vol_pg0);
+ return 1;
+ }
+
+ raid_device->volume_type = vol_pg0->VolumeType;
+
+ /* figure out what the underlying devices are by
+ * obtaining the device_info bits for the 1st device
+ */
+ if (!(mpt3sas_config_get_phys_disk_pg0(ioc, &mpi_reply,
+ &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM,
+ vol_pg0->PhysDisk[0].PhysDiskNum))) {
+ if (!(mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply,
+ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
+ le16_to_cpu(pd_pg0.DevHandle)))) {
+ raid_device->device_info =
+ le32_to_cpu(sas_device_pg0.DeviceInfo);
+ }
+ }
+
+ kfree(vol_pg0);
+ return 0;
+}
+
+
+
+/**
+ * _scsih_enable_tlr - setting TLR flags
+ * @ioc: per adapter object
+ * @sdev: scsi device struct
+ *
+ * Enabling Transaction Layer Retries for tape devices when
+ * vpd page 0x90 is present
+ *
+ */
+static void
+_scsih_enable_tlr(struct MPT3SAS_ADAPTER *ioc, struct scsi_device *sdev)
+{
+
+ /* only for TAPE */
+ if (sdev->type != TYPE_TAPE)
+ return;
+
+ if (!(ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR))
+ return;
+
+ sas_enable_tlr(sdev);
+ sdev_printk(KERN_INFO, sdev, "TLR %s\n",
+ sas_is_tlr_enabled(sdev) ? "Enabled" : "Disabled");
+ return;
+
+}
+
+/**
+ * _scsih_slave_configure - device configure routine.
+ * @sdev: scsi device struct
+ *
+ * Returns 0 if ok. Any other return is assumed to be an error and
+ * the device is ignored.
+ */
+static int
+_scsih_slave_configure(struct scsi_device *sdev)
+{
+ struct Scsi_Host *shost = sdev->host;
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ struct _sas_device *sas_device;
+ struct _raid_device *raid_device;
+ unsigned long flags;
+ int qdepth;
+ u8 ssp_target = 0;
+ char *ds = "";
+ char *r_level = "";
+ u16 handle, volume_handle = 0;
+ u64 volume_wwid = 0;
+
+ qdepth = 1;
+ sas_device_priv_data = sdev->hostdata;
+ sas_device_priv_data->configured_lun = 1;
+ sas_device_priv_data->flags &= ~MPT_DEVICE_FLAGS_INIT;
+ sas_target_priv_data = sas_device_priv_data->sas_target;
+ handle = sas_target_priv_data->handle;
+
+ /* raid volume handling */
+ if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME) {
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_handle(ioc, handle);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ if (!raid_device) {
+ dfailprintk(ioc, pr_warn(MPT3SAS_FMT
+ "failure at %s:%d/%s()!\n", ioc->name, __FILE__,
+ __LINE__, __func__));
+ return 1;
+ }
+
+ if (_scsih_get_volume_capabilities(ioc, raid_device)) {
+ dfailprintk(ioc, pr_warn(MPT3SAS_FMT
+ "failure at %s:%d/%s()!\n", ioc->name, __FILE__,
+ __LINE__, __func__));
+ return 1;
+ }
+
+
+ /* RAID Queue Depth Support
+ * IS volume = underlying qdepth of drive type, either
+ * MPT3SAS_SAS_QUEUE_DEPTH or MPT3SAS_SATA_QUEUE_DEPTH
+ * IM/IME/R10 = 128 (MPT3SAS_RAID_QUEUE_DEPTH)
+ */
+ if (raid_device->device_info &
+ MPI2_SAS_DEVICE_INFO_SSP_TARGET) {
+ qdepth = MPT3SAS_SAS_QUEUE_DEPTH;
+ ds = "SSP";
+ } else {
+ qdepth = MPT3SAS_SATA_QUEUE_DEPTH;
+ if (raid_device->device_info &
+ MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
+ ds = "SATA";
+ else
+ ds = "STP";
+ }
+
+ switch (raid_device->volume_type) {
+ case MPI2_RAID_VOL_TYPE_RAID0:
+ r_level = "RAID0";
+ break;
+ case MPI2_RAID_VOL_TYPE_RAID1E:
+ qdepth = MPT3SAS_RAID_QUEUE_DEPTH;
+ if (ioc->manu_pg10.OEMIdentifier &&
+ (le32_to_cpu(ioc->manu_pg10.GenericFlags0) &
+ MFG10_GF0_R10_DISPLAY) &&
+ !(raid_device->num_pds % 2))
+ r_level = "RAID10";
+ else
+ r_level = "RAID1E";
+ break;
+ case MPI2_RAID_VOL_TYPE_RAID1:
+ qdepth = MPT3SAS_RAID_QUEUE_DEPTH;
+ r_level = "RAID1";
+ break;
+ case MPI2_RAID_VOL_TYPE_RAID10:
+ qdepth = MPT3SAS_RAID_QUEUE_DEPTH;
+ r_level = "RAID10";
+ break;
+ case MPI2_RAID_VOL_TYPE_UNKNOWN:
+ default:
+ qdepth = MPT3SAS_RAID_QUEUE_DEPTH;
+ r_level = "RAIDX";
+ 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);
+
+
+ _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT);
+
+/* raid transport support */
+ _scsih_set_level(sdev, raid_device->volume_type);
+ return 0;
+ }
+
+ /* non-raid handling */
+ if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) {
+ if (mpt3sas_config_get_volume_handle(ioc, handle,
+ &volume_handle)) {
+ dfailprintk(ioc, pr_warn(MPT3SAS_FMT
+ "failure at %s:%d/%s()!\n", ioc->name,
+ __FILE__, __LINE__, __func__));
+ return 1;
+ }
+ if (volume_handle && mpt3sas_config_get_volume_wwid(ioc,
+ volume_handle, &volume_wwid)) {
+ dfailprintk(ioc, pr_warn(MPT3SAS_FMT
+ "failure at %s:%d/%s()!\n", ioc->name,
+ __FILE__, __LINE__, __func__));
+ return 1;
+ }
+ }
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+ sas_device_priv_data->sas_target->sas_address);
+ if (!sas_device) {
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ dfailprintk(ioc, pr_warn(MPT3SAS_FMT
+ "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__,
+ __func__));
+ return 1;
+ }
+
+ sas_device->volume_handle = volume_handle;
+ sas_device->volume_wwid = volume_wwid;
+ if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) {
+ qdepth = MPT3SAS_SAS_QUEUE_DEPTH;
+ ssp_target = 1;
+ ds = "SSP";
+ } else {
+ qdepth = MPT3SAS_SATA_QUEUE_DEPTH;
+ if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET)
+ ds = "STP";
+ else if (sas_device->device_info &
+ MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
+ ds = "SATA";
+ }
+
+ sdev_printk(KERN_INFO, sdev, "%s: handle(0x%04x), " \
+ "sas_addr(0x%016llx), phy(%d), device_name(0x%016llx)\n",
+ ds, handle, (unsigned long long)sas_device->sas_address,
+ sas_device->phy, (unsigned long long)sas_device->device_name);
+ sdev_printk(KERN_INFO, sdev,
+ "%s: enclosure_logical_id(0x%016llx), slot(%d)\n",
+ ds, (unsigned long long)
+ sas_device->enclosure_logical_id, sas_device->slot);
+
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ if (!ssp_target)
+ _scsih_display_sata_capabilities(ioc, handle, sdev);
+
+
+ _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT);
+
+ if (ssp_target) {
+ sas_read_port_mode_page(sdev);
+ _scsih_enable_tlr(ioc, sdev);
+ }
+
+ return 0;
+}
+
+/**
+ * _scsih_bios_param - fetch head, sector, cylinder info for a disk
+ * @sdev: scsi device struct
+ * @bdev: pointer to block device context
+ * @capacity: device size (in 512 byte sectors)
+ * @params: three element array to place output:
+ * params[0] number of heads (max 255)
+ * params[1] number of sectors (max 63)
+ * params[2] number of cylinders
+ *
+ * Return nothing.
+ */
+static int
+_scsih_bios_param(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int params[])
+{
+ int heads;
+ int sectors;
+ sector_t cylinders;
+ ulong dummy;
+
+ heads = 64;
+ sectors = 32;
+
+ dummy = heads * sectors;
+ cylinders = capacity;
+ sector_div(cylinders, dummy);
+
+ /*
+ * Handle extended translation size for logical drives
+ * > 1Gb
+ */
+ if ((ulong)capacity >= 0x200000) {
+ heads = 255;
+ sectors = 63;
+ dummy = heads * sectors;
+ cylinders = capacity;
+ sector_div(cylinders, dummy);
+ }
+
+ /* return result */
+ params[0] = heads;
+ params[1] = sectors;
+ params[2] = cylinders;
+
+ return 0;
+}
+
+/**
+ * _scsih_response_code - translation of device response code
+ * @ioc: per adapter object
+ * @response_code: response code returned by the device
+ *
+ * Return nothing.
+ */
+static void
+_scsih_response_code(struct MPT3SAS_ADAPTER *ioc, u8 response_code)
+{
+ char *desc;
+
+ switch (response_code) {
+ case MPI2_SCSITASKMGMT_RSP_TM_COMPLETE:
+ desc = "task management request completed";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_INVALID_FRAME:
+ desc = "invalid frame";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED:
+ desc = "task management request not supported";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_TM_FAILED:
+ desc = "task management request failed";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED:
+ desc = "task management request succeeded";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN:
+ desc = "invalid lun";
+ break;
+ case 0xA:
+ desc = "overlapped tag attempted";
+ break;
+ case MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC:
+ desc = "task queued, however not sent to target";
+ break;
+ default:
+ desc = "unknown";
+ break;
+ }
+ pr_warn(MPT3SAS_FMT "response_code(0x%01x): %s\n",
+ ioc->name, response_code, desc);
+}
+
+/**
+ * _scsih_tm_done - tm completion routine
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: none.
+ *
+ * The callback handler when using scsih_issue_tm.
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+static u8
+_scsih_tm_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+
+ if (ioc->tm_cmds.status == MPT3_CMD_NOT_USED)
+ return 1;
+ if (ioc->tm_cmds.smid != smid)
+ return 1;
+ mpt3sas_base_flush_reply_queues(ioc);
+ ioc->tm_cmds.status |= MPT3_CMD_COMPLETE;
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ if (mpi_reply) {
+ memcpy(ioc->tm_cmds.reply, mpi_reply, mpi_reply->MsgLength*4);
+ ioc->tm_cmds.status |= MPT3_CMD_REPLY_VALID;
+ }
+ ioc->tm_cmds.status &= ~MPT3_CMD_PENDING;
+ complete(&ioc->tm_cmds.done);
+ return 1;
+}
+
+/**
+ * mpt3sas_scsih_set_tm_flag - set per target tm_busy
+ * @ioc: per adapter object
+ * @handle: device handle
+ *
+ * During taskmangement request, we need to freeze the device queue.
+ */
+void
+mpt3sas_scsih_set_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct scsi_device *sdev;
+ u8 skip = 0;
+
+ shost_for_each_device(sdev, ioc->shost) {
+ if (skip)
+ continue;
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data)
+ continue;
+ if (sas_device_priv_data->sas_target->handle == handle) {
+ sas_device_priv_data->sas_target->tm_busy = 1;
+ skip = 1;
+ ioc->ignore_loginfos = 1;
+ }
+ }
+}
+
+/**
+ * mpt3sas_scsih_clear_tm_flag - clear per target tm_busy
+ * @ioc: per adapter object
+ * @handle: device handle
+ *
+ * During taskmangement request, we need to freeze the device queue.
+ */
+void
+mpt3sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct scsi_device *sdev;
+ u8 skip = 0;
+
+ shost_for_each_device(sdev, ioc->shost) {
+ if (skip)
+ continue;
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data)
+ continue;
+ if (sas_device_priv_data->sas_target->handle == handle) {
+ sas_device_priv_data->sas_target->tm_busy = 0;
+ skip = 1;
+ ioc->ignore_loginfos = 0;
+ }
+ }
+}
+
+/**
+ * mpt3sas_scsih_issue_tm - main routine for sending tm requests
+ * @ioc: per adapter struct
+ * @device_handle: device handle
+ * @channel: the channel assigned by the OS
+ * @id: the id assigned by the OS
+ * @lun: lun number
+ * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h)
+ * @smid_task: smid assigned to the task
+ * @timeout: timeout in seconds
+ * @serial_number: the serial_number from scmd
+ * @m_type: TM_MUTEX_ON or TM_MUTEX_OFF
+ * Context: user
+ *
+ * A generic API for sending task management requests to firmware.
+ *
+ * The callback index is set inside `ioc->tm_cb_idx`.
+ *
+ * Return SUCCESS or FAILED.
+ */
+int
+mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, uint channel,
+ uint id, uint lun, u8 type, u16 smid_task, ulong timeout,
+ unsigned long serial_number, enum mutex_type m_type)
+{
+ Mpi2SCSITaskManagementRequest_t *mpi_request;
+ Mpi2SCSITaskManagementReply_t *mpi_reply;
+ u16 smid = 0;
+ u32 ioc_state;
+ unsigned long timeleft;
+ struct scsiio_tracker *scsi_lookup = NULL;
+ int rc;
+
+ if (m_type == TM_MUTEX_ON)
+ mutex_lock(&ioc->tm_cmds.mutex);
+ if (ioc->tm_cmds.status != MPT3_CMD_NOT_USED) {
+ pr_info(MPT3SAS_FMT "%s: tm_cmd busy!!!\n",
+ __func__, ioc->name);
+ rc = FAILED;
+ goto err_out;
+ }
+
+ if (ioc->shost_recovery || ioc->remove_host ||
+ ioc->pci_error_recovery) {
+ pr_info(MPT3SAS_FMT "%s: host reset in progress!\n",
+ __func__, ioc->name);
+ rc = FAILED;
+ goto err_out;
+ }
+
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 0);
+ if (ioc_state & MPI2_DOORBELL_USED) {
+ dhsprintk(ioc, pr_info(MPT3SAS_FMT
+ "unexpected doorbell active!\n", ioc->name));
+ rc = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ rc = (!rc) ? SUCCESS : FAILED;
+ goto err_out;
+ }
+
+ if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
+ mpt3sas_base_fault_info(ioc, ioc_state &
+ MPI2_DOORBELL_DATA_MASK);
+ rc = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ rc = (!rc) ? SUCCESS : FAILED;
+ goto err_out;
+ }
+
+ smid = mpt3sas_base_get_smid_hpr(ioc, ioc->tm_cb_idx);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ rc = FAILED;
+ goto err_out;
+ }
+
+ if (type == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK)
+ scsi_lookup = &ioc->scsi_lookup[smid_task - 1];
+
+ dtmprintk(ioc, pr_info(MPT3SAS_FMT
+ "sending tm: handle(0x%04x), task_type(0x%02x), smid(%d)\n",
+ ioc->name, handle, type, smid_task));
+ ioc->tm_cmds.status = MPT3_CMD_PENDING;
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->tm_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t));
+ memset(ioc->tm_cmds.reply, 0, sizeof(Mpi2SCSITaskManagementReply_t));
+ mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
+ mpi_request->DevHandle = cpu_to_le16(handle);
+ mpi_request->TaskType = type;
+ mpi_request->TaskMID = cpu_to_le16(smid_task);
+ int_to_scsilun(lun, (struct scsi_lun *)mpi_request->LUN);
+ mpt3sas_scsih_set_tm_flag(ioc, handle);
+ init_completion(&ioc->tm_cmds.done);
+ mpt3sas_base_put_smid_hi_priority(ioc, smid);
+ timeleft = wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ);
+ if (!(ioc->tm_cmds.status & MPT3_CMD_COMPLETE)) {
+ pr_err(MPT3SAS_FMT "%s: timeout\n",
+ ioc->name, __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2SCSITaskManagementRequest_t)/4);
+ if (!(ioc->tm_cmds.status & MPT3_CMD_RESET)) {
+ rc = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ rc = (!rc) ? SUCCESS : FAILED;
+ ioc->tm_cmds.status = MPT3_CMD_NOT_USED;
+ mpt3sas_scsih_clear_tm_flag(ioc, handle);
+ goto err_out;
+ }
+ }
+
+ if (ioc->tm_cmds.status & MPT3_CMD_REPLY_VALID) {
+ mpt3sas_trigger_master(ioc, MASTER_TRIGGER_TASK_MANAGMENT);
+ mpi_reply = ioc->tm_cmds.reply;
+ dtmprintk(ioc, pr_info(MPT3SAS_FMT "complete tm: " \
+ "ioc_status(0x%04x), loginfo(0x%08x), term_count(0x%08x)\n",
+ ioc->name, le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo),
+ le32_to_cpu(mpi_reply->TerminationCount)));
+ if (ioc->logging_level & MPT_DEBUG_TM) {
+ _scsih_response_code(ioc, mpi_reply->ResponseCode);
+ if (mpi_reply->IOCStatus)
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2SCSITaskManagementRequest_t)/4);
+ }
+ }
+
+ switch (type) {
+ case MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK:
+ rc = SUCCESS;
+ if (scsi_lookup->scmd == NULL)
+ break;
+ rc = FAILED;
+ break;
+
+ case MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET:
+ if (_scsih_scsi_lookup_find_by_target(ioc, id, channel))
+ rc = FAILED;
+ else
+ rc = SUCCESS;
+ break;
+ case MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET:
+ case MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET:
+ if (_scsih_scsi_lookup_find_by_lun(ioc, id, lun, channel))
+ rc = FAILED;
+ else
+ rc = SUCCESS;
+ break;
+ case MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK:
+ rc = SUCCESS;
+ break;
+ default:
+ rc = FAILED;
+ break;
+ }
+
+ mpt3sas_scsih_clear_tm_flag(ioc, handle);
+ ioc->tm_cmds.status = MPT3_CMD_NOT_USED;
+ if (m_type == TM_MUTEX_ON)
+ mutex_unlock(&ioc->tm_cmds.mutex);
+
+ return rc;
+
+ err_out:
+ if (m_type == TM_MUTEX_ON)
+ mutex_unlock(&ioc->tm_cmds.mutex);
+ return rc;
+}
+
+/**
+ * _scsih_tm_display_info - displays info about the device
+ * @ioc: per adapter struct
+ * @scmd: pointer to scsi command object
+ *
+ * Called by task management callback handlers.
+ */
+static void
+_scsih_tm_display_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd)
+{
+ struct scsi_target *starget = scmd->device->sdev_target;
+ struct MPT3SAS_TARGET *priv_target = starget->hostdata;
+ struct _sas_device *sas_device = NULL;
+ unsigned long flags;
+ char *device_str = NULL;
+
+ if (!priv_target)
+ return;
+ device_str = "volume";
+
+ scsi_print_command(scmd);
+ if (priv_target->flags & MPT_TARGET_FLAGS_VOLUME) {
+ 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 = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+ priv_target->sas_address);
+ if (sas_device) {
+ if (priv_target->flags &
+ MPT_TARGET_FLAGS_RAID_COMPONENT) {
+ starget_printk(KERN_INFO, starget,
+ "volume handle(0x%04x), "
+ "volume wwid(0x%016llx)\n",
+ sas_device->volume_handle,
+ (unsigned long long)sas_device->volume_wwid);
+ }
+ starget_printk(KERN_INFO, starget,
+ "handle(0x%04x), sas_address(0x%016llx), phy(%d)\n",
+ sas_device->handle,
+ (unsigned long long)sas_device->sas_address,
+ sas_device->phy);
+ starget_printk(KERN_INFO, starget,
+ "enclosure_logical_id(0x%016llx), slot(%d)\n",
+ (unsigned long long)sas_device->enclosure_logical_id,
+ sas_device->slot);
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ }
+}
+
+/**
+ * _scsih_abort - eh threads main abort routine
+ * @scmd: pointer to scsi command object
+ *
+ * Returns SUCCESS if command aborted else FAILED
+ */
+static int
+_scsih_abort(struct scsi_cmnd *scmd)
+{
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ u16 smid;
+ u16 handle;
+ int r;
+
+ sdev_printk(KERN_INFO, scmd->device,
+ "attempting task abort! scmd(%p)\n", scmd);
+ _scsih_tm_display_info(ioc, scmd);
+
+ sas_device_priv_data = scmd->device->hostdata;
+ if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {
+ sdev_printk(KERN_INFO, scmd->device,
+ "device been deleted! scmd(%p)\n", scmd);
+ scmd->result = DID_NO_CONNECT << 16;
+ scmd->scsi_done(scmd);
+ r = SUCCESS;
+ goto out;
+ }
+
+ /* search for the command */
+ smid = _scsih_scsi_lookup_find_by_scmd(ioc, scmd);
+ if (!smid) {
+ scmd->result = DID_RESET << 16;
+ r = SUCCESS;
+ goto out;
+ }
+
+ /* for hidden raid components and volumes this is not supported */
+ if (sas_device_priv_data->sas_target->flags &
+ MPT_TARGET_FLAGS_RAID_COMPONENT ||
+ sas_device_priv_data->sas_target->flags & MPT_TARGET_FLAGS_VOLUME) {
+ scmd->result = DID_RESET << 16;
+ r = FAILED;
+ goto out;
+ }
+
+ mpt3sas_halt_firmware(ioc);
+
+ handle = sas_device_priv_data->sas_target->handle;
+ r = mpt3sas_scsih_issue_tm(ioc, handle, scmd->device->channel,
+ scmd->device->id, scmd->device->lun,
+ MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30,
+ scmd->serial_number, TM_MUTEX_ON);
+
+ out:
+ sdev_printk(KERN_INFO, scmd->device, "task abort: %s scmd(%p)\n",
+ ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+ return r;
+}
+
+/**
+ * _scsih_dev_reset - eh threads main device reset routine
+ * @scmd: pointer to scsi command object
+ *
+ * Returns SUCCESS if command aborted else FAILED
+ */
+static int
+_scsih_dev_reset(struct scsi_cmnd *scmd)
+{
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct _sas_device *sas_device;
+ unsigned long flags;
+ u16 handle;
+ int r;
+
+ sdev_printk(KERN_INFO, scmd->device,
+ "attempting device reset! scmd(%p)\n", scmd);
+ _scsih_tm_display_info(ioc, scmd);
+
+ sas_device_priv_data = scmd->device->hostdata;
+ if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {
+ sdev_printk(KERN_INFO, scmd->device,
+ "device been deleted! scmd(%p)\n", scmd);
+ scmd->result = DID_NO_CONNECT << 16;
+ scmd->scsi_done(scmd);
+ r = SUCCESS;
+ goto out;
+ }
+
+ /* for hidden raid components obtain the volume_handle */
+ handle = 0;
+ if (sas_device_priv_data->sas_target->flags &
+ MPT_TARGET_FLAGS_RAID_COMPONENT) {
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc,
+ sas_device_priv_data->sas_target->handle);
+ if (sas_device)
+ handle = sas_device->volume_handle;
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ } else
+ handle = sas_device_priv_data->sas_target->handle;
+
+ if (!handle) {
+ scmd->result = DID_RESET << 16;
+ r = FAILED;
+ goto out;
+ }
+
+ r = mpt3sas_scsih_issue_tm(ioc, handle, scmd->device->channel,
+ scmd->device->id, scmd->device->lun,
+ MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, 0, 30, 0,
+ TM_MUTEX_ON);
+
+ out:
+ sdev_printk(KERN_INFO, scmd->device, "device reset: %s scmd(%p)\n",
+ ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+ return r;
+}
+
+/**
+ * _scsih_target_reset - eh threads main target reset routine
+ * @scmd: pointer to scsi command object
+ *
+ * Returns SUCCESS if command aborted else FAILED
+ */
+static int
+_scsih_target_reset(struct scsi_cmnd *scmd)
+{
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct _sas_device *sas_device;
+ unsigned long flags;
+ u16 handle;
+ int r;
+ struct scsi_target *starget = scmd->device->sdev_target;
+
+ starget_printk(KERN_INFO, starget, "attempting target reset! scmd(%p)\n",
+ scmd);
+ _scsih_tm_display_info(ioc, scmd);
+
+ sas_device_priv_data = scmd->device->hostdata;
+ if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {
+ starget_printk(KERN_INFO, starget, "target been deleted! scmd(%p)\n",
+ scmd);
+ scmd->result = DID_NO_CONNECT << 16;
+ scmd->scsi_done(scmd);
+ r = SUCCESS;
+ goto out;
+ }
+
+ /* for hidden raid components obtain the volume_handle */
+ handle = 0;
+ if (sas_device_priv_data->sas_target->flags &
+ MPT_TARGET_FLAGS_RAID_COMPONENT) {
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc,
+ sas_device_priv_data->sas_target->handle);
+ if (sas_device)
+ handle = sas_device->volume_handle;
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ } else
+ handle = sas_device_priv_data->sas_target->handle;
+
+ if (!handle) {
+ scmd->result = DID_RESET << 16;
+ r = FAILED;
+ goto out;
+ }
+
+ r = mpt3sas_scsih_issue_tm(ioc, handle, scmd->device->channel,
+ scmd->device->id, 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0,
+ 30, 0, TM_MUTEX_ON);
+
+ out:
+ starget_printk(KERN_INFO, starget, "target reset: %s scmd(%p)\n",
+ ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+ return r;
+}
+
+
+/**
+ * _scsih_host_reset - eh threads main host reset routine
+ * @scmd: pointer to scsi command object
+ *
+ * Returns SUCCESS if command aborted else FAILED
+ */
+static int
+_scsih_host_reset(struct scsi_cmnd *scmd)
+{
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
+ int r, retval;
+
+ pr_info(MPT3SAS_FMT "attempting host reset! scmd(%p)\n",
+ ioc->name, scmd);
+ scsi_print_command(scmd);
+
+ retval = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ r = (retval < 0) ? FAILED : SUCCESS;
+ pr_info(MPT3SAS_FMT "host reset: %s scmd(%p)\n",
+ ioc->name, ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+
+ return r;
+}
+
+/**
+ * _scsih_fw_event_add - insert and queue up fw_event
+ * @ioc: per adapter object
+ * @fw_event: object describing the event
+ * Context: This function will acquire ioc->fw_event_lock.
+ *
+ * This adds the firmware event object into link list, then queues it up to
+ * be processed from user context.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_fw_event_add(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event)
+{
+ unsigned long flags;
+
+ if (ioc->firmware_event_thread == NULL)
+ return;
+
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ INIT_LIST_HEAD(&fw_event->list);
+ list_add_tail(&fw_event->list, &ioc->fw_event_list);
+ INIT_WORK(&fw_event->work, _firmware_event_work);
+ queue_work(ioc->firmware_event_thread, &fw_event->work);
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+}
+
+/**
+ * _scsih_fw_event_free - delete fw_event
+ * @ioc: per adapter object
+ * @fw_event: object describing the event
+ * Context: This function will acquire ioc->fw_event_lock.
+ *
+ * This removes firmware event object from link list, frees associated memory.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_fw_event_free(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work
+ *fw_event)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ list_del(&fw_event->list);
+ kfree(fw_event->event_data);
+ kfree(fw_event);
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+}
+
+
+ /**
+ * mpt3sas_send_trigger_data_event - send event for processing trigger data
+ * @ioc: per adapter object
+ * @event_data: trigger event data
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_send_trigger_data_event(struct MPT3SAS_ADAPTER *ioc,
+ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data)
+{
+ struct fw_event_work *fw_event;
+
+ if (ioc->is_driver_loading)
+ return;
+ fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
+ if (!fw_event)
+ return;
+ fw_event->event_data = kzalloc(sizeof(*event_data), GFP_ATOMIC);
+ if (!fw_event->event_data)
+ return;
+ fw_event->event = MPT3SAS_PROCESS_TRIGGER_DIAG;
+ fw_event->ioc = ioc;
+ memcpy(fw_event->event_data, event_data, sizeof(*event_data));
+ _scsih_fw_event_add(ioc, fw_event);
+}
+
+/**
+ * _scsih_error_recovery_delete_devices - remove devices not responding
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+static void
+_scsih_error_recovery_delete_devices(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct fw_event_work *fw_event;
+
+ if (ioc->is_driver_loading)
+ return;
+ fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
+ if (!fw_event)
+ return;
+ fw_event->event = MPT3SAS_REMOVE_UNRESPONDING_DEVICES;
+ fw_event->ioc = ioc;
+ _scsih_fw_event_add(ioc, fw_event);
+}
+
+/**
+ * mpt3sas_port_enable_complete - port enable completed (fake event)
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_port_enable_complete(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct fw_event_work *fw_event;
+
+ fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
+ if (!fw_event)
+ return;
+ fw_event->event = MPT3SAS_PORT_ENABLE_COMPLETE;
+ fw_event->ioc = ioc;
+ _scsih_fw_event_add(ioc, fw_event);
+}
+
+/**
+ * _scsih_fw_event_cleanup_queue - cleanup event queue
+ * @ioc: per adapter object
+ *
+ * Walk the firmware event queue, either killing timers, or waiting
+ * for outstanding events to complete
+ *
+ * Return nothing.
+ */
+static void
+_scsih_fw_event_cleanup_queue(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct fw_event_work *fw_event, *next;
+
+ if (list_empty(&ioc->fw_event_list) ||
+ !ioc->firmware_event_thread || in_interrupt())
+ return;
+
+ list_for_each_entry_safe(fw_event, next, &ioc->fw_event_list, list) {
+ if (cancel_delayed_work(&fw_event->delayed_work)) {
+ _scsih_fw_event_free(ioc, fw_event);
+ continue;
+ }
+ fw_event->cancel_pending_work = 1;
+ }
+}
+
+/**
+ * _scsih_ublock_io_all_device - unblock every device
+ * @ioc: per adapter object
+ *
+ * change the device state from block to running
+ */
+static void
+_scsih_ublock_io_all_device(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct scsi_device *sdev;
+
+ shost_for_each_device(sdev, ioc->shost) {
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data)
+ continue;
+ if (!sas_device_priv_data->block)
+ continue;
+
+ sas_device_priv_data->block = 0;
+ dewtprintk(ioc, sdev_printk(KERN_INFO, sdev,
+ "device_running, handle(0x%04x)\n",
+ sas_device_priv_data->sas_target->handle));
+ scsi_internal_device_unblock(sdev, SDEV_RUNNING);
+ }
+}
+
+
+/**
+ * _scsih_ublock_io_device - prepare device to be deleted
+ * @ioc: per adapter object
+ * @sas_addr: sas address
+ *
+ * unblock then put device in offline state
+ */
+static void
+_scsih_ublock_io_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address)
+{
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct scsi_device *sdev;
+
+ shost_for_each_device(sdev, ioc->shost) {
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data)
+ continue;
+ if (sas_device_priv_data->sas_target->sas_address
+ != sas_address)
+ continue;
+ if (sas_device_priv_data->block) {
+ sas_device_priv_data->block = 0;
+ scsi_internal_device_unblock(sdev, SDEV_RUNNING);
+ }
+ }
+}
+
+/**
+ * _scsih_block_io_all_device - set the device state to SDEV_BLOCK
+ * @ioc: per adapter object
+ * @handle: device handle
+ *
+ * During device pull we need to appropiately set the sdev state.
+ */
+static void
+_scsih_block_io_all_device(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct scsi_device *sdev;
+
+ shost_for_each_device(sdev, ioc->shost) {
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data)
+ continue;
+ if (sas_device_priv_data->block)
+ continue;
+ sas_device_priv_data->block = 1;
+ scsi_internal_device_block(sdev);
+ sdev_printk(KERN_INFO, sdev, "device_blocked, handle(0x%04x)\n",
+ sas_device_priv_data->sas_target->handle);
+ }
+}
+
+/**
+ * _scsih_block_io_device - set the device state to SDEV_BLOCK
+ * @ioc: per adapter object
+ * @handle: device handle
+ *
+ * During device pull we need to appropiately set the sdev state.
+ */
+static void
+_scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct scsi_device *sdev;
+
+ shost_for_each_device(sdev, ioc->shost) {
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data)
+ continue;
+ if (sas_device_priv_data->sas_target->handle != handle)
+ continue;
+ if (sas_device_priv_data->block)
+ continue;
+ sas_device_priv_data->block = 1;
+ scsi_internal_device_block(sdev);
+ sdev_printk(KERN_INFO, sdev,
+ "device_blocked, handle(0x%04x)\n", handle);
+ }
+}
+
+/**
+ * _scsih_block_io_to_children_attached_to_ex
+ * @ioc: per adapter object
+ * @sas_expander: the sas_device object
+ *
+ * This routine set sdev state to SDEV_BLOCK for all devices
+ * attached to this expander. This function called when expander is
+ * pulled.
+ */
+static void
+_scsih_block_io_to_children_attached_to_ex(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_node *sas_expander)
+{
+ struct _sas_port *mpt3sas_port;
+ struct _sas_device *sas_device;
+ struct _sas_node *expander_sibling;
+ unsigned long flags;
+
+ if (!sas_expander)
+ return;
+
+ list_for_each_entry(mpt3sas_port,
+ &sas_expander->sas_port_list, port_list) {
+ if (mpt3sas_port->remote_identify.device_type ==
+ SAS_END_DEVICE) {
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device =
+ mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+ mpt3sas_port->remote_identify.sas_address);
+ if (sas_device)
+ set_bit(sas_device->handle,
+ ioc->blocking_handles);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ }
+ }
+
+ list_for_each_entry(mpt3sas_port,
+ &sas_expander->sas_port_list, port_list) {
+
+ if (mpt3sas_port->remote_identify.device_type ==
+ SAS_EDGE_EXPANDER_DEVICE ||
+ mpt3sas_port->remote_identify.device_type ==
+ SAS_FANOUT_EXPANDER_DEVICE) {
+ expander_sibling =
+ mpt3sas_scsih_expander_find_by_sas_address(
+ ioc, mpt3sas_port->remote_identify.sas_address);
+ _scsih_block_io_to_children_attached_to_ex(ioc,
+ expander_sibling);
+ }
+ }
+}
+
+/**
+ * _scsih_block_io_to_children_attached_directly
+ * @ioc: per adapter object
+ * @event_data: topology change event data
+ *
+ * This routine set sdev state to SDEV_BLOCK for all devices
+ * direct attached during device pull.
+ */
+static void
+_scsih_block_io_to_children_attached_directly(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataSasTopologyChangeList_t *event_data)
+{
+ int i;
+ u16 handle;
+ u16 reason_code;
+ u8 phy_number;
+
+ for (i = 0; i < event_data->NumEntries; i++) {
+ handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
+ if (!handle)
+ continue;
+ phy_number = event_data->StartPhyNum + i;
+ reason_code = event_data->PHY[i].PhyStatus &
+ MPI2_EVENT_SAS_TOPO_RC_MASK;
+ if (reason_code == MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING)
+ _scsih_block_io_device(ioc, handle);
+ }
+}
+
+/**
+ * _scsih_tm_tr_send - send task management request
+ * @ioc: per adapter object
+ * @handle: device handle
+ * Context: interrupt time.
+ *
+ * This code is to initiate the device removal handshake protocol
+ * with controller firmware. This function will issue target reset
+ * using high priority request queue. It will send a sas iounit
+ * control request (MPI2_SAS_OP_REMOVE_DEVICE) from this completion.
+ *
+ * This is designed to send muliple task management request at the same
+ * time to the fifo. If the fifo is full, we will append the request,
+ * and process it in a future completion.
+ */
+static void
+_scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ Mpi2SCSITaskManagementRequest_t *mpi_request;
+ u16 smid;
+ struct _sas_device *sas_device;
+ struct MPT3SAS_TARGET *sas_target_priv_data = NULL;
+ u64 sas_address = 0;
+ unsigned long flags;
+ struct _tr_list *delayed_tr;
+ u32 ioc_state;
+
+ if (ioc->remove_host) {
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: host has been removed: handle(0x%04x)\n",
+ __func__, ioc->name, handle));
+ return;
+ } else if (ioc->pci_error_recovery) {
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: host in pci error recovery: handle(0x%04x)\n",
+ __func__, ioc->name,
+ handle));
+ return;
+ }
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: host is not operational: handle(0x%04x)\n",
+ __func__, ioc->name,
+ handle));
+ return;
+ }
+
+ /* if PD, then return */
+ if (test_bit(handle, ioc->pd_handles))
+ return;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+ if (sas_device && sas_device->starget &&
+ sas_device->starget->hostdata) {
+ sas_target_priv_data = sas_device->starget->hostdata;
+ sas_target_priv_data->deleted = 1;
+ sas_address = sas_device->sas_address;
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ if (sas_target_priv_data) {
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "setting delete flag: handle(0x%04x), sas_addr(0x%016llx)\n",
+ ioc->name, handle,
+ (unsigned long long)sas_address));
+ _scsih_ublock_io_device(ioc, sas_address);
+ sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE;
+ }
+
+ smid = mpt3sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx);
+ if (!smid) {
+ delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC);
+ if (!delayed_tr)
+ return;
+ INIT_LIST_HEAD(&delayed_tr->list);
+ delayed_tr->handle = handle;
+ list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list);
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "DELAYED:tr:handle(0x%04x), (open)\n",
+ ioc->name, handle));
+ return;
+ }
+
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "tr_send:handle(0x%04x), (open), smid(%d), cb(%d)\n",
+ ioc->name, handle, smid,
+ ioc->tm_tr_cb_idx));
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
+ mpi_request->DevHandle = cpu_to_le16(handle);
+ mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
+ mpt3sas_base_put_smid_hi_priority(ioc, smid);
+ mpt3sas_trigger_master(ioc, MASTER_TRIGGER_DEVICE_REMOVAL);
+}
+
+/**
+ * _scsih_tm_tr_complete -
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: interrupt time.
+ *
+ * This is the target reset completion routine.
+ * This code is part of the code to initiate the device removal
+ * handshake protocol with controller firmware.
+ * It will send a sas iounit control request (MPI2_SAS_OP_REMOVE_DEVICE)
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+static u8
+_scsih_tm_tr_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply)
+{
+ u16 handle;
+ Mpi2SCSITaskManagementRequest_t *mpi_request_tm;
+ Mpi2SCSITaskManagementReply_t *mpi_reply =
+ mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ Mpi2SasIoUnitControlRequest_t *mpi_request;
+ u16 smid_sas_ctrl;
+ u32 ioc_state;
+
+ if (ioc->remove_host) {
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: host has been removed\n", __func__, ioc->name));
+ return 1;
+ } else if (ioc->pci_error_recovery) {
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: host in pci error recovery\n", __func__,
+ ioc->name));
+ return 1;
+ }
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: host is not operational\n", __func__, ioc->name));
+ return 1;
+ }
+ if (unlikely(!mpi_reply)) {
+ pr_err(MPT3SAS_FMT "mpi_reply not valid at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return 1;
+ }
+ mpi_request_tm = mpt3sas_base_get_msg_frame(ioc, smid);
+ handle = le16_to_cpu(mpi_request_tm->DevHandle);
+ if (handle != le16_to_cpu(mpi_reply->DevHandle)) {
+ dewtprintk(ioc, pr_err(MPT3SAS_FMT
+ "spurious interrupt: handle(0x%04x:0x%04x), smid(%d)!!!\n",
+ ioc->name, handle,
+ le16_to_cpu(mpi_reply->DevHandle), smid));
+ return 0;
+ }
+
+ mpt3sas_trigger_master(ioc, MASTER_TRIGGER_TASK_MANAGMENT);
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "tr_complete:handle(0x%04x), (open) smid(%d), ioc_status(0x%04x), "
+ "loginfo(0x%08x), completed(%d)\n", ioc->name,
+ handle, smid, le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo),
+ le32_to_cpu(mpi_reply->TerminationCount)));
+
+ smid_sas_ctrl = mpt3sas_base_get_smid(ioc, ioc->tm_sas_control_cb_idx);
+ if (!smid_sas_ctrl) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ return 1;
+ }
+
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "sc_send:handle(0x%04x), (open), smid(%d), cb(%d)\n",
+ ioc->name, handle, smid_sas_ctrl,
+ ioc->tm_sas_control_cb_idx));
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid_sas_ctrl);
+ memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
+ mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE;
+ mpi_request->DevHandle = mpi_request_tm->DevHandle;
+ mpt3sas_base_put_smid_default(ioc, smid_sas_ctrl);
+
+ return _scsih_check_for_pending_tm(ioc, smid);
+}
+
+
+/**
+ * _scsih_sas_control_complete - completion routine
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: interrupt time.
+ *
+ * This is the sas iounit control completion routine.
+ * This code is part of the code to initiate the device removal
+ * handshake protocol with controller firmware.
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+static u8
+_scsih_sas_control_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u8 msix_index, u32 reply)
+{
+ Mpi2SasIoUnitControlReply_t *mpi_reply =
+ mpt3sas_base_get_reply_virt_addr(ioc, reply);
+
+ if (likely(mpi_reply)) {
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "sc_complete:handle(0x%04x), (open) "
+ "smid(%d), ioc_status(0x%04x), loginfo(0x%08x)\n",
+ ioc->name, le16_to_cpu(mpi_reply->DevHandle), smid,
+ le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo)));
+ } else {
+ pr_err(MPT3SAS_FMT "mpi_reply not valid at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ }
+ return 1;
+}
+
+/**
+ * _scsih_tm_tr_volume_send - send target reset request for volumes
+ * @ioc: per adapter object
+ * @handle: device handle
+ * Context: interrupt time.
+ *
+ * This is designed to send muliple task management request at the same
+ * time to the fifo. If the fifo is full, we will append the request,
+ * and process it in a future completion.
+ */
+static void
+_scsih_tm_tr_volume_send(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ Mpi2SCSITaskManagementRequest_t *mpi_request;
+ u16 smid;
+ struct _tr_list *delayed_tr;
+
+ if (ioc->shost_recovery || ioc->remove_host ||
+ ioc->pci_error_recovery) {
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: host reset in progress!\n",
+ __func__, ioc->name));
+ return;
+ }
+
+ smid = mpt3sas_base_get_smid_hpr(ioc, ioc->tm_tr_volume_cb_idx);
+ if (!smid) {
+ delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC);
+ if (!delayed_tr)
+ return;
+ INIT_LIST_HEAD(&delayed_tr->list);
+ delayed_tr->handle = handle;
+ list_add_tail(&delayed_tr->list, &ioc->delayed_tr_volume_list);
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "DELAYED:tr:handle(0x%04x), (open)\n",
+ ioc->name, handle));
+ return;
+ }
+
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "tr_send:handle(0x%04x), (open), smid(%d), cb(%d)\n",
+ ioc->name, handle, smid,
+ ioc->tm_tr_volume_cb_idx));
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
+ mpi_request->DevHandle = cpu_to_le16(handle);
+ mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
+ mpt3sas_base_put_smid_hi_priority(ioc, smid);
+}
+
+/**
+ * _scsih_tm_volume_tr_complete - target reset completion
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: interrupt time.
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+static u8
+_scsih_tm_volume_tr_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid,
+ u8 msix_index, u32 reply)
+{
+ u16 handle;
+ Mpi2SCSITaskManagementRequest_t *mpi_request_tm;
+ Mpi2SCSITaskManagementReply_t *mpi_reply =
+ mpt3sas_base_get_reply_virt_addr(ioc, reply);
+
+ if (ioc->shost_recovery || ioc->remove_host ||
+ ioc->pci_error_recovery) {
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: host reset in progress!\n",
+ __func__, ioc->name));
+ return 1;
+ }
+ if (unlikely(!mpi_reply)) {
+ pr_err(MPT3SAS_FMT "mpi_reply not valid at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return 1;
+ }
+
+ mpi_request_tm = mpt3sas_base_get_msg_frame(ioc, smid);
+ handle = le16_to_cpu(mpi_request_tm->DevHandle);
+ if (handle != le16_to_cpu(mpi_reply->DevHandle)) {
+ dewtprintk(ioc, pr_err(MPT3SAS_FMT
+ "spurious interrupt: handle(0x%04x:0x%04x), smid(%d)!!!\n",
+ ioc->name, handle,
+ le16_to_cpu(mpi_reply->DevHandle), smid));
+ return 0;
+ }
+
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "tr_complete:handle(0x%04x), (open) smid(%d), ioc_status(0x%04x), "
+ "loginfo(0x%08x), completed(%d)\n", ioc->name,
+ handle, smid, le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo),
+ le32_to_cpu(mpi_reply->TerminationCount)));
+
+ return _scsih_check_for_pending_tm(ioc, smid);
+}
+
+
+/**
+ * _scsih_check_for_pending_tm - check for pending task management
+ * @ioc: per adapter object
+ * @smid: system request message index
+ *
+ * This will check delayed target reset list, and feed the
+ * next reqeust.
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+static u8
+_scsih_check_for_pending_tm(struct MPT3SAS_ADAPTER *ioc, u16 smid)
+{
+ struct _tr_list *delayed_tr;
+
+ if (!list_empty(&ioc->delayed_tr_volume_list)) {
+ delayed_tr = list_entry(ioc->delayed_tr_volume_list.next,
+ struct _tr_list, list);
+ mpt3sas_base_free_smid(ioc, smid);
+ _scsih_tm_tr_volume_send(ioc, delayed_tr->handle);
+ list_del(&delayed_tr->list);
+ kfree(delayed_tr);
+ return 0;
+ }
+
+ if (!list_empty(&ioc->delayed_tr_list)) {
+ delayed_tr = list_entry(ioc->delayed_tr_list.next,
+ struct _tr_list, list);
+ mpt3sas_base_free_smid(ioc, smid);
+ _scsih_tm_tr_send(ioc, delayed_tr->handle);
+ list_del(&delayed_tr->list);
+ kfree(delayed_tr);
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * _scsih_check_topo_delete_events - sanity check on topo events
+ * @ioc: per adapter object
+ * @event_data: the event data payload
+ *
+ * This routine added to better handle cable breaker.
+ *
+ * This handles the case where driver receives multiple expander
+ * add and delete events in a single shot. When there is a delete event
+ * the routine will void any pending add events waiting in the event queue.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_check_topo_delete_events(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataSasTopologyChangeList_t *event_data)
+{
+ struct fw_event_work *fw_event;
+ Mpi2EventDataSasTopologyChangeList_t *local_event_data;
+ u16 expander_handle;
+ struct _sas_node *sas_expander;
+ unsigned long flags;
+ int i, reason_code;
+ u16 handle;
+
+ for (i = 0 ; i < event_data->NumEntries; i++) {
+ handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
+ if (!handle)
+ continue;
+ reason_code = event_data->PHY[i].PhyStatus &
+ MPI2_EVENT_SAS_TOPO_RC_MASK;
+ if (reason_code == MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING)
+ _scsih_tm_tr_send(ioc, handle);
+ }
+
+ expander_handle = le16_to_cpu(event_data->ExpanderDevHandle);
+ if (expander_handle < ioc->sas_hba.num_phys) {
+ _scsih_block_io_to_children_attached_directly(ioc, event_data);
+ return;
+ }
+ if (event_data->ExpStatus ==
+ MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING) {
+ /* put expander attached devices into blocking state */
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_expander = mpt3sas_scsih_expander_find_by_handle(ioc,
+ expander_handle);
+ _scsih_block_io_to_children_attached_to_ex(ioc, sas_expander);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ do {
+ handle = find_first_bit(ioc->blocking_handles,
+ ioc->facts.MaxDevHandle);
+ if (handle < ioc->facts.MaxDevHandle)
+ _scsih_block_io_device(ioc, handle);
+ } while (test_and_clear_bit(handle, ioc->blocking_handles));
+ } else if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_RESPONDING)
+ _scsih_block_io_to_children_attached_directly(ioc, event_data);
+
+ if (event_data->ExpStatus != MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING)
+ return;
+
+ /* mark ignore flag for pending events */
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ list_for_each_entry(fw_event, &ioc->fw_event_list, list) {
+ if (fw_event->event != MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST ||
+ fw_event->ignore)
+ continue;
+ local_event_data = fw_event->event_data;
+ if (local_event_data->ExpStatus ==
+ MPI2_EVENT_SAS_TOPO_ES_ADDED ||
+ local_event_data->ExpStatus ==
+ MPI2_EVENT_SAS_TOPO_ES_RESPONDING) {
+ if (le16_to_cpu(local_event_data->ExpanderDevHandle) ==
+ expander_handle) {
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "setting ignoring flag\n", ioc->name));
+ fw_event->ignore = 1;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+}
+
+/**
+ * _scsih_set_volume_delete_flag - setting volume delete flag
+ * @ioc: per adapter object
+ * @handle: device handle
+ *
+ * This returns nothing.
+ */
+static void
+_scsih_set_volume_delete_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _raid_device *raid_device;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_handle(ioc, handle);
+ if (raid_device && raid_device->starget &&
+ raid_device->starget->hostdata) {
+ sas_target_priv_data =
+ raid_device->starget->hostdata;
+ sas_target_priv_data->deleted = 1;
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "setting delete flag: handle(0x%04x), "
+ "wwid(0x%016llx)\n", ioc->name, handle,
+ (unsigned long long) raid_device->wwid));
+ }
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+}
+
+/**
+ * _scsih_set_volume_handle_for_tr - set handle for target reset to volume
+ * @handle: input handle
+ * @a: handle for volume a
+ * @b: handle for volume b
+ *
+ * IR firmware only supports two raid volumes. The purpose of this
+ * routine is to set the volume handle in either a or b. When the given
+ * input handle is non-zero, or when a and b have not been set before.
+ */
+static void
+_scsih_set_volume_handle_for_tr(u16 handle, u16 *a, u16 *b)
+{
+ if (!handle || handle == *a || handle == *b)
+ return;
+ if (!*a)
+ *a = handle;
+ else if (!*b)
+ *b = handle;
+}
+
+/**
+ * _scsih_check_ir_config_unhide_events - check for UNHIDE events
+ * @ioc: per adapter object
+ * @event_data: the event data payload
+ * Context: interrupt time.
+ *
+ * This routine will send target reset to volume, followed by target
+ * resets to the PDs. This is called when a PD has been removed, or
+ * volume has been deleted or removed. When the target reset is sent
+ * to volume, the PD target resets need to be queued to start upon
+ * completion of the volume target reset.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_check_ir_config_unhide_events(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataIrConfigChangeList_t *event_data)
+{
+ Mpi2EventIrConfigElement_t *element;
+ int i;
+ u16 handle, volume_handle, a, b;
+ struct _tr_list *delayed_tr;
+
+ a = 0;
+ b = 0;
+
+ /* Volume Resets for Deleted or Removed */
+ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
+ for (i = 0; i < event_data->NumElements; i++, element++) {
+ if (le32_to_cpu(event_data->Flags) &
+ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG)
+ continue;
+ if (element->ReasonCode ==
+ MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED ||
+ element->ReasonCode ==
+ MPI2_EVENT_IR_CHANGE_RC_REMOVED) {
+ volume_handle = le16_to_cpu(element->VolDevHandle);
+ _scsih_set_volume_delete_flag(ioc, volume_handle);
+ _scsih_set_volume_handle_for_tr(volume_handle, &a, &b);
+ }
+ }
+
+ /* Volume Resets for UNHIDE events */
+ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
+ for (i = 0; i < event_data->NumElements; i++, element++) {
+ if (le32_to_cpu(event_data->Flags) &
+ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG)
+ continue;
+ if (element->ReasonCode == MPI2_EVENT_IR_CHANGE_RC_UNHIDE) {
+ volume_handle = le16_to_cpu(element->VolDevHandle);
+ _scsih_set_volume_handle_for_tr(volume_handle, &a, &b);
+ }
+ }
+
+ if (a)
+ _scsih_tm_tr_volume_send(ioc, a);
+ if (b)
+ _scsih_tm_tr_volume_send(ioc, b);
+
+ /* PD target resets */
+ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
+ for (i = 0; i < event_data->NumElements; i++, element++) {
+ if (element->ReasonCode != MPI2_EVENT_IR_CHANGE_RC_UNHIDE)
+ continue;
+ handle = le16_to_cpu(element->PhysDiskDevHandle);
+ volume_handle = le16_to_cpu(element->VolDevHandle);
+ clear_bit(handle, ioc->pd_handles);
+ if (!volume_handle)
+ _scsih_tm_tr_send(ioc, handle);
+ else if (volume_handle == a || volume_handle == b) {
+ delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC);
+ BUG_ON(!delayed_tr);
+ INIT_LIST_HEAD(&delayed_tr->list);
+ delayed_tr->handle = handle;
+ list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list);
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "DELAYED:tr:handle(0x%04x), (open)\n", ioc->name,
+ handle));
+ } else
+ _scsih_tm_tr_send(ioc, handle);
+ }
+}
+
+
+/**
+ * _scsih_check_volume_delete_events - set delete flag for volumes
+ * @ioc: per adapter object
+ * @event_data: the event data payload
+ * Context: interrupt time.
+ *
+ * This will handle the case when the cable connected to entire volume is
+ * pulled. We will take care of setting the deleted flag so normal IO will
+ * not be sent.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_check_volume_delete_events(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataIrVolume_t *event_data)
+{
+ u32 state;
+
+ if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED)
+ return;
+ state = le32_to_cpu(event_data->NewValue);
+ if (state == MPI2_RAID_VOL_STATE_MISSING || state ==
+ MPI2_RAID_VOL_STATE_FAILED)
+ _scsih_set_volume_delete_flag(ioc,
+ le16_to_cpu(event_data->VolDevHandle));
+}
+
+/**
+ * _scsih_flush_running_cmds - completing outstanding commands.
+ * @ioc: per adapter object
+ *
+ * The flushing out of all pending scmd commands following host reset,
+ * where all IO is dropped to the floor.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_flush_running_cmds(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct scsi_cmnd *scmd;
+ u16 smid;
+ u16 count = 0;
+
+ for (smid = 1; smid <= ioc->scsiio_depth; smid++) {
+ scmd = _scsih_scsi_lookup_get_clear(ioc, smid);
+ if (!scmd)
+ continue;
+ count++;
+ mpt3sas_base_free_smid(ioc, smid);
+ scsi_dma_unmap(scmd);
+ if (ioc->pci_error_recovery)
+ scmd->result = DID_NO_CONNECT << 16;
+ else
+ scmd->result = DID_RESET << 16;
+ scmd->scsi_done(scmd);
+ }
+ dtmprintk(ioc, pr_info(MPT3SAS_FMT "completing %d cmds\n",
+ ioc->name, count));
+}
+
+/**
+ * _scsih_setup_eedp - setup MPI request for EEDP transfer
+ * @ioc: per adapter object
+ * @scmd: pointer to scsi command object
+ * @mpi_request: pointer to the SCSI_IO reqest message frame
+ *
+ * Supporting protection 1 and 3.
+ *
+ * Returns nothing
+ */
+static void
+_scsih_setup_eedp(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
+ Mpi2SCSIIORequest_t *mpi_request)
+{
+ u16 eedp_flags;
+ unsigned char prot_op = scsi_get_prot_op(scmd);
+ unsigned char prot_type = scsi_get_prot_type(scmd);
+ Mpi25SCSIIORequest_t *mpi_request_3v =
+ (Mpi25SCSIIORequest_t *)mpi_request;
+
+ if (prot_type == SCSI_PROT_DIF_TYPE0 || prot_op == SCSI_PROT_NORMAL)
+ return;
+
+ if (prot_op == SCSI_PROT_READ_STRIP)
+ eedp_flags = MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP;
+ else if (prot_op == SCSI_PROT_WRITE_INSERT)
+ eedp_flags = MPI2_SCSIIO_EEDPFLAGS_INSERT_OP;
+ else
+ return;
+
+ switch (prot_type) {
+ case SCSI_PROT_DIF_TYPE1:
+ case SCSI_PROT_DIF_TYPE2:
+
+ /*
+ * enable ref/guard checking
+ * auto increment ref tag
+ */
+ eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG |
+ MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG |
+ MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
+ mpi_request->CDB.EEDP32.PrimaryReferenceTag =
+ cpu_to_be32(scsi_get_lba(scmd));
+ break;
+
+ case SCSI_PROT_DIF_TYPE3:
+
+ /*
+ * enable guard checking
+ */
+ eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
+
+ break;
+ }
+
+ mpi_request_3v->EEDPBlockSize =
+ cpu_to_le16(scmd->device->sector_size);
+ mpi_request->EEDPFlags = cpu_to_le16(eedp_flags);
+}
+
+/**
+ * _scsih_eedp_error_handling - return sense code for EEDP errors
+ * @scmd: pointer to scsi command object
+ * @ioc_status: ioc status
+ *
+ * Returns nothing
+ */
+static void
+_scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status)
+{
+ u8 ascq;
+
+ switch (ioc_status) {
+ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
+ ascq = 0x01;
+ break;
+ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
+ ascq = 0x02;
+ break;
+ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
+ ascq = 0x03;
+ break;
+ default:
+ ascq = 0x00;
+ break;
+ }
+ scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST, 0x10,
+ ascq);
+ scmd->result = DRIVER_SENSE << 24 | (DID_ABORT << 16) |
+ SAM_STAT_CHECK_CONDITION;
+}
+
+
+/**
+ * _scsih_qcmd_lck - main scsi request entry point
+ * @scmd: pointer to scsi command object
+ * @done: function pointer to be invoked on completion
+ *
+ * The callback index is set inside `ioc->scsi_io_cb_idx`.
+ *
+ * Returns 0 on success. If there's a failure, return either:
+ * SCSI_MLQUEUE_DEVICE_BUSY if the device queue is full, or
+ * SCSI_MLQUEUE_HOST_BUSY if the entire host queue is full
+ */
+static int
+_scsih_qcmd_lck(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
+{
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ Mpi2SCSIIORequest_t *mpi_request;
+ u32 mpi_control;
+ u16 smid;
+ u16 handle;
+
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+ if (ioc->logging_level & MPT_DEBUG_SCSI)
+ scsi_print_command(scmd);
+#endif
+
+ scmd->scsi_done = done;
+ sas_device_priv_data = scmd->device->hostdata;
+ if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scmd->scsi_done(scmd);
+ return 0;
+ }
+
+ if (ioc->pci_error_recovery || ioc->remove_host) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scmd->scsi_done(scmd);
+ return 0;
+ }
+
+ sas_target_priv_data = sas_device_priv_data->sas_target;
+
+ /* invalid device handle */
+ handle = sas_target_priv_data->handle;
+ if (handle == MPT3SAS_INVALID_DEVICE_HANDLE) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scmd->scsi_done(scmd);
+ return 0;
+ }
+
+
+ /* host recovery or link resets sent via IOCTLs */
+ if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress)
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ /* device has been deleted */
+ else if (sas_target_priv_data->deleted) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scmd->scsi_done(scmd);
+ return 0;
+ /* device busy with task managment */
+ } else if (sas_target_priv_data->tm_busy ||
+ sas_device_priv_data->block)
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+
+ if (scmd->sc_data_direction == DMA_FROM_DEVICE)
+ mpi_control = MPI2_SCSIIO_CONTROL_READ;
+ else if (scmd->sc_data_direction == DMA_TO_DEVICE)
+ mpi_control = MPI2_SCSIIO_CONTROL_WRITE;
+ else
+ mpi_control = MPI2_SCSIIO_CONTROL_NODATATRANSFER;
+
+ /* set tags */
+ if (!(sas_device_priv_data->flags & MPT_DEVICE_FLAGS_INIT)) {
+ if (scmd->device->tagged_supported) {
+ if (scmd->device->ordered_tags)
+ mpi_control |= MPI2_SCSIIO_CONTROL_ORDEREDQ;
+ else
+ mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
+ } else
+ mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
+ } else
+ mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
+
+ if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON) &&
+ scmd->cmd_len != 32)
+ mpi_control |= MPI2_SCSIIO_CONTROL_TLR_ON;
+
+ smid = mpt3sas_base_get_smid_scsiio(ioc, ioc->scsi_io_cb_idx, scmd);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ goto out;
+ }
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ memset(mpi_request, 0, sizeof(Mpi2SCSIIORequest_t));
+ _scsih_setup_eedp(ioc, scmd, mpi_request);
+
+ if (scmd->cmd_len == 32)
+ mpi_control |= 4 << MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT;
+ mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
+ if (sas_device_priv_data->sas_target->flags &
+ MPT_TARGET_FLAGS_RAID_COMPONENT)
+ mpi_request->Function = MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH;
+ else
+ mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
+ mpi_request->DevHandle = cpu_to_le16(handle);
+ mpi_request->DataLength = cpu_to_le32(scsi_bufflen(scmd));
+ mpi_request->Control = cpu_to_le32(mpi_control);
+ mpi_request->IoFlags = cpu_to_le16(scmd->cmd_len);
+ mpi_request->MsgFlags = MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR;
+ mpi_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE;
+ mpi_request->SenseBufferLowAddress =
+ mpt3sas_base_get_sense_buffer_dma(ioc, smid);
+ mpi_request->SGLOffset0 = offsetof(Mpi2SCSIIORequest_t, SGL) / 4;
+ int_to_scsilun(sas_device_priv_data->lun, (struct scsi_lun *)
+ mpi_request->LUN);
+ memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len);
+
+ if (mpi_request->DataLength) {
+ if (ioc->build_sg_scmd(ioc, scmd, smid)) {
+ mpt3sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+ } else
+ ioc->build_zero_len_sge(ioc, &mpi_request->SGL);
+
+ if (likely(mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST)) {
+ if (sas_target_priv_data->flags & MPT_TARGET_FASTPATH_IO) {
+ mpi_request->IoFlags = cpu_to_le16(scmd->cmd_len |
+ MPI25_SCSIIO_IOFLAGS_FAST_PATH);
+ mpt3sas_base_put_smid_fast_path(ioc, smid, handle);
+ } else
+ mpt3sas_base_put_smid_scsi_io(ioc, smid, handle);
+ } else
+ mpt3sas_base_put_smid_default(ioc, smid);
+ return 0;
+
+ out:
+ return SCSI_MLQUEUE_HOST_BUSY;
+}
+static DEF_SCSI_QCMD(_scsih_qcmd)
+
+
+/**
+ * _scsih_normalize_sense - normalize descriptor and fixed format sense data
+ * @sense_buffer: sense data returned by target
+ * @data: normalized skey/asc/ascq
+ *
+ * Return nothing.
+ */
+static void
+_scsih_normalize_sense(char *sense_buffer, struct sense_info *data)
+{
+ if ((sense_buffer[0] & 0x7F) >= 0x72) {
+ /* descriptor format */
+ data->skey = sense_buffer[1] & 0x0F;
+ data->asc = sense_buffer[2];
+ data->ascq = sense_buffer[3];
+ } else {
+ /* fixed format */
+ data->skey = sense_buffer[2] & 0x0F;
+ data->asc = sense_buffer[12];
+ data->ascq = sense_buffer[13];
+ }
+}
+
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+/**
+ * _scsih_scsi_ioc_info - translated non-succesfull SCSI_IO request
+ * @ioc: per adapter object
+ * @scmd: pointer to scsi command object
+ * @mpi_reply: reply mf payload returned from firmware
+ *
+ * scsi_status - SCSI Status code returned from target device
+ * scsi_state - state info associated with SCSI_IO determined by ioc
+ * ioc_status - ioc supplied status info
+ *
+ * Return nothing.
+ */
+static void
+_scsih_scsi_ioc_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
+ Mpi2SCSIIOReply_t *mpi_reply, u16 smid)
+{
+ u32 response_info;
+ u8 *response_bytes;
+ u16 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ u8 scsi_state = mpi_reply->SCSIState;
+ u8 scsi_status = mpi_reply->SCSIStatus;
+ char *desc_ioc_state = NULL;
+ char *desc_scsi_status = NULL;
+ char *desc_scsi_state = ioc->tmp_string;
+ u32 log_info = le32_to_cpu(mpi_reply->IOCLogInfo);
+ struct _sas_device *sas_device = NULL;
+ unsigned long flags;
+ struct scsi_target *starget = scmd->device->sdev_target;
+ struct MPT3SAS_TARGET *priv_target = starget->hostdata;
+ char *device_str = NULL;
+
+ if (!priv_target)
+ return;
+ device_str = "volume";
+
+ if (log_info == 0x31170000)
+ return;
+
+ switch (ioc_status) {
+ case MPI2_IOCSTATUS_SUCCESS:
+ desc_ioc_state = "success";
+ break;
+ case MPI2_IOCSTATUS_INVALID_FUNCTION:
+ desc_ioc_state = "invalid function";
+ break;
+ case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
+ desc_ioc_state = "scsi recovered error";
+ break;
+ case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE:
+ desc_ioc_state = "scsi invalid dev handle";
+ break;
+ case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
+ desc_ioc_state = "scsi device not there";
+ break;
+ case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
+ desc_ioc_state = "scsi data overrun";
+ break;
+ case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN:
+ desc_ioc_state = "scsi data underrun";
+ break;
+ case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR:
+ desc_ioc_state = "scsi io data error";
+ break;
+ case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR:
+ desc_ioc_state = "scsi protocol error";
+ break;
+ case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
+ desc_ioc_state = "scsi task terminated";
+ break;
+ case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
+ desc_ioc_state = "scsi residual mismatch";
+ break;
+ case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
+ desc_ioc_state = "scsi task mgmt failed";
+ break;
+ case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
+ desc_ioc_state = "scsi ioc terminated";
+ break;
+ case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
+ desc_ioc_state = "scsi ext terminated";
+ break;
+ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
+ desc_ioc_state = "eedp guard error";
+ break;
+ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
+ desc_ioc_state = "eedp ref tag error";
+ break;
+ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
+ desc_ioc_state = "eedp app tag error";
+ break;
+ default:
+ desc_ioc_state = "unknown";
+ break;
+ }
+
+ switch (scsi_status) {
+ case MPI2_SCSI_STATUS_GOOD:
+ desc_scsi_status = "good";
+ break;
+ case MPI2_SCSI_STATUS_CHECK_CONDITION:
+ desc_scsi_status = "check condition";
+ break;
+ case MPI2_SCSI_STATUS_CONDITION_MET:
+ desc_scsi_status = "condition met";
+ break;
+ case MPI2_SCSI_STATUS_BUSY:
+ desc_scsi_status = "busy";
+ break;
+ case MPI2_SCSI_STATUS_INTERMEDIATE:
+ desc_scsi_status = "intermediate";
+ break;
+ case MPI2_SCSI_STATUS_INTERMEDIATE_CONDMET:
+ desc_scsi_status = "intermediate condmet";
+ break;
+ case MPI2_SCSI_STATUS_RESERVATION_CONFLICT:
+ desc_scsi_status = "reservation conflict";
+ break;
+ case MPI2_SCSI_STATUS_COMMAND_TERMINATED:
+ desc_scsi_status = "command terminated";
+ break;
+ case MPI2_SCSI_STATUS_TASK_SET_FULL:
+ desc_scsi_status = "task set full";
+ break;
+ case MPI2_SCSI_STATUS_ACA_ACTIVE:
+ desc_scsi_status = "aca active";
+ break;
+ case MPI2_SCSI_STATUS_TASK_ABORTED:
+ desc_scsi_status = "task aborted";
+ break;
+ default:
+ desc_scsi_status = "unknown";
+ break;
+ }
+
+ desc_scsi_state[0] = '\0';
+ if (!scsi_state)
+ desc_scsi_state = " ";
+ if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID)
+ strcat(desc_scsi_state, "response info ");
+ if (scsi_state & MPI2_SCSI_STATE_TERMINATED)
+ strcat(desc_scsi_state, "state terminated ");
+ if (scsi_state & MPI2_SCSI_STATE_NO_SCSI_STATUS)
+ strcat(desc_scsi_state, "no status ");
+ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_FAILED)
+ strcat(desc_scsi_state, "autosense failed ");
+ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID)
+ strcat(desc_scsi_state, "autosense valid ");
+
+ scsi_print_command(scmd);
+
+ if (priv_target->flags & MPT_TARGET_FLAGS_VOLUME) {
+ pr_warn(MPT3SAS_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 = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+ priv_target->sas_address);
+ if (sas_device) {
+ pr_warn(MPT3SAS_FMT
+ "\tsas_address(0x%016llx), phy(%d)\n",
+ ioc->name, (unsigned long long)
+ sas_device->sas_address, sas_device->phy);
+ pr_warn(MPT3SAS_FMT
+ "\tenclosure_logical_id(0x%016llx), slot(%d)\n",
+ ioc->name, (unsigned long long)
+ sas_device->enclosure_logical_id, sas_device->slot);
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ }
+
+ pr_warn(MPT3SAS_FMT
+ "\thandle(0x%04x), ioc_status(%s)(0x%04x), smid(%d)\n",
+ ioc->name, le16_to_cpu(mpi_reply->DevHandle),
+ desc_ioc_state, ioc_status, smid);
+ pr_warn(MPT3SAS_FMT
+ "\trequest_len(%d), underflow(%d), resid(%d)\n",
+ ioc->name, scsi_bufflen(scmd), scmd->underflow,
+ scsi_get_resid(scmd));
+ pr_warn(MPT3SAS_FMT
+ "\ttag(%d), transfer_count(%d), sc->result(0x%08x)\n",
+ ioc->name, le16_to_cpu(mpi_reply->TaskTag),
+ le32_to_cpu(mpi_reply->TransferCount), scmd->result);
+ pr_warn(MPT3SAS_FMT
+ "\tscsi_status(%s)(0x%02x), scsi_state(%s)(0x%02x)\n",
+ ioc->name, desc_scsi_status,
+ scsi_status, desc_scsi_state, scsi_state);
+
+ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) {
+ struct sense_info data;
+ _scsih_normalize_sense(scmd->sense_buffer, &data);
+ pr_warn(MPT3SAS_FMT
+ "\t[sense_key,asc,ascq]: [0x%02x,0x%02x,0x%02x], count(%d)\n",
+ ioc->name, data.skey,
+ data.asc, data.ascq, le32_to_cpu(mpi_reply->SenseCount));
+ }
+
+ if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) {
+ response_info = le32_to_cpu(mpi_reply->ResponseInfo);
+ response_bytes = (u8 *)&response_info;
+ _scsih_response_code(ioc, response_bytes[0]);
+ }
+}
+#endif
+
+/**
+ * _scsih_turn_on_fault_led - illuminate Fault LED
+ * @ioc: per adapter object
+ * @handle: device handle
+ * Context: process
+ *
+ * Return nothing.
+ */
+static void
+_scsih_turn_on_fault_led(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ Mpi2SepReply_t mpi_reply;
+ Mpi2SepRequest_t mpi_request;
+
+ memset(&mpi_request, 0, sizeof(Mpi2SepRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR;
+ mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS;
+ mpi_request.SlotStatus =
+ cpu_to_le32(MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT);
+ mpi_request.DevHandle = cpu_to_le16(handle);
+ mpi_request.Flags = MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS;
+ if ((mpt3sas_base_scsi_enclosure_processor(ioc, &mpi_reply,
+ &mpi_request)) != 0) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name,
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) {
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "enclosure_processor: ioc_status (0x%04x), loginfo(0x%08x)\n",
+ ioc->name, le16_to_cpu(mpi_reply.IOCStatus),
+ le32_to_cpu(mpi_reply.IOCLogInfo)));
+ return;
+ }
+}
+
+/**
+ * _scsih_send_event_to_turn_on_fault_led - fire delayed event
+ * @ioc: per adapter object
+ * @handle: device handle
+ * Context: interrupt.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_send_event_to_turn_on_fault_led(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct fw_event_work *fw_event;
+
+ fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
+ if (!fw_event)
+ return;
+ fw_event->event = MPT3SAS_TURN_ON_FAULT_LED;
+ fw_event->device_handle = handle;
+ fw_event->ioc = ioc;
+ _scsih_fw_event_add(ioc, fw_event);
+}
+
+/**
+ * _scsih_smart_predicted_fault - process smart errors
+ * @ioc: per adapter object
+ * @handle: device handle
+ * Context: interrupt.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_smart_predicted_fault(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct scsi_target *starget;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ Mpi2EventNotificationReply_t *event_reply;
+ Mpi2EventDataSasDeviceStatusChange_t *event_data;
+ struct _sas_device *sas_device;
+ ssize_t sz;
+ unsigned long flags;
+
+ /* only handle non-raid devices */
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+ if (!sas_device) {
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ return;
+ }
+ starget = sas_device->starget;
+ sas_target_priv_data = starget->hostdata;
+
+ if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) ||
+ ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))) {
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ return;
+ }
+ starget_printk(KERN_WARNING, starget, "predicted fault\n");
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM)
+ _scsih_send_event_to_turn_on_fault_led(ioc, handle);
+
+ /* insert into event log */
+ sz = offsetof(Mpi2EventNotificationReply_t, EventData) +
+ sizeof(Mpi2EventDataSasDeviceStatusChange_t);
+ event_reply = kzalloc(sz, GFP_KERNEL);
+ if (!event_reply) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ event_reply->Function = MPI2_FUNCTION_EVENT_NOTIFICATION;
+ event_reply->Event =
+ cpu_to_le16(MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE);
+ event_reply->MsgLength = sz/4;
+ event_reply->EventDataLength =
+ cpu_to_le16(sizeof(Mpi2EventDataSasDeviceStatusChange_t)/4);
+ event_data = (Mpi2EventDataSasDeviceStatusChange_t *)
+ event_reply->EventData;
+ event_data->ReasonCode = MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA;
+ event_data->ASC = 0x5D;
+ event_data->DevHandle = cpu_to_le16(handle);
+ event_data->SASAddress = cpu_to_le64(sas_target_priv_data->sas_address);
+ mpt3sas_ctl_add_to_event_log(ioc, event_reply);
+ kfree(event_reply);
+}
+
+/**
+ * _scsih_io_done - scsi request callback
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Callback handler when using _scsih_qcmd.
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+static u8
+_scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
+{
+ Mpi2SCSIIORequest_t *mpi_request;
+ Mpi2SCSIIOReply_t *mpi_reply;
+ struct scsi_cmnd *scmd;
+ u16 ioc_status;
+ u32 xfer_cnt;
+ u8 scsi_state;
+ u8 scsi_status;
+ u32 log_info;
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ u32 response_code = 0;
+
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ scmd = _scsih_scsi_lookup_get_clear(ioc, smid);
+ if (scmd == NULL)
+ return 1;
+
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+
+ if (mpi_reply == NULL) {
+ scmd->result = DID_OK << 16;
+ goto out;
+ }
+
+ sas_device_priv_data = scmd->device->hostdata;
+ if (!sas_device_priv_data || !sas_device_priv_data->sas_target ||
+ sas_device_priv_data->sas_target->deleted) {
+ scmd->result = DID_NO_CONNECT << 16;
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus);
+
+ /* turning off TLR */
+ scsi_state = mpi_reply->SCSIState;
+ if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID)
+ response_code =
+ le32_to_cpu(mpi_reply->ResponseInfo) & 0xFF;
+ if (!sas_device_priv_data->tlr_snoop_check) {
+ sas_device_priv_data->tlr_snoop_check++;
+ if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON) &&
+ response_code == MPI2_SCSITASKMGMT_RSP_INVALID_FRAME)
+ sas_device_priv_data->flags &=
+ ~MPT_DEVICE_TLR_ON;
+ }
+
+ xfer_cnt = le32_to_cpu(mpi_reply->TransferCount);
+ scsi_set_resid(scmd, scsi_bufflen(scmd) - xfer_cnt);
+ if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE)
+ log_info = le32_to_cpu(mpi_reply->IOCLogInfo);
+ else
+ log_info = 0;
+ ioc_status &= MPI2_IOCSTATUS_MASK;
+ scsi_status = mpi_reply->SCSIStatus;
+
+ if (ioc_status == MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN && xfer_cnt == 0 &&
+ (scsi_status == MPI2_SCSI_STATUS_BUSY ||
+ scsi_status == MPI2_SCSI_STATUS_RESERVATION_CONFLICT ||
+ scsi_status == MPI2_SCSI_STATUS_TASK_SET_FULL)) {
+ ioc_status = MPI2_IOCSTATUS_SUCCESS;
+ }
+
+ if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) {
+ struct sense_info data;
+ const void *sense_data = mpt3sas_base_get_sense_buffer(ioc,
+ smid);
+ u32 sz = min_t(u32, SCSI_SENSE_BUFFERSIZE,
+ le32_to_cpu(mpi_reply->SenseCount));
+ memcpy(scmd->sense_buffer, sense_data, sz);
+ _scsih_normalize_sense(scmd->sense_buffer, &data);
+ /* failure prediction threshold exceeded */
+ if (data.asc == 0x5D)
+ _scsih_smart_predicted_fault(ioc,
+ le16_to_cpu(mpi_reply->DevHandle));
+ mpt3sas_trigger_scsi(ioc, data.skey, data.asc, data.ascq);
+ }
+
+ switch (ioc_status) {
+ case MPI2_IOCSTATUS_BUSY:
+ case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES:
+ scmd->result = SAM_STAT_BUSY;
+ break;
+
+ case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
+ scmd->result = DID_NO_CONNECT << 16;
+ break;
+
+ case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
+ if (sas_device_priv_data->block) {
+ scmd->result = DID_TRANSPORT_DISRUPTED << 16;
+ goto out;
+ }
+ if (log_info == 0x31110630) {
+ if (scmd->retries > 2) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scsi_device_set_state(scmd->device,
+ SDEV_OFFLINE);
+ } else {
+ scmd->result = DID_SOFT_ERROR << 16;
+ scmd->device->expecting_cc_ua = 1;
+ }
+ break;
+ }
+ scmd->result = DID_SOFT_ERROR << 16;
+ break;
+ case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
+ case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
+ scmd->result = DID_RESET << 16;
+ break;
+
+ case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
+ if ((xfer_cnt == 0) || (scmd->underflow > xfer_cnt))
+ scmd->result = DID_SOFT_ERROR << 16;
+ else
+ scmd->result = (DID_OK << 16) | scsi_status;
+ break;
+
+ case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN:
+ scmd->result = (DID_OK << 16) | scsi_status;
+
+ if ((scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID))
+ break;
+
+ if (xfer_cnt < scmd->underflow) {
+ if (scsi_status == SAM_STAT_BUSY)
+ scmd->result = SAM_STAT_BUSY;
+ else
+ scmd->result = DID_SOFT_ERROR << 16;
+ } else if (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED |
+ MPI2_SCSI_STATE_NO_SCSI_STATUS))
+ scmd->result = DID_SOFT_ERROR << 16;
+ else if (scsi_state & MPI2_SCSI_STATE_TERMINATED)
+ scmd->result = DID_RESET << 16;
+ else if (!xfer_cnt && scmd->cmnd[0] == REPORT_LUNS) {
+ mpi_reply->SCSIState = MPI2_SCSI_STATE_AUTOSENSE_VALID;
+ mpi_reply->SCSIStatus = SAM_STAT_CHECK_CONDITION;
+ scmd->result = (DRIVER_SENSE << 24) |
+ SAM_STAT_CHECK_CONDITION;
+ scmd->sense_buffer[0] = 0x70;
+ scmd->sense_buffer[2] = ILLEGAL_REQUEST;
+ scmd->sense_buffer[12] = 0x20;
+ scmd->sense_buffer[13] = 0;
+ }
+ break;
+
+ case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
+ scsi_set_resid(scmd, 0);
+ case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
+ case MPI2_IOCSTATUS_SUCCESS:
+ scmd->result = (DID_OK << 16) | scsi_status;
+ if (response_code ==
+ MPI2_SCSITASKMGMT_RSP_INVALID_FRAME ||
+ (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED |
+ MPI2_SCSI_STATE_NO_SCSI_STATUS)))
+ scmd->result = DID_SOFT_ERROR << 16;
+ else if (scsi_state & MPI2_SCSI_STATE_TERMINATED)
+ scmd->result = DID_RESET << 16;
+ break;
+
+ case MPI2_IOCSTATUS_EEDP_GUARD_ERROR:
+ case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR:
+ case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR:
+ _scsih_eedp_error_handling(scmd, ioc_status);
+ break;
+
+ case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR:
+ case MPI2_IOCSTATUS_INVALID_FUNCTION:
+ case MPI2_IOCSTATUS_INVALID_SGL:
+ case MPI2_IOCSTATUS_INTERNAL_ERROR:
+ case MPI2_IOCSTATUS_INVALID_FIELD:
+ case MPI2_IOCSTATUS_INVALID_STATE:
+ case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR:
+ case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
+ default:
+ scmd->result = DID_SOFT_ERROR << 16;
+ break;
+
+ }
+
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+ if (scmd->result && (ioc->logging_level & MPT_DEBUG_REPLY))
+ _scsih_scsi_ioc_info(ioc , scmd, mpi_reply, smid);
+#endif
+
+ out:
+
+ scsi_dma_unmap(scmd);
+
+ scmd->scsi_done(scmd);
+ return 1;
+}
+
+/**
+ * _scsih_sas_host_refresh - refreshing sas host object contents
+ * @ioc: per adapter object
+ * Context: user
+ *
+ * During port enable, fw will send topology events for every device. Its
+ * possible that the handles may change from the previous setting, so this
+ * code keeping handles updating if changed.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_host_refresh(struct MPT3SAS_ADAPTER *ioc)
+{
+ u16 sz;
+ u16 ioc_status;
+ int i;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
+ u16 attached_handle;
+ u8 link_rate;
+
+ dtmprintk(ioc, pr_info(MPT3SAS_FMT
+ "updating handles for sas_host(0x%016llx)\n",
+ ioc->name, (unsigned long long)ioc->sas_hba.sas_address));
+
+ sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys
+ * sizeof(Mpi2SasIOUnit0PhyData_t));
+ sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg0) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ if ((mpt3sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
+ sas_iounit_pg0, sz)) != 0)
+ goto out;
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ goto out;
+ for (i = 0; i < ioc->sas_hba.num_phys ; i++) {
+ link_rate = sas_iounit_pg0->PhyData[i].NegotiatedLinkRate >> 4;
+ if (i == 0)
+ ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0->
+ PhyData[0].ControllerDevHandle);
+ ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle;
+ attached_handle = le16_to_cpu(sas_iounit_pg0->PhyData[i].
+ AttachedDevHandle);
+ if (attached_handle && link_rate < MPI2_SAS_NEG_LINK_RATE_1_5)
+ link_rate = MPI2_SAS_NEG_LINK_RATE_1_5;
+ mpt3sas_transport_update_links(ioc, ioc->sas_hba.sas_address,
+ attached_handle, i, link_rate);
+ }
+ out:
+ kfree(sas_iounit_pg0);
+}
+
+/**
+ * _scsih_sas_host_add - create sas host object
+ * @ioc: per adapter object
+ *
+ * Creating host side data object, stored in ioc->sas_hba
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_host_add(struct MPT3SAS_ADAPTER *ioc)
+{
+ int i;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
+ Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
+ Mpi2SasPhyPage0_t phy_pg0;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ Mpi2SasEnclosurePage0_t enclosure_pg0;
+ u16 ioc_status;
+ u16 sz;
+ u8 device_missing_delay;
+
+ mpt3sas_config_get_number_hba_phys(ioc, &ioc->sas_hba.num_phys);
+ if (!ioc->sas_hba.num_phys) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ /* sas_iounit page 0 */
+ sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys *
+ sizeof(Mpi2SasIOUnit0PhyData_t));
+ sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg0) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+ if ((mpt3sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
+ sas_iounit_pg0, sz))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ /* sas_iounit page 1 */
+ sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys *
+ sizeof(Mpi2SasIOUnit1PhyData_t));
+ sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg1) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ if ((mpt3sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
+ sas_iounit_pg1, sz))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ ioc->io_missing_delay =
+ sas_iounit_pg1->IODeviceMissingDelay;
+ device_missing_delay =
+ sas_iounit_pg1->ReportDeviceMissingDelay;
+ if (device_missing_delay & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16)
+ ioc->device_missing_delay = (device_missing_delay &
+ MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16;
+ else
+ ioc->device_missing_delay = device_missing_delay &
+ MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK;
+
+ ioc->sas_hba.parent_dev = &ioc->shost->shost_gendev;
+ ioc->sas_hba.phy = kcalloc(ioc->sas_hba.num_phys,
+ sizeof(struct _sas_phy), GFP_KERNEL);
+ if (!ioc->sas_hba.phy) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ for (i = 0; i < ioc->sas_hba.num_phys ; i++) {
+ if ((mpt3sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0,
+ i))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+
+ if (i == 0)
+ ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0->
+ PhyData[0].ControllerDevHandle);
+ ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle;
+ ioc->sas_hba.phy[i].phy_id = i;
+ mpt3sas_transport_add_host_phy(ioc, &ioc->sas_hba.phy[i],
+ phy_pg0, ioc->sas_hba.parent_dev);
+ }
+ if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ioc->sas_hba.handle))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out;
+ }
+ ioc->sas_hba.enclosure_handle =
+ le16_to_cpu(sas_device_pg0.EnclosureHandle);
+ ioc->sas_hba.sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
+ pr_info(MPT3SAS_FMT
+ "host_add: handle(0x%04x), sas_addr(0x%016llx), phys(%d)\n",
+ ioc->name, ioc->sas_hba.handle,
+ (unsigned long long) ioc->sas_hba.sas_address,
+ ioc->sas_hba.num_phys) ;
+
+ if (ioc->sas_hba.enclosure_handle) {
+ if (!(mpt3sas_config_get_enclosure_pg0(ioc, &mpi_reply,
+ &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE,
+ ioc->sas_hba.enclosure_handle)))
+ ioc->sas_hba.enclosure_logical_id =
+ le64_to_cpu(enclosure_pg0.EnclosureLogicalID);
+ }
+
+ out:
+ kfree(sas_iounit_pg1);
+ kfree(sas_iounit_pg0);
+}
+
+/**
+ * _scsih_expander_add - creating expander object
+ * @ioc: per adapter object
+ * @handle: expander handle
+ *
+ * Creating expander object, stored in ioc->sas_expander_list.
+ *
+ * Return 0 for success, else error.
+ */
+static int
+_scsih_expander_add(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _sas_node *sas_expander;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2ExpanderPage0_t expander_pg0;
+ Mpi2ExpanderPage1_t expander_pg1;
+ Mpi2SasEnclosurePage0_t enclosure_pg0;
+ u32 ioc_status;
+ u16 parent_handle;
+ u64 sas_address, sas_address_parent = 0;
+ int i;
+ unsigned long flags;
+ struct _sas_port *mpt3sas_port = NULL;
+
+ int rc = 0;
+
+ if (!handle)
+ return -1;
+
+ if (ioc->shost_recovery || ioc->pci_error_recovery)
+ return -1;
+
+ if ((mpt3sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0,
+ MPI2_SAS_EXPAND_PGAD_FORM_HNDL, handle))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ /* handle out of order topology events */
+ parent_handle = le16_to_cpu(expander_pg0.ParentDevHandle);
+ if (_scsih_get_sas_address(ioc, parent_handle, &sas_address_parent)
+ != 0) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -1;
+ }
+ if (sas_address_parent != ioc->sas_hba.sas_address) {
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_expander = mpt3sas_scsih_expander_find_by_sas_address(ioc,
+ sas_address_parent);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ if (!sas_expander) {
+ rc = _scsih_expander_add(ioc, parent_handle);
+ if (rc != 0)
+ return rc;
+ }
+ }
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_address = le64_to_cpu(expander_pg0.SASAddress);
+ sas_expander = mpt3sas_scsih_expander_find_by_sas_address(ioc,
+ sas_address);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ if (sas_expander)
+ return 0;
+
+ sas_expander = kzalloc(sizeof(struct _sas_node),
+ GFP_KERNEL);
+ if (!sas_expander) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ sas_expander->handle = handle;
+ sas_expander->num_phys = expander_pg0.NumPhys;
+ sas_expander->sas_address_parent = sas_address_parent;
+ sas_expander->sas_address = sas_address;
+
+ pr_info(MPT3SAS_FMT "expander_add: handle(0x%04x)," \
+ " parent(0x%04x), sas_addr(0x%016llx), phys(%d)\n", ioc->name,
+ handle, parent_handle, (unsigned long long)
+ sas_expander->sas_address, sas_expander->num_phys);
+
+ if (!sas_expander->num_phys)
+ goto out_fail;
+ sas_expander->phy = kcalloc(sas_expander->num_phys,
+ sizeof(struct _sas_phy), GFP_KERNEL);
+ if (!sas_expander->phy) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -1;
+ goto out_fail;
+ }
+
+ INIT_LIST_HEAD(&sas_expander->sas_port_list);
+ mpt3sas_port = mpt3sas_transport_port_add(ioc, handle,
+ sas_address_parent);
+ if (!mpt3sas_port) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -1;
+ goto out_fail;
+ }
+ sas_expander->parent_dev = &mpt3sas_port->rphy->dev;
+
+ for (i = 0 ; i < sas_expander->num_phys ; i++) {
+ if ((mpt3sas_config_get_expander_pg1(ioc, &mpi_reply,
+ &expander_pg1, i, handle))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -1;
+ goto out_fail;
+ }
+ sas_expander->phy[i].handle = handle;
+ sas_expander->phy[i].phy_id = i;
+
+ if ((mpt3sas_transport_add_expander_phy(ioc,
+ &sas_expander->phy[i], expander_pg1,
+ sas_expander->parent_dev))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -1;
+ goto out_fail;
+ }
+ }
+
+ if (sas_expander->enclosure_handle) {
+ if (!(mpt3sas_config_get_enclosure_pg0(ioc, &mpi_reply,
+ &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE,
+ sas_expander->enclosure_handle)))
+ sas_expander->enclosure_logical_id =
+ le64_to_cpu(enclosure_pg0.EnclosureLogicalID);
+ }
+
+ _scsih_expander_node_add(ioc, sas_expander);
+ return 0;
+
+ out_fail:
+
+ if (mpt3sas_port)
+ mpt3sas_transport_port_remove(ioc, sas_expander->sas_address,
+ sas_address_parent);
+ kfree(sas_expander);
+ return rc;
+}
+
+/**
+ * mpt3sas_expander_remove - removing expander object
+ * @ioc: per adapter object
+ * @sas_address: expander sas_address
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_expander_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address)
+{
+ struct _sas_node *sas_expander;
+ unsigned long flags;
+
+ if (ioc->shost_recovery)
+ return;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_expander = mpt3sas_scsih_expander_find_by_sas_address(ioc,
+ sas_address);
+ if (sas_expander)
+ list_del(&sas_expander->list);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ if (sas_expander)
+ _scsih_expander_node_remove(ioc, sas_expander);
+}
+
+/**
+ * _scsih_done - internal SCSI_IO callback handler.
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Callback handler when sending internal generated SCSI_IO.
+ * The callback index passed is `ioc->scsih_cb_idx`
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+static u8
+_scsih_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ if (ioc->scsih_cmds.status == MPT3_CMD_NOT_USED)
+ return 1;
+ if (ioc->scsih_cmds.smid != smid)
+ return 1;
+ ioc->scsih_cmds.status |= MPT3_CMD_COMPLETE;
+ if (mpi_reply) {
+ memcpy(ioc->scsih_cmds.reply, mpi_reply,
+ mpi_reply->MsgLength*4);
+ ioc->scsih_cmds.status |= MPT3_CMD_REPLY_VALID;
+ }
+ ioc->scsih_cmds.status &= ~MPT3_CMD_PENDING;
+ complete(&ioc->scsih_cmds.done);
+ return 1;
+}
+
+
+
+
+#define MPT3_MAX_LUNS (255)
+
+
+/**
+ * _scsih_check_access_status - check access flags
+ * @ioc: per adapter object
+ * @sas_address: sas address
+ * @handle: sas device handle
+ * @access_flags: errors returned during discovery of the device
+ *
+ * Return 0 for success, else failure
+ */
+static u8
+_scsih_check_access_status(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
+ u16 handle, u8 access_status)
+{
+ u8 rc = 1;
+ char *desc = NULL;
+
+ switch (access_status) {
+ case MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS:
+ case MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION:
+ rc = 0;
+ break;
+ case MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED:
+ desc = "sata capability failed";
+ break;
+ case MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT:
+ desc = "sata affiliation conflict";
+ break;
+ case MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE:
+ desc = "route not addressable";
+ break;
+ case MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE:
+ desc = "smp error not addressable";
+ break;
+ case MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED:
+ desc = "device blocked";
+ break;
+ case MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE:
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_MAX:
+ desc = "sata initialization failed";
+ break;
+ default:
+ desc = "unknown";
+ break;
+ }
+
+ if (!rc)
+ return 0;
+
+ pr_err(MPT3SAS_FMT
+ "discovery errors(%s): sas_address(0x%016llx), handle(0x%04x)\n",
+ ioc->name, desc, (unsigned long long)sas_address, handle);
+ return rc;
+}
+
+/**
+ * _scsih_check_device - checking device responsiveness
+ * @ioc: per adapter object
+ * @parent_sas_address: sas address of parent expander or sas host
+ * @handle: attached device handle
+ * @phy_numberv: phy number
+ * @link_rate: new link rate
+ *
+ * Returns nothing.
+ */
+static void
+_scsih_check_device(struct MPT3SAS_ADAPTER *ioc,
+ u64 parent_sas_address, u16 handle, u8 phy_number, u8 link_rate)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ struct _sas_device *sas_device;
+ u32 ioc_status;
+ unsigned long flags;
+ u64 sas_address;
+ struct scsi_target *starget;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ u32 device_info;
+
+
+ if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle)))
+ return;
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+ return;
+
+ /* wide port handling ~ we need only handle device once for the phy that
+ * is matched in sas device page zero
+ */
+ if (phy_number != sas_device_pg0.PhyNum)
+ return;
+
+ /* check if this is end device */
+ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
+ if (!(_scsih_is_end_device(device_info)))
+ return;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
+ sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+ sas_address);
+
+ if (!sas_device) {
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ return;
+ }
+
+ if (unlikely(sas_device->handle != handle)) {
+ starget = sas_device->starget;
+ sas_target_priv_data = starget->hostdata;
+ starget_printk(KERN_INFO, starget,
+ "handle changed from(0x%04x) to (0x%04x)!!!\n",
+ sas_device->handle, handle);
+ sas_target_priv_data->handle = handle;
+ sas_device->handle = handle;
+ }
+
+ /* check if device is present */
+ if (!(le16_to_cpu(sas_device_pg0.Flags) &
+ MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) {
+ pr_err(MPT3SAS_FMT
+ "device is not present handle(0x%04x), flags!!!\n",
+ ioc->name, handle);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ return;
+ }
+
+ /* check if there were any issues with discovery */
+ if (_scsih_check_access_status(ioc, sas_address, handle,
+ sas_device_pg0.AccessStatus)) {
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ return;
+ }
+
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ _scsih_ublock_io_device(ioc, sas_address);
+
+}
+
+/**
+ * _scsih_add_device - creating sas device object
+ * @ioc: per adapter object
+ * @handle: sas device handle
+ * @phy_num: phy number end device attached to
+ * @is_pd: is this hidden raid component
+ *
+ * Creating end device object, stored in ioc->sas_device_list.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num,
+ u8 is_pd)
+{
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ Mpi2SasEnclosurePage0_t enclosure_pg0;
+ struct _sas_device *sas_device;
+ u32 ioc_status;
+ u64 sas_address;
+ u32 device_info;
+ unsigned long flags;
+
+ if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -1;
+ }
+
+ /* check if this is end device */
+ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
+ if (!(_scsih_is_end_device(device_info)))
+ return -1;
+ sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
+
+ /* check if device is present */
+ if (!(le16_to_cpu(sas_device_pg0.Flags) &
+ MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) {
+ pr_err(MPT3SAS_FMT "device is not present handle(0x04%x)!!!\n",
+ ioc->name, handle);
+ return -1;
+ }
+
+ /* check if there were any issues with discovery */
+ if (_scsih_check_access_status(ioc, sas_address, handle,
+ sas_device_pg0.AccessStatus))
+ return -1;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+ sas_address);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ if (sas_device)
+ return -1;
+
+ sas_device = kzalloc(sizeof(struct _sas_device),
+ GFP_KERNEL);
+ if (!sas_device) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+
+ sas_device->handle = handle;
+ if (_scsih_get_sas_address(ioc,
+ le16_to_cpu(sas_device_pg0.ParentDevHandle),
+ &sas_device->sas_address_parent) != 0)
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ sas_device->enclosure_handle =
+ le16_to_cpu(sas_device_pg0.EnclosureHandle);
+ sas_device->slot =
+ le16_to_cpu(sas_device_pg0.Slot);
+ sas_device->device_info = device_info;
+ sas_device->sas_address = sas_address;
+ sas_device->phy = sas_device_pg0.PhyNum;
+ sas_device->fast_path = (le16_to_cpu(sas_device_pg0.Flags) &
+ MPI25_SAS_DEVICE0_FLAGS_FAST_PATH_CAPABLE) ? 1 : 0;
+
+ /* get enclosure_logical_id */
+ if (sas_device->enclosure_handle && !(mpt3sas_config_get_enclosure_pg0(
+ ioc, &mpi_reply, &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE,
+ sas_device->enclosure_handle)))
+ sas_device->enclosure_logical_id =
+ le64_to_cpu(enclosure_pg0.EnclosureLogicalID);
+
+ /* get device name */
+ sas_device->device_name = le64_to_cpu(sas_device_pg0.DeviceName);
+
+ if (ioc->wait_for_discovery_to_complete)
+ _scsih_sas_device_init_add(ioc, sas_device);
+ else
+ _scsih_sas_device_add(ioc, sas_device);
+
+ return 0;
+}
+
+/**
+ * _scsih_remove_device - removing sas device object
+ * @ioc: per adapter object
+ * @sas_device_delete: the sas_device object
+ *
+ * Return nothing.
+ */
+static void
+_scsih_remove_device(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_device *sas_device)
+{
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+
+
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: enter: handle(0x%04x), sas_addr(0x%016llx)\n",
+ ioc->name, __func__,
+ sas_device->handle, (unsigned long long)
+ sas_device->sas_address));
+
+ if (sas_device->starget && sas_device->starget->hostdata) {
+ sas_target_priv_data = sas_device->starget->hostdata;
+ sas_target_priv_data->deleted = 1;
+ _scsih_ublock_io_device(ioc, sas_device->sas_address);
+ sas_target_priv_data->handle =
+ MPT3SAS_INVALID_DEVICE_HANDLE;
+ }
+ mpt3sas_transport_port_remove(ioc,
+ sas_device->sas_address,
+ sas_device->sas_address_parent);
+
+ pr_info(MPT3SAS_FMT
+ "removing handle(0x%04x), sas_addr(0x%016llx)\n",
+ ioc->name, sas_device->handle,
+ (unsigned long long) sas_device->sas_address);
+
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: exit: handle(0x%04x), sas_addr(0x%016llx)\n",
+ ioc->name, __func__,
+ sas_device->handle, (unsigned long long)
+ sas_device->sas_address));
+
+ kfree(sas_device);
+}
+
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+/**
+ * _scsih_sas_topology_change_event_debug - debug for topology event
+ * @ioc: per adapter object
+ * @event_data: event data payload
+ * Context: user.
+ */
+static void
+_scsih_sas_topology_change_event_debug(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataSasTopologyChangeList_t *event_data)
+{
+ int i;
+ u16 handle;
+ u16 reason_code;
+ u8 phy_number;
+ char *status_str = NULL;
+ u8 link_rate, prev_link_rate;
+
+ switch (event_data->ExpStatus) {
+ case MPI2_EVENT_SAS_TOPO_ES_ADDED:
+ status_str = "add";
+ break;
+ case MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING:
+ status_str = "remove";
+ break;
+ case MPI2_EVENT_SAS_TOPO_ES_RESPONDING:
+ case 0:
+ status_str = "responding";
+ break;
+ case MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING:
+ status_str = "remove delay";
+ break;
+ default:
+ status_str = "unknown status";
+ break;
+ }
+ pr_info(MPT3SAS_FMT "sas topology change: (%s)\n",
+ ioc->name, status_str);
+ pr_info("\thandle(0x%04x), enclosure_handle(0x%04x) " \
+ "start_phy(%02d), count(%d)\n",
+ le16_to_cpu(event_data->ExpanderDevHandle),
+ le16_to_cpu(event_data->EnclosureHandle),
+ event_data->StartPhyNum, event_data->NumEntries);
+ for (i = 0; i < event_data->NumEntries; i++) {
+ handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
+ if (!handle)
+ continue;
+ phy_number = event_data->StartPhyNum + i;
+ reason_code = event_data->PHY[i].PhyStatus &
+ MPI2_EVENT_SAS_TOPO_RC_MASK;
+ switch (reason_code) {
+ case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
+ status_str = "target add";
+ break;
+ case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
+ status_str = "target remove";
+ break;
+ case MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING:
+ status_str = "delay target remove";
+ break;
+ case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
+ status_str = "link rate change";
+ break;
+ case MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE:
+ status_str = "target responding";
+ break;
+ default:
+ status_str = "unknown";
+ break;
+ }
+ link_rate = event_data->PHY[i].LinkRate >> 4;
+ prev_link_rate = event_data->PHY[i].LinkRate & 0xF;
+ pr_info("\tphy(%02d), attached_handle(0x%04x): %s:" \
+ " link rate: new(0x%02x), old(0x%02x)\n", phy_number,
+ handle, status_str, link_rate, prev_link_rate);
+
+ }
+}
+#endif
+
+/**
+ * _scsih_sas_topology_change_event - handle topology changes
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ *
+ */
+static int
+_scsih_sas_topology_change_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ int i;
+ u16 parent_handle, handle;
+ u16 reason_code;
+ u8 phy_number, max_phys;
+ struct _sas_node *sas_expander;
+ u64 sas_address;
+ unsigned long flags;
+ u8 link_rate, prev_link_rate;
+ Mpi2EventDataSasTopologyChangeList_t *event_data = fw_event->event_data;
+
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ _scsih_sas_topology_change_event_debug(ioc, event_data);
+#endif
+
+ if (ioc->shost_recovery || ioc->remove_host || ioc->pci_error_recovery)
+ return 0;
+
+ if (!ioc->sas_hba.num_phys)
+ _scsih_sas_host_add(ioc);
+ else
+ _scsih_sas_host_refresh(ioc);
+
+ if (fw_event->ignore) {
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "ignoring expander event\n", ioc->name));
+ return 0;
+ }
+
+ parent_handle = le16_to_cpu(event_data->ExpanderDevHandle);
+
+ /* handle expander add */
+ if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_ADDED)
+ if (_scsih_expander_add(ioc, parent_handle) != 0)
+ return 0;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_expander = mpt3sas_scsih_expander_find_by_handle(ioc,
+ parent_handle);
+ if (sas_expander) {
+ sas_address = sas_expander->sas_address;
+ max_phys = sas_expander->num_phys;
+ } else if (parent_handle < ioc->sas_hba.num_phys) {
+ sas_address = ioc->sas_hba.sas_address;
+ max_phys = ioc->sas_hba.num_phys;
+ } else {
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ /* handle siblings events */
+ for (i = 0; i < event_data->NumEntries; i++) {
+ if (fw_event->ignore) {
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "ignoring expander event\n", ioc->name));
+ return 0;
+ }
+ if (ioc->remove_host || ioc->pci_error_recovery)
+ return 0;
+ phy_number = event_data->StartPhyNum + i;
+ if (phy_number >= max_phys)
+ continue;
+ reason_code = event_data->PHY[i].PhyStatus &
+ MPI2_EVENT_SAS_TOPO_RC_MASK;
+ if ((event_data->PHY[i].PhyStatus &
+ MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT) && (reason_code !=
+ MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING))
+ continue;
+ handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
+ if (!handle)
+ continue;
+ link_rate = event_data->PHY[i].LinkRate >> 4;
+ prev_link_rate = event_data->PHY[i].LinkRate & 0xF;
+ switch (reason_code) {
+ case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
+
+ if (ioc->shost_recovery)
+ break;
+
+ if (link_rate == prev_link_rate)
+ break;
+
+ mpt3sas_transport_update_links(ioc, sas_address,
+ handle, phy_number, link_rate);
+
+ if (link_rate < MPI2_SAS_NEG_LINK_RATE_1_5)
+ break;
+
+ _scsih_check_device(ioc, sas_address, handle,
+ phy_number, link_rate);
+
+
+ case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
+
+ if (ioc->shost_recovery)
+ break;
+
+ mpt3sas_transport_update_links(ioc, sas_address,
+ handle, phy_number, link_rate);
+
+ _scsih_add_device(ioc, handle, phy_number, 0);
+
+ break;
+ case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
+
+ _scsih_device_remove_by_handle(ioc, handle);
+ break;
+ }
+ }
+
+ /* handle expander removal */
+ if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING &&
+ sas_expander)
+ mpt3sas_expander_remove(ioc, sas_address);
+
+ return 0;
+}
+
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+/**
+ * _scsih_sas_device_status_change_event_debug - debug for device event
+ * @event_data: event data payload
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_device_status_change_event_debug(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataSasDeviceStatusChange_t *event_data)
+{
+ char *reason_str = NULL;
+
+ switch (event_data->ReasonCode) {
+ case MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA:
+ reason_str = "smart data";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED:
+ reason_str = "unsupported device discovered";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET:
+ reason_str = "internal device reset";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL:
+ reason_str = "internal task abort";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL:
+ reason_str = "internal task abort set";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL:
+ reason_str = "internal clear task set";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL:
+ reason_str = "internal query task";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE:
+ reason_str = "sata init failure";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET:
+ reason_str = "internal device reset complete";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL:
+ reason_str = "internal task abort complete";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION:
+ reason_str = "internal async notification";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_EXPANDER_REDUCED_FUNCTIONALITY:
+ reason_str = "expander reduced functionality";
+ break;
+ case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_EXPANDER_REDUCED_FUNCTIONALITY:
+ reason_str = "expander reduced functionality complete";
+ break;
+ default:
+ reason_str = "unknown reason";
+ break;
+ }
+ pr_info(MPT3SAS_FMT "device status change: (%s)\n"
+ "\thandle(0x%04x), sas address(0x%016llx), tag(%d)",
+ ioc->name, reason_str, le16_to_cpu(event_data->DevHandle),
+ (unsigned long long)le64_to_cpu(event_data->SASAddress),
+ le16_to_cpu(event_data->TaskTag));
+ if (event_data->ReasonCode == MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA)
+ pr_info(MPT3SAS_FMT ", ASC(0x%x), ASCQ(0x%x)\n", ioc->name,
+ event_data->ASC, event_data->ASCQ);
+ pr_info("\n");
+}
+#endif
+
+/**
+ * _scsih_sas_device_status_change_event - handle device status change
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_device_status_change_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ struct MPT3SAS_TARGET *target_priv_data;
+ struct _sas_device *sas_device;
+ u64 sas_address;
+ unsigned long flags;
+ Mpi2EventDataSasDeviceStatusChange_t *event_data =
+ fw_event->event_data;
+
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ _scsih_sas_device_status_change_event_debug(ioc,
+ event_data);
+#endif
+
+ /* In MPI Revision K (0xC), the internal device reset complete was
+ * implemented, so avoid setting tm_busy flag for older firmware.
+ */
+ if ((ioc->facts.HeaderVersion >> 8) < 0xC)
+ return;
+
+ if (event_data->ReasonCode !=
+ MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET &&
+ event_data->ReasonCode !=
+ MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET)
+ return;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_address = le64_to_cpu(event_data->SASAddress);
+ sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+ sas_address);
+
+ if (!sas_device || !sas_device->starget) {
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ return;
+ }
+
+ target_priv_data = sas_device->starget->hostdata;
+ if (!target_priv_data) {
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ return;
+ }
+
+ if (event_data->ReasonCode ==
+ MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET)
+ target_priv_data->tm_busy = 1;
+ else
+ target_priv_data->tm_busy = 0;
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+}
+
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+/**
+ * _scsih_sas_enclosure_dev_status_change_event_debug - debug for enclosure
+ * event
+ * @ioc: per adapter object
+ * @event_data: event data payload
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_enclosure_dev_status_change_event_debug(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataSasEnclDevStatusChange_t *event_data)
+{
+ char *reason_str = NULL;
+
+ switch (event_data->ReasonCode) {
+ case MPI2_EVENT_SAS_ENCL_RC_ADDED:
+ reason_str = "enclosure add";
+ break;
+ case MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING:
+ reason_str = "enclosure remove";
+ break;
+ default:
+ reason_str = "unknown reason";
+ break;
+ }
+
+ pr_info(MPT3SAS_FMT "enclosure status change: (%s)\n"
+ "\thandle(0x%04x), enclosure logical id(0x%016llx)"
+ " number slots(%d)\n", ioc->name, reason_str,
+ le16_to_cpu(event_data->EnclosureHandle),
+ (unsigned long long)le64_to_cpu(event_data->EnclosureLogicalID),
+ le16_to_cpu(event_data->StartSlot));
+}
+#endif
+
+/**
+ * _scsih_sas_enclosure_dev_status_change_event - handle enclosure events
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_enclosure_dev_status_change_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ _scsih_sas_enclosure_dev_status_change_event_debug(ioc,
+ fw_event->event_data);
+#endif
+}
+
+/**
+ * _scsih_sas_broadcast_primitive_event - handle broadcast events
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_broadcast_primitive_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ struct scsi_cmnd *scmd;
+ struct scsi_device *sdev;
+ u16 smid, handle;
+ u32 lun;
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ u32 termination_count;
+ u32 query_count;
+ Mpi2SCSITaskManagementReply_t *mpi_reply;
+ Mpi2EventDataSasBroadcastPrimitive_t *event_data = fw_event->event_data;
+ u16 ioc_status;
+ unsigned long flags;
+ int r;
+ u8 max_retries = 0;
+ u8 task_abort_retries;
+
+ mutex_lock(&ioc->tm_cmds.mutex);
+ pr_info(MPT3SAS_FMT
+ "%s: enter: phy number(%d), width(%d)\n",
+ ioc->name, __func__, event_data->PhyNum,
+ event_data->PortWidth);
+
+ _scsih_block_io_all_device(ioc);
+
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ mpi_reply = ioc->tm_cmds.reply;
+ broadcast_aen_retry:
+
+ /* sanity checks for retrying this loop */
+ if (max_retries++ == 5) {
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT "%s: giving up\n",
+ ioc->name, __func__));
+ goto out;
+ } else if (max_retries > 1)
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT "%s: %d retry\n",
+ ioc->name, __func__, max_retries - 1));
+
+ termination_count = 0;
+ query_count = 0;
+ for (smid = 1; smid <= ioc->scsiio_depth; smid++) {
+ if (ioc->shost_recovery)
+ goto out;
+ scmd = _scsih_scsi_lookup_get(ioc, smid);
+ if (!scmd)
+ continue;
+ sdev = scmd->device;
+ sas_device_priv_data = sdev->hostdata;
+ if (!sas_device_priv_data || !sas_device_priv_data->sas_target)
+ continue;
+ /* skip hidden raid components */
+ if (sas_device_priv_data->sas_target->flags &
+ MPT_TARGET_FLAGS_RAID_COMPONENT)
+ continue;
+ /* skip volumes */
+ if (sas_device_priv_data->sas_target->flags &
+ MPT_TARGET_FLAGS_VOLUME)
+ continue;
+
+ handle = sas_device_priv_data->sas_target->handle;
+ lun = sas_device_priv_data->lun;
+ query_count++;
+
+ if (ioc->shost_recovery)
+ goto out;
+
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ r = mpt3sas_scsih_issue_tm(ioc, handle, 0, 0, lun,
+ MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30, 0,
+ TM_MUTEX_OFF);
+ if (r == FAILED) {
+ sdev_printk(KERN_WARNING, sdev,
+ "mpt3sas_scsih_issue_tm: FAILED when sending "
+ "QUERY_TASK: scmd(%p)\n", scmd);
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ goto broadcast_aen_retry;
+ }
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus)
+ & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ sdev_printk(KERN_WARNING, sdev,
+ "query task: FAILED with IOCSTATUS(0x%04x), scmd(%p)\n",
+ ioc_status, scmd);
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ goto broadcast_aen_retry;
+ }
+
+ /* see if IO is still owned by IOC and target */
+ if (mpi_reply->ResponseCode ==
+ MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED ||
+ mpi_reply->ResponseCode ==
+ MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC) {
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ continue;
+ }
+ task_abort_retries = 0;
+ tm_retry:
+ if (task_abort_retries++ == 60) {
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: ABORT_TASK: giving up\n", ioc->name,
+ __func__));
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ goto broadcast_aen_retry;
+ }
+
+ if (ioc->shost_recovery)
+ goto out_no_lock;
+
+ r = mpt3sas_scsih_issue_tm(ioc, handle, sdev->channel, sdev->id,
+ sdev->lun, MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30,
+ scmd->serial_number, TM_MUTEX_OFF);
+ if (r == FAILED) {
+ sdev_printk(KERN_WARNING, sdev,
+ "mpt3sas_scsih_issue_tm: ABORT_TASK: FAILED : "
+ "scmd(%p)\n", scmd);
+ goto tm_retry;
+ }
+
+ if (task_abort_retries > 1)
+ sdev_printk(KERN_WARNING, sdev,
+ "mpt3sas_scsih_issue_tm: ABORT_TASK: RETRIES (%d):"
+ " scmd(%p)\n",
+ task_abort_retries - 1, scmd);
+
+ termination_count += le32_to_cpu(mpi_reply->TerminationCount);
+ spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+ }
+
+ if (ioc->broadcast_aen_pending) {
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: loop back due to pending AEN\n",
+ ioc->name, __func__));
+ ioc->broadcast_aen_pending = 0;
+ goto broadcast_aen_retry;
+ }
+
+ out:
+ spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+ out_no_lock:
+
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s - exit, query_count = %d termination_count = %d\n",
+ ioc->name, __func__, query_count, termination_count));
+
+ ioc->broadcast_aen_busy = 0;
+ if (!ioc->shost_recovery)
+ _scsih_ublock_io_all_device(ioc);
+ mutex_unlock(&ioc->tm_cmds.mutex);
+}
+
+/**
+ * _scsih_sas_discovery_event - handle discovery events
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_discovery_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ Mpi2EventDataSasDiscovery_t *event_data = fw_event->event_data;
+
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) {
+ pr_info(MPT3SAS_FMT "discovery event: (%s)", ioc->name,
+ (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ?
+ "start" : "stop");
+ if (event_data->DiscoveryStatus)
+ pr_info("discovery_status(0x%08x)",
+ le32_to_cpu(event_data->DiscoveryStatus));
+ pr_info("\n");
+ }
+#endif
+
+ if (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED &&
+ !ioc->sas_hba.num_phys) {
+ if (disable_discovery > 0 && ioc->shost_recovery) {
+ /* Wait for the reset to complete */
+ while (ioc->shost_recovery)
+ ssleep(1);
+ }
+ _scsih_sas_host_add(ioc);
+ }
+}
+
+/**
+ * _scsih_ir_fastpath - turn on fastpath for IR physdisk
+ * @ioc: per adapter object
+ * @handle: device handle for physical disk
+ * @phys_disk_num: physical disk number
+ *
+ * Return 0 for success, else failure.
+ */
+static int
+_scsih_ir_fastpath(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phys_disk_num)
+{
+ Mpi2RaidActionRequest_t *mpi_request;
+ Mpi2RaidActionReply_t *mpi_reply;
+ u16 smid;
+ u8 issue_reset = 0;
+ int rc = 0;
+ u16 ioc_status;
+ u32 log_info;
+
+
+ mutex_lock(&ioc->scsih_cmds.mutex);
+
+ if (ioc->scsih_cmds.status != MPT3_CMD_NOT_USED) {
+ pr_err(MPT3SAS_FMT "%s: scsih_cmd in use\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+ ioc->scsih_cmds.status = MPT3_CMD_PENDING;
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->scsih_cb_idx);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED;
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->scsih_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi2RaidActionRequest_t));
+
+ mpi_request->Function = MPI2_FUNCTION_RAID_ACTION;
+ mpi_request->Action = MPI2_RAID_ACTION_PHYSDISK_HIDDEN;
+ mpi_request->PhysDiskNum = phys_disk_num;
+
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT "IR RAID_ACTION: turning fast "\
+ "path on for handle(0x%04x), phys_disk_num (0x%02x)\n", ioc->name,
+ handle, phys_disk_num));
+
+ init_completion(&ioc->scsih_cmds.done);
+ mpt3sas_base_put_smid_default(ioc, smid);
+ wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ);
+
+ if (!(ioc->scsih_cmds.status & MPT3_CMD_COMPLETE)) {
+ pr_err(MPT3SAS_FMT "%s: timeout\n",
+ ioc->name, __func__);
+ if (!(ioc->scsih_cmds.status & MPT3_CMD_RESET))
+ issue_reset = 1;
+ rc = -EFAULT;
+ goto out;
+ }
+
+ if (ioc->scsih_cmds.status & MPT3_CMD_REPLY_VALID) {
+
+ mpi_reply = ioc->scsih_cmds.reply;
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus);
+ if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE)
+ log_info = le32_to_cpu(mpi_reply->IOCLogInfo);
+ else
+ log_info = 0;
+ ioc_status &= MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "IR RAID_ACTION: failed: ioc_status(0x%04x), "
+ "loginfo(0x%08x)!!!\n", ioc->name, ioc_status,
+ log_info));
+ rc = -EFAULT;
+ } else
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "IR RAID_ACTION: completed successfully\n",
+ ioc->name));
+ }
+
+ out:
+ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED;
+ mutex_unlock(&ioc->scsih_cmds.mutex);
+
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ return rc;
+}
+
+/**
+ * _scsih_reprobe_lun - reprobing lun
+ * @sdev: scsi device struct
+ * @no_uld_attach: sdev->no_uld_attach flag setting
+ *
+ **/
+static void
+_scsih_reprobe_lun(struct scsi_device *sdev, void *no_uld_attach)
+{
+ int rc;
+ sdev->no_uld_attach = no_uld_attach ? 1 : 0;
+ sdev_printk(KERN_INFO, sdev, "%s raid component\n",
+ sdev->no_uld_attach ? "hidding" : "exposing");
+ rc = scsi_device_reprobe(sdev);
+}
+
+/**
+ * _scsih_sas_volume_add - add new volume
+ * @ioc: per adapter object
+ * @element: IR config element data
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_volume_add(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventIrConfigElement_t *element)
+{
+ struct _raid_device *raid_device;
+ unsigned long flags;
+ u64 wwid;
+ u16 handle = le16_to_cpu(element->VolDevHandle);
+ int rc;
+
+ mpt3sas_config_get_volume_wwid(ioc, handle, &wwid);
+ if (!wwid) {
+ pr_err(MPT3SAS_FMT
+ "failure at %s:%d/%s()!\n", ioc->name,
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_wwid(ioc, wwid);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+
+ if (raid_device)
+ return;
+
+ raid_device = kzalloc(sizeof(struct _raid_device), GFP_KERNEL);
+ if (!raid_device) {
+ pr_err(MPT3SAS_FMT
+ "failure at %s:%d/%s()!\n", ioc->name,
+ __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ raid_device->id = ioc->sas_id++;
+ raid_device->channel = RAID_CHANNEL;
+ raid_device->handle = handle;
+ raid_device->wwid = wwid;
+ _scsih_raid_device_add(ioc, raid_device);
+ if (!ioc->wait_for_discovery_to_complete) {
+ rc = scsi_add_device(ioc->shost, RAID_CHANNEL,
+ raid_device->id, 0);
+ if (rc)
+ _scsih_raid_device_remove(ioc, raid_device);
+ } else {
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ _scsih_determine_boot_device(ioc, raid_device, 1);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ }
+}
+
+/**
+ * _scsih_sas_volume_delete - delete volume
+ * @ioc: per adapter object
+ * @handle: volume device handle
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_volume_delete(struct MPT3SAS_ADAPTER *ioc, u16 handle)
+{
+ struct _raid_device *raid_device;
+ unsigned long flags;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ struct scsi_target *starget = NULL;
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_handle(ioc, handle);
+ if (raid_device) {
+ if (raid_device->starget) {
+ starget = raid_device->starget;
+ sas_target_priv_data = starget->hostdata;
+ sas_target_priv_data->deleted = 1;
+ }
+ pr_info(MPT3SAS_FMT "removing handle(0x%04x), wwid(0x%016llx)\n",
+ ioc->name, raid_device->handle,
+ (unsigned long long) raid_device->wwid);
+ list_del(&raid_device->list);
+ kfree(raid_device);
+ }
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ if (starget)
+ scsi_remove_target(&starget->dev);
+}
+
+/**
+ * _scsih_sas_pd_expose - expose pd component to /dev/sdX
+ * @ioc: per adapter object
+ * @element: IR config element data
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_pd_expose(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventIrConfigElement_t *element)
+{
+ struct _sas_device *sas_device;
+ struct scsi_target *starget = NULL;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ unsigned long flags;
+ u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+ if (sas_device) {
+ sas_device->volume_handle = 0;
+ sas_device->volume_wwid = 0;
+ clear_bit(handle, ioc->pd_handles);
+ if (sas_device->starget && sas_device->starget->hostdata) {
+ starget = sas_device->starget;
+ sas_target_priv_data = starget->hostdata;
+ sas_target_priv_data->flags &=
+ ~MPT_TARGET_FLAGS_RAID_COMPONENT;
+ }
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (!sas_device)
+ return;
+
+ /* exposing raid component */
+ if (starget)
+ starget_for_each_device(starget, NULL, _scsih_reprobe_lun);
+}
+
+/**
+ * _scsih_sas_pd_hide - hide pd component from /dev/sdX
+ * @ioc: per adapter object
+ * @element: IR config element data
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_pd_hide(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventIrConfigElement_t *element)
+{
+ struct _sas_device *sas_device;
+ struct scsi_target *starget = NULL;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ unsigned long flags;
+ u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
+ u16 volume_handle = 0;
+ u64 volume_wwid = 0;
+
+ mpt3sas_config_get_volume_handle(ioc, handle, &volume_handle);
+ if (volume_handle)
+ mpt3sas_config_get_volume_wwid(ioc, volume_handle,
+ &volume_wwid);
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+ if (sas_device) {
+ set_bit(handle, ioc->pd_handles);
+ if (sas_device->starget && sas_device->starget->hostdata) {
+ starget = sas_device->starget;
+ sas_target_priv_data = starget->hostdata;
+ sas_target_priv_data->flags |=
+ MPT_TARGET_FLAGS_RAID_COMPONENT;
+ sas_device->volume_handle = volume_handle;
+ sas_device->volume_wwid = volume_wwid;
+ }
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (!sas_device)
+ return;
+
+ /* hiding raid component */
+ _scsih_ir_fastpath(ioc, handle, element->PhysDiskNum);
+ if (starget)
+ starget_for_each_device(starget, (void *)1, _scsih_reprobe_lun);
+}
+
+/**
+ * _scsih_sas_pd_delete - delete pd component
+ * @ioc: per adapter object
+ * @element: IR config element data
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_pd_delete(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventIrConfigElement_t *element)
+{
+ u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
+
+ _scsih_device_remove_by_handle(ioc, handle);
+}
+
+/**
+ * _scsih_sas_pd_add - remove pd component
+ * @ioc: per adapter object
+ * @element: IR config element data
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_pd_add(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventIrConfigElement_t *element)
+{
+ struct _sas_device *sas_device;
+ unsigned long flags;
+ u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ u32 ioc_status;
+ u64 sas_address;
+ u16 parent_handle;
+
+ set_bit(handle, ioc->pd_handles);
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (sas_device) {
+ _scsih_ir_fastpath(ioc, handle, element->PhysDiskNum);
+ return;
+ }
+
+ if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
+ if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address))
+ mpt3sas_transport_update_links(ioc, sas_address, handle,
+ sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
+
+ _scsih_ir_fastpath(ioc, handle, element->PhysDiskNum);
+ _scsih_add_device(ioc, handle, 0, 1);
+}
+
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+/**
+ * _scsih_sas_ir_config_change_event_debug - debug for IR Config Change events
+ * @ioc: per adapter object
+ * @event_data: event data payload
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_ir_config_change_event_debug(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataIrConfigChangeList_t *event_data)
+{
+ Mpi2EventIrConfigElement_t *element;
+ u8 element_type;
+ int i;
+ char *reason_str = NULL, *element_str = NULL;
+
+ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
+
+ pr_info(MPT3SAS_FMT "raid config change: (%s), elements(%d)\n",
+ ioc->name, (le32_to_cpu(event_data->Flags) &
+ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ?
+ "foreign" : "native", event_data->NumElements);
+ for (i = 0; i < event_data->NumElements; i++, element++) {
+ switch (element->ReasonCode) {
+ case MPI2_EVENT_IR_CHANGE_RC_ADDED:
+ reason_str = "add";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_REMOVED:
+ reason_str = "remove";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_NO_CHANGE:
+ reason_str = "no change";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_HIDE:
+ reason_str = "hide";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_UNHIDE:
+ reason_str = "unhide";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED:
+ reason_str = "volume_created";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED:
+ reason_str = "volume_deleted";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED:
+ reason_str = "pd_created";
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED:
+ reason_str = "pd_deleted";
+ break;
+ default:
+ reason_str = "unknown reason";
+ break;
+ }
+ element_type = le16_to_cpu(element->ElementFlags) &
+ MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK;
+ switch (element_type) {
+ case MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT:
+ element_str = "volume";
+ break;
+ case MPI2_EVENT_IR_CHANGE_EFLAGS_VOLPHYSDISK_ELEMENT:
+ element_str = "phys disk";
+ break;
+ case MPI2_EVENT_IR_CHANGE_EFLAGS_HOTSPARE_ELEMENT:
+ element_str = "hot spare";
+ break;
+ default:
+ element_str = "unknown element";
+ break;
+ }
+ pr_info("\t(%s:%s), vol handle(0x%04x), " \
+ "pd handle(0x%04x), pd num(0x%02x)\n", element_str,
+ reason_str, le16_to_cpu(element->VolDevHandle),
+ le16_to_cpu(element->PhysDiskDevHandle),
+ element->PhysDiskNum);
+ }
+}
+#endif
+
+/**
+ * _scsih_sas_ir_config_change_event - handle ir configuration change events
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_ir_config_change_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ Mpi2EventIrConfigElement_t *element;
+ int i;
+ u8 foreign_config;
+ Mpi2EventDataIrConfigChangeList_t *event_data = fw_event->event_data;
+
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ _scsih_sas_ir_config_change_event_debug(ioc, event_data);
+
+#endif
+
+ foreign_config = (le32_to_cpu(event_data->Flags) &
+ MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? 1 : 0;
+
+ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0];
+ if (ioc->shost_recovery) {
+
+ for (i = 0; i < event_data->NumElements; i++, element++) {
+ if (element->ReasonCode == MPI2_EVENT_IR_CHANGE_RC_HIDE)
+ _scsih_ir_fastpath(ioc,
+ le16_to_cpu(element->PhysDiskDevHandle),
+ element->PhysDiskNum);
+ }
+ return;
+ }
+ for (i = 0; i < event_data->NumElements; i++, element++) {
+
+ switch (element->ReasonCode) {
+ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED:
+ case MPI2_EVENT_IR_CHANGE_RC_ADDED:
+ if (!foreign_config)
+ _scsih_sas_volume_add(ioc, element);
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED:
+ case MPI2_EVENT_IR_CHANGE_RC_REMOVED:
+ if (!foreign_config)
+ _scsih_sas_volume_delete(ioc,
+ le16_to_cpu(element->VolDevHandle));
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED:
+ _scsih_sas_pd_hide(ioc, element);
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED:
+ _scsih_sas_pd_expose(ioc, element);
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_HIDE:
+ _scsih_sas_pd_add(ioc, element);
+ break;
+ case MPI2_EVENT_IR_CHANGE_RC_UNHIDE:
+ _scsih_sas_pd_delete(ioc, element);
+ break;
+ }
+ }
+}
+
+/**
+ * _scsih_sas_ir_volume_event - IR volume event
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_ir_volume_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ u64 wwid;
+ unsigned long flags;
+ struct _raid_device *raid_device;
+ u16 handle;
+ u32 state;
+ int rc;
+ Mpi2EventDataIrVolume_t *event_data = fw_event->event_data;
+
+ if (ioc->shost_recovery)
+ return;
+
+ if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED)
+ return;
+
+ handle = le16_to_cpu(event_data->VolDevHandle);
+ state = le32_to_cpu(event_data->NewValue);
+ dewtprintk(ioc, pr_info(MPT3SAS_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:
+ case MPI2_RAID_VOL_STATE_FAILED:
+ _scsih_sas_volume_delete(ioc, handle);
+ break;
+
+ case MPI2_RAID_VOL_STATE_ONLINE:
+ case MPI2_RAID_VOL_STATE_DEGRADED:
+ case MPI2_RAID_VOL_STATE_OPTIMAL:
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_handle(ioc, handle);
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+
+ if (raid_device)
+ break;
+
+ mpt3sas_config_get_volume_wwid(ioc, handle, &wwid);
+ if (!wwid) {
+ pr_err(MPT3SAS_FMT
+ "failure at %s:%d/%s()!\n", ioc->name,
+ __FILE__, __LINE__, __func__);
+ break;
+ }
+
+ raid_device = kzalloc(sizeof(struct _raid_device), GFP_KERNEL);
+ if (!raid_device) {
+ pr_err(MPT3SAS_FMT
+ "failure at %s:%d/%s()!\n", ioc->name,
+ __FILE__, __LINE__, __func__);
+ break;
+ }
+
+ raid_device->id = ioc->sas_id++;
+ raid_device->channel = RAID_CHANNEL;
+ raid_device->handle = handle;
+ raid_device->wwid = wwid;
+ _scsih_raid_device_add(ioc, raid_device);
+ rc = scsi_add_device(ioc->shost, RAID_CHANNEL,
+ raid_device->id, 0);
+ if (rc)
+ _scsih_raid_device_remove(ioc, raid_device);
+ break;
+
+ case MPI2_RAID_VOL_STATE_INITIALIZING:
+ default:
+ break;
+ }
+}
+
+/**
+ * _scsih_sas_ir_physical_disk_event - PD event
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_ir_physical_disk_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ u16 handle, parent_handle;
+ u32 state;
+ struct _sas_device *sas_device;
+ unsigned long flags;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ u32 ioc_status;
+ Mpi2EventDataIrPhysicalDisk_t *event_data = fw_event->event_data;
+ u64 sas_address;
+
+ if (ioc->shost_recovery)
+ return;
+
+ if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED)
+ return;
+
+ handle = le16_to_cpu(event_data->PhysDiskDevHandle);
+ state = le32_to_cpu(event_data->NewValue);
+
+ dewtprintk(ioc, pr_info(MPT3SAS_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:
+ case MPI2_RAID_PD_STATE_DEGRADED:
+ case MPI2_RAID_PD_STATE_REBUILDING:
+ case MPI2_RAID_PD_STATE_OPTIMAL:
+ case MPI2_RAID_PD_STATE_HOT_SPARE:
+
+ set_bit(handle, ioc->pd_handles);
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ if (sas_device)
+ return;
+
+ if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply,
+ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
+ handle))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
+ if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address))
+ mpt3sas_transport_update_links(ioc, sas_address, handle,
+ sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
+
+ _scsih_add_device(ioc, handle, 0, 1);
+
+ break;
+
+ case MPI2_RAID_PD_STATE_OFFLINE:
+ case MPI2_RAID_PD_STATE_NOT_CONFIGURED:
+ case MPI2_RAID_PD_STATE_NOT_COMPATIBLE:
+ default:
+ break;
+ }
+}
+
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+/**
+ * _scsih_sas_ir_operation_status_event_debug - debug for IR op event
+ * @ioc: per adapter object
+ * @event_data: event data payload
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_ir_operation_status_event_debug(struct MPT3SAS_ADAPTER *ioc,
+ Mpi2EventDataIrOperationStatus_t *event_data)
+{
+ char *reason_str = NULL;
+
+ switch (event_data->RAIDOperation) {
+ case MPI2_EVENT_IR_RAIDOP_RESYNC:
+ reason_str = "resync";
+ break;
+ case MPI2_EVENT_IR_RAIDOP_ONLINE_CAP_EXPANSION:
+ reason_str = "online capacity expansion";
+ break;
+ case MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK:
+ reason_str = "consistency check";
+ break;
+ case MPI2_EVENT_IR_RAIDOP_BACKGROUND_INIT:
+ reason_str = "background init";
+ break;
+ case MPI2_EVENT_IR_RAIDOP_MAKE_DATA_CONSISTENT:
+ reason_str = "make data consistent";
+ break;
+ }
+
+ if (!reason_str)
+ return;
+
+ pr_info(MPT3SAS_FMT "raid operational status: (%s)" \
+ "\thandle(0x%04x), percent complete(%d)\n",
+ ioc->name, reason_str,
+ le16_to_cpu(event_data->VolDevHandle),
+ event_data->PercentComplete);
+}
+#endif
+
+/**
+ * _scsih_sas_ir_operation_status_event - handle RAID operation events
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_sas_ir_operation_status_event(struct MPT3SAS_ADAPTER *ioc,
+ struct fw_event_work *fw_event)
+{
+ Mpi2EventDataIrOperationStatus_t *event_data = fw_event->event_data;
+ static struct _raid_device *raid_device;
+ unsigned long flags;
+ u16 handle;
+
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+ if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
+ _scsih_sas_ir_operation_status_event_debug(ioc,
+ event_data);
+#endif
+
+ /* code added for raid transport support */
+ if (event_data->RAIDOperation == MPI2_EVENT_IR_RAIDOP_RESYNC) {
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ handle = le16_to_cpu(event_data->VolDevHandle);
+ raid_device = _scsih_raid_device_find_by_handle(ioc, handle);
+ if (raid_device)
+ raid_device->percent_complete =
+ event_data->PercentComplete;
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ }
+}
+
+/**
+ * _scsih_prep_device_scan - initialize parameters prior to device scan
+ * @ioc: per adapter object
+ *
+ * Set the deleted flag prior to device scan. If the device is found during
+ * the scan, then we clear the deleted flag.
+ */
+static void
+_scsih_prep_device_scan(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct MPT3SAS_DEVICE *sas_device_priv_data;
+ struct scsi_device *sdev;
+
+ shost_for_each_device(sdev, ioc->shost) {
+ sas_device_priv_data = sdev->hostdata;
+ if (sas_device_priv_data && sas_device_priv_data->sas_target)
+ sas_device_priv_data->sas_target->deleted = 1;
+ }
+}
+
+/**
+ * _scsih_mark_responding_sas_device - mark a sas_devices as responding
+ * @ioc: per adapter object
+ * @sas_address: sas address
+ * @slot: enclosure slot id
+ * @handle: device handle
+ *
+ * After host reset, find out whether devices are still responding.
+ * Used in _scsih_remove_unresponsive_sas_devices.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
+ u16 slot, u16 handle)
+{
+ struct MPT3SAS_TARGET *sas_target_priv_data = NULL;
+ struct scsi_target *starget;
+ struct _sas_device *sas_device;
+ unsigned long flags;
+
+ 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->responding = 1;
+ starget = sas_device->starget;
+ if (starget && starget->hostdata) {
+ sas_target_priv_data = starget->hostdata;
+ sas_target_priv_data->tm_busy = 0;
+ sas_target_priv_data->deleted = 0;
+ } else
+ sas_target_priv_data = NULL;
+ 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;
+ pr_info("\thandle changed from(0x%04x)!!!\n",
+ sas_device->handle);
+ sas_device->handle = handle;
+ if (sas_target_priv_data)
+ sas_target_priv_data->handle = handle;
+ goto out;
+ }
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+}
+
+/**
+ * _scsih_search_responding_sas_devices -
+ * @ioc: per adapter object
+ *
+ * After host reset, find out whether devices are still responding.
+ * If not remove.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_search_responding_sas_devices(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 ioc_status;
+ u16 handle;
+ u32 device_info;
+
+ pr_info(MPT3SAS_FMT "search for end-devices: start\n", ioc->name);
+
+ if (list_empty(&ioc->sas_device_list))
+ goto out;
+
+ handle = 0xFFFF;
+ while (!(mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply,
+ &sas_device_pg0, MPI2_SAS_DEVICE_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(sas_device_pg0.DevHandle);
+ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
+ if (!(_scsih_is_end_device(device_info)))
+ continue;
+ _scsih_mark_responding_sas_device(ioc,
+ le64_to_cpu(sas_device_pg0.SASAddress),
+ le16_to_cpu(sas_device_pg0.Slot), handle);
+ }
+
+ out:
+ pr_info(MPT3SAS_FMT "search for end-devices: complete\n",
+ ioc->name);
+}
+
+/**
+ * _scsih_mark_responding_raid_device - mark a raid_device as responding
+ * @ioc: per adapter object
+ * @wwid: world wide identifier for raid volume
+ * @handle: device handle
+ *
+ * After host reset, find out whether devices are still responding.
+ * Used in _scsih_remove_unresponsive_raid_devices.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_mark_responding_raid_device(struct MPT3SAS_ADAPTER *ioc, u64 wwid,
+ u16 handle)
+{
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ struct scsi_target *starget;
+ struct _raid_device *raid_device;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ list_for_each_entry(raid_device, &ioc->raid_device_list, list) {
+ if (raid_device->wwid == wwid && raid_device->starget) {
+ starget = raid_device->starget;
+ if (starget && starget->hostdata) {
+ sas_target_priv_data = starget->hostdata;
+ sas_target_priv_data->deleted = 0;
+ } else
+ sas_target_priv_data = NULL;
+ raid_device->responding = 1;
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ starget_printk(KERN_INFO, raid_device->starget,
+ "handle(0x%04x), wwid(0x%016llx)\n", handle,
+ (unsigned long long)raid_device->wwid);
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ if (raid_device->handle == handle) {
+ spin_unlock_irqrestore(&ioc->raid_device_lock,
+ flags);
+ return;
+ }
+ pr_info("\thandle changed from(0x%04x)!!!\n",
+ raid_device->handle);
+ raid_device->handle = handle;
+ if (sas_target_priv_data)
+ sas_target_priv_data->handle = handle;
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ return;
+ }
+ }
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+}
+
+/**
+ * _scsih_search_responding_raid_devices -
+ * @ioc: per adapter object
+ *
+ * After host reset, find out whether devices are still responding.
+ * If not remove.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_search_responding_raid_devices(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2RaidVolPage1_t volume_pg1;
+ Mpi2RaidVolPage0_t volume_pg0;
+ Mpi2RaidPhysDiskPage0_t pd_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 ioc_status;
+ u16 handle;
+ u8 phys_disk_num;
+
+ if (!ioc->ir_firmware)
+ return;
+
+ pr_info(MPT3SAS_FMT "search for raid volumes: start\n",
+ ioc->name);
+
+ if (list_empty(&ioc->raid_device_list))
+ goto out;
+
+ handle = 0xFFFF;
+ while (!(mpt3sas_config_get_raid_volume_pg1(ioc, &mpi_reply,
+ &volume_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(volume_pg1.DevHandle);
+
+ if (mpt3sas_config_get_raid_volume_pg0(ioc, &mpi_reply,
+ &volume_pg0, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle,
+ sizeof(Mpi2RaidVolPage0_t)))
+ continue;
+
+ if (volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_OPTIMAL ||
+ volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_ONLINE ||
+ volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_DEGRADED)
+ _scsih_mark_responding_raid_device(ioc,
+ le64_to_cpu(volume_pg1.WWID), handle);
+ }
+
+ /* refresh the pd_handles */
+ phys_disk_num = 0xFF;
+ memset(ioc->pd_handles, 0, ioc->pd_handles_sz);
+ while (!(mpt3sas_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);
+ }
+ out:
+ pr_info(MPT3SAS_FMT "search for responding raid volumes: complete\n",
+ ioc->name);
+}
+
+/**
+ * _scsih_mark_responding_expander - mark a expander as responding
+ * @ioc: per adapter object
+ * @sas_address: sas address
+ * @handle:
+ *
+ * After host reset, find out whether devices are still responding.
+ * Used in _scsih_remove_unresponsive_expanders.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_mark_responding_expander(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
+ u16 handle)
+{
+ struct _sas_node *sas_expander;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
+ if (sas_expander->sas_address != sas_address)
+ continue;
+ sas_expander->responding = 1;
+ if (sas_expander->handle == handle)
+ goto out;
+ pr_info("\texpander(0x%016llx): handle changed" \
+ " from(0x%04x) to (0x%04x)!!!\n",
+ (unsigned long long)sas_expander->sas_address,
+ sas_expander->handle, handle);
+ sas_expander->handle = handle;
+ for (i = 0 ; i < sas_expander->num_phys ; i++)
+ sas_expander->phy[i].handle = handle;
+ goto out;
+ }
+ out:
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+}
+
+/**
+ * _scsih_search_responding_expanders -
+ * @ioc: per adapter object
+ *
+ * After host reset, find out whether devices are still responding.
+ * If not remove.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_search_responding_expanders(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2ExpanderPage0_t expander_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 ioc_status;
+ u64 sas_address;
+ u16 handle;
+
+ pr_info(MPT3SAS_FMT "search for expanders: start\n", ioc->name);
+
+ if (list_empty(&ioc->sas_expander_list))
+ goto out;
+
+ handle = 0xFFFF;
+ while (!(mpt3sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0,
+ MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL, 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(expander_pg0.DevHandle);
+ sas_address = le64_to_cpu(expander_pg0.SASAddress);
+ pr_info("\texpander present: handle(0x%04x), sas_addr(0x%016llx)\n",
+ handle,
+ (unsigned long long)sas_address);
+ _scsih_mark_responding_expander(ioc, sas_address, handle);
+ }
+
+ out:
+ pr_info(MPT3SAS_FMT "search for expanders: complete\n", ioc->name);
+}
+
+/**
+ * _scsih_remove_unresponding_sas_devices - removing unresponding devices
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+static void
+_scsih_remove_unresponding_sas_devices(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct _sas_device *sas_device, *sas_device_next;
+ struct _sas_node *sas_expander, *sas_expander_next;
+ struct _raid_device *raid_device, *raid_device_next;
+ struct list_head tmp_list;
+ unsigned long flags;
+
+ pr_info(MPT3SAS_FMT "removing unresponding devices: start\n",
+ ioc->name);
+
+ /* removing unresponding end devices */
+ pr_info(MPT3SAS_FMT "removing unresponding devices: end-devices\n",
+ ioc->name);
+ list_for_each_entry_safe(sas_device, sas_device_next,
+ &ioc->sas_device_list, list) {
+ if (!sas_device->responding)
+ mpt3sas_device_remove_by_sas_address(ioc,
+ sas_device->sas_address);
+ else
+ sas_device->responding = 0;
+ }
+
+ /* removing unresponding volumes */
+ if (ioc->ir_firmware) {
+ pr_info(MPT3SAS_FMT "removing unresponding devices: volumes\n",
+ ioc->name);
+ list_for_each_entry_safe(raid_device, raid_device_next,
+ &ioc->raid_device_list, list) {
+ if (!raid_device->responding)
+ _scsih_sas_volume_delete(ioc,
+ raid_device->handle);
+ else
+ raid_device->responding = 0;
+ }
+ }
+
+ /* removing unresponding expanders */
+ pr_info(MPT3SAS_FMT "removing unresponding devices: expanders\n",
+ ioc->name);
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ INIT_LIST_HEAD(&tmp_list);
+ list_for_each_entry_safe(sas_expander, sas_expander_next,
+ &ioc->sas_expander_list, list) {
+ if (!sas_expander->responding)
+ list_move_tail(&sas_expander->list, &tmp_list);
+ else
+ sas_expander->responding = 0;
+ }
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ list_for_each_entry_safe(sas_expander, sas_expander_next, &tmp_list,
+ list) {
+ list_del(&sas_expander->list);
+ _scsih_expander_node_remove(ioc, sas_expander);
+ }
+
+ pr_info(MPT3SAS_FMT "removing unresponding devices: complete\n",
+ ioc->name);
+
+ /* unblock devices */
+ _scsih_ublock_io_all_device(ioc);
+}
+
+static void
+_scsih_refresh_expander_links(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_node *sas_expander, u16 handle)
+{
+ Mpi2ExpanderPage1_t expander_pg1;
+ Mpi2ConfigReply_t mpi_reply;
+ int i;
+
+ for (i = 0 ; i < sas_expander->num_phys ; i++) {
+ if ((mpt3sas_config_get_expander_pg1(ioc, &mpi_reply,
+ &expander_pg1, i, handle))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ mpt3sas_transport_update_links(ioc, sas_expander->sas_address,
+ le16_to_cpu(expander_pg1.AttachedDevHandle), i,
+ expander_pg1.NegotiatedLinkRate >> 4);
+ }
+}
+
+/**
+ * _scsih_scan_for_devices_after_reset - scan for devices after host reset
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+static void
+_scsih_scan_for_devices_after_reset(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2ExpanderPage0_t expander_pg0;
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ Mpi2RaidVolPage1_t volume_pg1;
+ Mpi2RaidVolPage0_t volume_pg0;
+ Mpi2RaidPhysDiskPage0_t pd_pg0;
+ Mpi2EventIrConfigElement_t element;
+ Mpi2ConfigReply_t mpi_reply;
+ u8 phys_disk_num;
+ u16 ioc_status;
+ u16 handle, parent_handle;
+ u64 sas_address;
+ struct _sas_device *sas_device;
+ struct _sas_node *expander_device;
+ static struct _raid_device *raid_device;
+ u8 retry_count;
+ unsigned long flags;
+
+ pr_info(MPT3SAS_FMT "scan devices: start\n", ioc->name);
+
+ _scsih_sas_host_refresh(ioc);
+
+ pr_info(MPT3SAS_FMT "\tscan devices: expanders start\n", ioc->name);
+
+ /* expanders */
+ handle = 0xFFFF;
+ while (!(mpt3sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0,
+ MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL, handle))) {
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ break;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_info(MPT3SAS_FMT "\tbreak from expander scan: " \
+ "ioc_status(0x%04x), loginfo(0x%08x)\n",
+ ioc->name, ioc_status,
+ le32_to_cpu(mpi_reply.IOCLogInfo));
+ break;
+ }
+ handle = le16_to_cpu(expander_pg0.DevHandle);
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ expander_device = mpt3sas_scsih_expander_find_by_sas_address(
+ ioc, le64_to_cpu(expander_pg0.SASAddress));
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ if (expander_device)
+ _scsih_refresh_expander_links(ioc, expander_device,
+ handle);
+ else {
+ pr_info(MPT3SAS_FMT "\tBEFORE adding expander: " \
+ "handle (0x%04x), sas_addr(0x%016llx)\n", ioc->name,
+ handle, (unsigned long long)
+ le64_to_cpu(expander_pg0.SASAddress));
+ _scsih_expander_add(ioc, handle);
+ pr_info(MPT3SAS_FMT "\tAFTER adding expander: " \
+ "handle (0x%04x), sas_addr(0x%016llx)\n", ioc->name,
+ handle, (unsigned long long)
+ le64_to_cpu(expander_pg0.SASAddress));
+ }
+ }
+
+ pr_info(MPT3SAS_FMT "\tscan devices: expanders complete\n",
+ ioc->name);
+
+ if (!ioc->ir_firmware)
+ goto skip_to_sas;
+
+ pr_info(MPT3SAS_FMT "\tscan devices: phys disk start\n", ioc->name);
+
+ /* phys disk */
+ phys_disk_num = 0xFF;
+ while (!(mpt3sas_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;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_info(MPT3SAS_FMT "\tbreak from phys disk scan: "\
+ "ioc_status(0x%04x), loginfo(0x%08x)\n",
+ ioc->name, ioc_status,
+ le32_to_cpu(mpi_reply.IOCLogInfo));
+ break;
+ }
+ phys_disk_num = pd_pg0.PhysDiskNum;
+ handle = le16_to_cpu(pd_pg0.DevHandle);
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (sas_device)
+ continue;
+ if (mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply,
+ &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
+ handle) != 0)
+ continue;
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_info(MPT3SAS_FMT "\tbreak from phys disk scan " \
+ "ioc_status(0x%04x), loginfo(0x%08x)\n",
+ ioc->name, ioc_status,
+ le32_to_cpu(mpi_reply.IOCLogInfo));
+ break;
+ }
+ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
+ if (!_scsih_get_sas_address(ioc, parent_handle,
+ &sas_address)) {
+ pr_info(MPT3SAS_FMT "\tBEFORE adding phys disk: " \
+ " handle (0x%04x), sas_addr(0x%016llx)\n",
+ ioc->name, handle, (unsigned long long)
+ le64_to_cpu(sas_device_pg0.SASAddress));
+ mpt3sas_transport_update_links(ioc, sas_address,
+ handle, sas_device_pg0.PhyNum,
+ MPI2_SAS_NEG_LINK_RATE_1_5);
+ set_bit(handle, ioc->pd_handles);
+ retry_count = 0;
+ /* This will retry adding the end device.
+ * _scsih_add_device() will decide on retries and
+ * return "1" when it should be retried
+ */
+ while (_scsih_add_device(ioc, handle, retry_count++,
+ 1)) {
+ ssleep(1);
+ }
+ pr_info(MPT3SAS_FMT "\tAFTER adding phys disk: " \
+ " handle (0x%04x), sas_addr(0x%016llx)\n",
+ ioc->name, handle, (unsigned long long)
+ le64_to_cpu(sas_device_pg0.SASAddress));
+ }
+ }
+
+ pr_info(MPT3SAS_FMT "\tscan devices: phys disk complete\n",
+ ioc->name);
+
+ pr_info(MPT3SAS_FMT "\tscan devices: volumes start\n", ioc->name);
+
+ /* volumes */
+ handle = 0xFFFF;
+ while (!(mpt3sas_config_get_raid_volume_pg1(ioc, &mpi_reply,
+ &volume_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;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_info(MPT3SAS_FMT "\tbreak from volume scan: " \
+ "ioc_status(0x%04x), loginfo(0x%08x)\n",
+ ioc->name, ioc_status,
+ le32_to_cpu(mpi_reply.IOCLogInfo));
+ break;
+ }
+ handle = le16_to_cpu(volume_pg1.DevHandle);
+ spin_lock_irqsave(&ioc->raid_device_lock, flags);
+ raid_device = _scsih_raid_device_find_by_wwid(ioc,
+ le64_to_cpu(volume_pg1.WWID));
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
+ if (raid_device)
+ continue;
+ if (mpt3sas_config_get_raid_volume_pg0(ioc, &mpi_reply,
+ &volume_pg0, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle,
+ sizeof(Mpi2RaidVolPage0_t)))
+ continue;
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_info(MPT3SAS_FMT "\tbreak from volume scan: " \
+ "ioc_status(0x%04x), loginfo(0x%08x)\n",
+ ioc->name, ioc_status,
+ le32_to_cpu(mpi_reply.IOCLogInfo));
+ break;
+ }
+ if (volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_OPTIMAL ||
+ volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_ONLINE ||
+ volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_DEGRADED) {
+ memset(&element, 0, sizeof(Mpi2EventIrConfigElement_t));
+ element.ReasonCode = MPI2_EVENT_IR_CHANGE_RC_ADDED;
+ element.VolDevHandle = volume_pg1.DevHandle;
+ pr_info(MPT3SAS_FMT
+ "\tBEFORE adding volume: handle (0x%04x)\n",
+ ioc->name, volume_pg1.DevHandle);
+ _scsih_sas_volume_add(ioc, &element);
+ pr_info(MPT3SAS_FMT
+ "\tAFTER adding volume: handle (0x%04x)\n",
+ ioc->name, volume_pg1.DevHandle);
+ }
+ }
+
+ pr_info(MPT3SAS_FMT "\tscan devices: volumes complete\n",
+ ioc->name);
+
+ skip_to_sas:
+
+ pr_info(MPT3SAS_FMT "\tscan devices: end devices start\n",
+ ioc->name);
+
+ /* sas devices */
+ handle = 0xFFFF;
+ while (!(mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply,
+ &sas_device_pg0, MPI2_SAS_DEVICE_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;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_info(MPT3SAS_FMT "\tbreak from end device scan:"\
+ " ioc_status(0x%04x), loginfo(0x%08x)\n",
+ ioc->name, ioc_status,
+ le32_to_cpu(mpi_reply.IOCLogInfo));
+ break;
+ }
+ handle = le16_to_cpu(sas_device_pg0.DevHandle);
+ if (!(_scsih_is_end_device(
+ le32_to_cpu(sas_device_pg0.DeviceInfo))))
+ continue;
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+ le64_to_cpu(sas_device_pg0.SASAddress));
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (sas_device)
+ continue;
+ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
+ if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) {
+ pr_info(MPT3SAS_FMT "\tBEFORE adding end device: " \
+ "handle (0x%04x), sas_addr(0x%016llx)\n", ioc->name,
+ handle, (unsigned long long)
+ le64_to_cpu(sas_device_pg0.SASAddress));
+ mpt3sas_transport_update_links(ioc, sas_address, handle,
+ sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
+ retry_count = 0;
+ /* This will retry adding the end device.
+ * _scsih_add_device() will decide on retries and
+ * return "1" when it should be retried
+ */
+ while (_scsih_add_device(ioc, handle, retry_count++,
+ 0)) {
+ ssleep(1);
+ }
+ pr_info(MPT3SAS_FMT "\tAFTER adding end device: " \
+ "handle (0x%04x), sas_addr(0x%016llx)\n", ioc->name,
+ handle, (unsigned long long)
+ le64_to_cpu(sas_device_pg0.SASAddress));
+ }
+ }
+ pr_info(MPT3SAS_FMT "\tscan devices: end devices complete\n",
+ ioc->name);
+
+ pr_info(MPT3SAS_FMT "scan devices: complete\n", ioc->name);
+}
+/**
+ * mpt3sas_scsih_reset_handler - reset callback handler (for scsih)
+ * @ioc: per adapter object
+ * @reset_phase: phase
+ *
+ * The handler for doing any required cleanup or initialization.
+ *
+ * The reset phase can be MPT3_IOC_PRE_RESET, MPT3_IOC_AFTER_RESET,
+ * MPT3_IOC_DONE_RESET
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_scsih_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase)
+{
+ switch (reset_phase) {
+ case MPT3_IOC_PRE_RESET:
+ dtmprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: MPT3_IOC_PRE_RESET\n", ioc->name, __func__));
+ break;
+ case MPT3_IOC_AFTER_RESET:
+ dtmprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: MPT3_IOC_AFTER_RESET\n", ioc->name, __func__));
+ if (ioc->scsih_cmds.status & MPT3_CMD_PENDING) {
+ ioc->scsih_cmds.status |= MPT3_CMD_RESET;
+ mpt3sas_base_free_smid(ioc, ioc->scsih_cmds.smid);
+ complete(&ioc->scsih_cmds.done);
+ }
+ if (ioc->tm_cmds.status & MPT3_CMD_PENDING) {
+ ioc->tm_cmds.status |= MPT3_CMD_RESET;
+ mpt3sas_base_free_smid(ioc, ioc->tm_cmds.smid);
+ complete(&ioc->tm_cmds.done);
+ }
+
+ _scsih_fw_event_cleanup_queue(ioc);
+ _scsih_flush_running_cmds(ioc);
+ break;
+ case MPT3_IOC_DONE_RESET:
+ dtmprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: MPT3_IOC_DONE_RESET\n", ioc->name, __func__));
+ if ((!ioc->is_driver_loading) && !(disable_discovery > 0 &&
+ !ioc->sas_hba.num_phys)) {
+ _scsih_prep_device_scan(ioc);
+ _scsih_search_responding_sas_devices(ioc);
+ _scsih_search_responding_raid_devices(ioc);
+ _scsih_search_responding_expanders(ioc);
+ _scsih_error_recovery_delete_devices(ioc);
+ }
+ break;
+ }
+}
+
+/**
+ * _mpt3sas_fw_work - delayed task for processing firmware events
+ * @ioc: per adapter object
+ * @fw_event: The fw_event_work object
+ * Context: user.
+ *
+ * Return nothing.
+ */
+static void
+_mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event)
+{
+ /* the queue is being flushed so ignore this event */
+ if (ioc->remove_host || fw_event->cancel_pending_work ||
+ ioc->pci_error_recovery) {
+ _scsih_fw_event_free(ioc, fw_event);
+ return;
+ }
+
+ switch (fw_event->event) {
+ case MPT3SAS_PROCESS_TRIGGER_DIAG:
+ mpt3sas_process_trigger_data(ioc, fw_event->event_data);
+ break;
+ case MPT3SAS_REMOVE_UNRESPONDING_DEVICES:
+ while (scsi_host_in_recovery(ioc->shost) || ioc->shost_recovery)
+ ssleep(1);
+ _scsih_remove_unresponding_sas_devices(ioc);
+ _scsih_scan_for_devices_after_reset(ioc);
+ break;
+ case MPT3SAS_PORT_ENABLE_COMPLETE:
+ ioc->start_scan = 0;
+ if (missing_delay[0] != -1 && missing_delay[1] != -1)
+ mpt3sas_base_update_missing_delay(ioc, missing_delay[0],
+ missing_delay[1]);
+ dewtprintk(ioc, pr_info(MPT3SAS_FMT
+ "port enable: complete from worker thread\n",
+ ioc->name));
+ break;
+ case MPT3SAS_TURN_ON_FAULT_LED:
+ _scsih_turn_on_fault_led(ioc, fw_event->device_handle);
+ break;
+ case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
+ _scsih_sas_topology_change_event(ioc, fw_event);
+ break;
+ case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE:
+ _scsih_sas_device_status_change_event(ioc, fw_event);
+ break;
+ case MPI2_EVENT_SAS_DISCOVERY:
+ _scsih_sas_discovery_event(ioc, fw_event);
+ break;
+ case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE:
+ _scsih_sas_broadcast_primitive_event(ioc, fw_event);
+ break;
+ case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
+ _scsih_sas_enclosure_dev_status_change_event(ioc,
+ fw_event);
+ break;
+ case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST:
+ _scsih_sas_ir_config_change_event(ioc, fw_event);
+ break;
+ case MPI2_EVENT_IR_VOLUME:
+ _scsih_sas_ir_volume_event(ioc, fw_event);
+ break;
+ case MPI2_EVENT_IR_PHYSICAL_DISK:
+ _scsih_sas_ir_physical_disk_event(ioc, fw_event);
+ break;
+ case MPI2_EVENT_IR_OPERATION_STATUS:
+ _scsih_sas_ir_operation_status_event(ioc, fw_event);
+ break;
+ }
+ _scsih_fw_event_free(ioc, fw_event);
+}
+
+/**
+ * _firmware_event_work
+ * @ioc: per adapter object
+ * @work: The fw_event_work object
+ * Context: user.
+ *
+ * wrappers for the work thread handling firmware events
+ *
+ * Return nothing.
+ */
+
+static void
+_firmware_event_work(struct work_struct *work)
+{
+ struct fw_event_work *fw_event = container_of(work,
+ struct fw_event_work, work);
+
+ _mpt3sas_fw_work(fw_event->ioc, fw_event);
+}
+
+/**
+ * mpt3sas_scsih_event_callback - firmware event handler (called at ISR time)
+ * @ioc: per adapter object
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ * Context: interrupt.
+ *
+ * This function merely adds a new work task into ioc->firmware_event_thread.
+ * The tasks are worked from _firmware_event_work in user context.
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+u8
+mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index,
+ u32 reply)
+{
+ struct fw_event_work *fw_event;
+ Mpi2EventNotificationReply_t *mpi_reply;
+ u16 event;
+ u16 sz;
+
+ /* events turned off due to host reset or driver unloading */
+ if (ioc->remove_host || ioc->pci_error_recovery)
+ return 1;
+
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+
+ if (unlikely(!mpi_reply)) {
+ pr_err(MPT3SAS_FMT "mpi_reply not valid at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return 1;
+ }
+
+ event = le16_to_cpu(mpi_reply->Event);
+
+ if (event != MPI2_EVENT_LOG_ENTRY_ADDED)
+ mpt3sas_trigger_event(ioc, event, 0);
+
+ switch (event) {
+ /* handle these */
+ case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE:
+ {
+ Mpi2EventDataSasBroadcastPrimitive_t *baen_data =
+ (Mpi2EventDataSasBroadcastPrimitive_t *)
+ mpi_reply->EventData;
+
+ if (baen_data->Primitive !=
+ MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT)
+ return 1;
+
+ if (ioc->broadcast_aen_busy) {
+ ioc->broadcast_aen_pending++;
+ return 1;
+ } else
+ ioc->broadcast_aen_busy = 1;
+ break;
+ }
+
+ case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
+ _scsih_check_topo_delete_events(ioc,
+ (Mpi2EventDataSasTopologyChangeList_t *)
+ mpi_reply->EventData);
+ break;
+ case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST:
+ _scsih_check_ir_config_unhide_events(ioc,
+ (Mpi2EventDataIrConfigChangeList_t *)
+ mpi_reply->EventData);
+ break;
+ case MPI2_EVENT_IR_VOLUME:
+ _scsih_check_volume_delete_events(ioc,
+ (Mpi2EventDataIrVolume_t *)
+ mpi_reply->EventData);
+ break;
+
+ case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE:
+ case MPI2_EVENT_IR_OPERATION_STATUS:
+ case MPI2_EVENT_SAS_DISCOVERY:
+ case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
+ case MPI2_EVENT_IR_PHYSICAL_DISK:
+ break;
+
+ default: /* ignore the rest */
+ return 1;
+ }
+
+ fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
+ if (!fw_event) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return 1;
+ }
+ sz = le16_to_cpu(mpi_reply->EventDataLength) * 4;
+ fw_event->event_data = kzalloc(sz, GFP_ATOMIC);
+ if (!fw_event->event_data) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ kfree(fw_event);
+ return 1;
+ }
+
+ memcpy(fw_event->event_data, mpi_reply->EventData, sz);
+ fw_event->ioc = ioc;
+ fw_event->VF_ID = mpi_reply->VF_ID;
+ fw_event->VP_ID = mpi_reply->VP_ID;
+ fw_event->event = event;
+ _scsih_fw_event_add(ioc, fw_event);
+ return 1;
+}
+
+/* shost template */
+static struct scsi_host_template scsih_driver_template = {
+ .module = THIS_MODULE,
+ .name = "Fusion MPT SAS Host",
+ .proc_name = MPT3SAS_DRIVER_NAME,
+ .queuecommand = _scsih_qcmd,
+ .target_alloc = _scsih_target_alloc,
+ .slave_alloc = _scsih_slave_alloc,
+ .slave_configure = _scsih_slave_configure,
+ .target_destroy = _scsih_target_destroy,
+ .slave_destroy = _scsih_slave_destroy,
+ .scan_finished = _scsih_scan_finished,
+ .scan_start = _scsih_scan_start,
+ .change_queue_depth = _scsih_change_queue_depth,
+ .change_queue_type = _scsih_change_queue_type,
+ .eh_abort_handler = _scsih_abort,
+ .eh_device_reset_handler = _scsih_dev_reset,
+ .eh_target_reset_handler = _scsih_target_reset,
+ .eh_host_reset_handler = _scsih_host_reset,
+ .bios_param = _scsih_bios_param,
+ .can_queue = 1,
+ .this_id = -1,
+ .sg_tablesize = MPT3SAS_SG_DEPTH,
+ .max_sectors = 32767,
+ .cmd_per_lun = 7,
+ .use_clustering = ENABLE_CLUSTERING,
+ .shost_attrs = mpt3sas_host_attrs,
+ .sdev_attrs = mpt3sas_dev_attrs,
+};
+
+/**
+ * _scsih_expander_node_remove - removing expander device from list.
+ * @ioc: per adapter object
+ * @sas_expander: the sas_device object
+ * Context: Calling function should acquire ioc->sas_node_lock.
+ *
+ * Removing object and freeing associated memory from the
+ * ioc->sas_expander_list.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_node *sas_expander)
+{
+ struct _sas_port *mpt3sas_port, *next;
+
+ /* remove sibling ports attached to this expander */
+ list_for_each_entry_safe(mpt3sas_port, next,
+ &sas_expander->sas_port_list, port_list) {
+ if (ioc->shost_recovery)
+ return;
+ if (mpt3sas_port->remote_identify.device_type ==
+ SAS_END_DEVICE)
+ mpt3sas_device_remove_by_sas_address(ioc,
+ mpt3sas_port->remote_identify.sas_address);
+ else if (mpt3sas_port->remote_identify.device_type ==
+ SAS_EDGE_EXPANDER_DEVICE ||
+ mpt3sas_port->remote_identify.device_type ==
+ SAS_FANOUT_EXPANDER_DEVICE)
+ mpt3sas_expander_remove(ioc,
+ mpt3sas_port->remote_identify.sas_address);
+ }
+
+ mpt3sas_transport_port_remove(ioc, sas_expander->sas_address,
+ sas_expander->sas_address_parent);
+
+ pr_info(MPT3SAS_FMT
+ "expander_remove: handle(0x%04x), sas_addr(0x%016llx)\n",
+ ioc->name,
+ sas_expander->handle, (unsigned long long)
+ sas_expander->sas_address);
+
+ kfree(sas_expander->phy);
+ kfree(sas_expander);
+}
+
+/**
+ * _scsih_ir_shutdown - IR shutdown notification
+ * @ioc: per adapter object
+ *
+ * Sending RAID Action to alert the Integrated RAID subsystem of the IOC that
+ * the host system is shutting down.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_ir_shutdown(struct MPT3SAS_ADAPTER *ioc)
+{
+ Mpi2RaidActionRequest_t *mpi_request;
+ Mpi2RaidActionReply_t *mpi_reply;
+ u16 smid;
+
+ /* is IR firmware build loaded ? */
+ if (!ioc->ir_firmware)
+ return;
+
+ /* are there any volumes ? */
+ if (list_empty(&ioc->raid_device_list))
+ return;
+
+ mutex_lock(&ioc->scsih_cmds.mutex);
+
+ if (ioc->scsih_cmds.status != MPT3_CMD_NOT_USED) {
+ pr_err(MPT3SAS_FMT "%s: scsih_cmd in use\n",
+ ioc->name, __func__);
+ goto out;
+ }
+ ioc->scsih_cmds.status = MPT3_CMD_PENDING;
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->scsih_cb_idx);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED;
+ goto out;
+ }
+
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->scsih_cmds.smid = smid;
+ memset(mpi_request, 0, sizeof(Mpi2RaidActionRequest_t));
+
+ mpi_request->Function = MPI2_FUNCTION_RAID_ACTION;
+ mpi_request->Action = MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED;
+
+ pr_info(MPT3SAS_FMT "IR shutdown (sending)\n", ioc->name);
+ init_completion(&ioc->scsih_cmds.done);
+ mpt3sas_base_put_smid_default(ioc, smid);
+ wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ);
+
+ if (!(ioc->scsih_cmds.status & MPT3_CMD_COMPLETE)) {
+ pr_err(MPT3SAS_FMT "%s: timeout\n",
+ ioc->name, __func__);
+ goto out;
+ }
+
+ if (ioc->scsih_cmds.status & MPT3_CMD_REPLY_VALID) {
+ mpi_reply = ioc->scsih_cmds.reply;
+ pr_info(MPT3SAS_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:
+ ioc->scsih_cmds.status = MPT3_CMD_NOT_USED;
+ mutex_unlock(&ioc->scsih_cmds.mutex);
+}
+
+/**
+ * _scsih_remove - detach and remove add host
+ * @pdev: PCI device struct
+ *
+ * Routine called when unloading the driver.
+ * Return nothing.
+ */
+static void __devexit
+_scsih_remove(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ struct _sas_port *mpt3sas_port, *next_port;
+ struct _raid_device *raid_device, *next;
+ struct MPT3SAS_TARGET *sas_target_priv_data;
+ struct workqueue_struct *wq;
+ unsigned long flags;
+
+ ioc->remove_host = 1;
+ _scsih_fw_event_cleanup_queue(ioc);
+
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ wq = ioc->firmware_event_thread;
+ ioc->firmware_event_thread = NULL;
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+ if (wq)
+ destroy_workqueue(wq);
+
+ /* release all the volumes */
+ _scsih_ir_shutdown(ioc);
+ list_for_each_entry_safe(raid_device, next, &ioc->raid_device_list,
+ list) {
+ if (raid_device->starget) {
+ sas_target_priv_data =
+ raid_device->starget->hostdata;
+ sas_target_priv_data->deleted = 1;
+ scsi_remove_target(&raid_device->starget->dev);
+ }
+ pr_info(MPT3SAS_FMT "removing handle(0x%04x), wwid(0x%016llx)\n",
+ ioc->name, raid_device->handle,
+ (unsigned long long) raid_device->wwid);
+ _scsih_raid_device_remove(ioc, raid_device);
+ }
+
+ /* free ports attached to the sas_host */
+ list_for_each_entry_safe(mpt3sas_port, next_port,
+ &ioc->sas_hba.sas_port_list, port_list) {
+ if (mpt3sas_port->remote_identify.device_type ==
+ SAS_END_DEVICE)
+ mpt3sas_device_remove_by_sas_address(ioc,
+ mpt3sas_port->remote_identify.sas_address);
+ else if (mpt3sas_port->remote_identify.device_type ==
+ SAS_EDGE_EXPANDER_DEVICE ||
+ mpt3sas_port->remote_identify.device_type ==
+ SAS_FANOUT_EXPANDER_DEVICE)
+ mpt3sas_expander_remove(ioc,
+ mpt3sas_port->remote_identify.sas_address);
+ }
+
+ /* free phys attached to the sas_host */
+ if (ioc->sas_hba.num_phys) {
+ kfree(ioc->sas_hba.phy);
+ ioc->sas_hba.phy = NULL;
+ ioc->sas_hba.num_phys = 0;
+ }
+
+ sas_remove_host(shost);
+ mpt3sas_base_detach(ioc);
+ list_del(&ioc->list);
+ scsi_remove_host(shost);
+ scsi_host_put(shost);
+}
+
+/**
+ * _scsih_shutdown - routine call during system shutdown
+ * @pdev: PCI device struct
+ *
+ * Return nothing.
+ */
+static void
+_scsih_shutdown(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ struct workqueue_struct *wq;
+ unsigned long flags;
+
+ ioc->remove_host = 1;
+ _scsih_fw_event_cleanup_queue(ioc);
+
+ spin_lock_irqsave(&ioc->fw_event_lock, flags);
+ wq = ioc->firmware_event_thread;
+ ioc->firmware_event_thread = NULL;
+ spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+ if (wq)
+ destroy_workqueue(wq);
+
+ _scsih_ir_shutdown(ioc);
+ mpt3sas_base_detach(ioc);
+}
+
+
+/**
+ * _scsih_probe_boot_devices - reports 1st device
+ * @ioc: per adapter object
+ *
+ * If specified in bios page 2, this routine reports the 1st
+ * device scsi-ml or sas transport for persistent boot device
+ * purposes. Please refer to function _scsih_determine_boot_device()
+ */
+static void
+_scsih_probe_boot_devices(struct MPT3SAS_ADAPTER *ioc)
+{
+ u8 is_raid;
+ void *device;
+ struct _sas_device *sas_device;
+ struct _raid_device *raid_device;
+ u16 handle;
+ u64 sas_address_parent;
+ u64 sas_address;
+ unsigned long flags;
+ int rc;
+
+ /* no Bios, return immediately */
+ if (!ioc->bios_pg3.BiosVersion)
+ return;
+
+ device = NULL;
+ is_raid = 0;
+ if (ioc->req_boot_device.device) {
+ device = ioc->req_boot_device.device;
+ is_raid = ioc->req_boot_device.is_raid;
+ } else if (ioc->req_alt_boot_device.device) {
+ device = ioc->req_alt_boot_device.device;
+ is_raid = ioc->req_alt_boot_device.is_raid;
+ } else if (ioc->current_boot_device.device) {
+ device = ioc->current_boot_device.device;
+ is_raid = ioc->current_boot_device.is_raid;
+ }
+
+ if (!device)
+ return;
+
+ if (is_raid) {
+ raid_device = device;
+ rc = scsi_add_device(ioc->shost, RAID_CHANNEL,
+ raid_device->id, 0);
+ if (rc)
+ _scsih_raid_device_remove(ioc, raid_device);
+ } else {
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = device;
+ handle = sas_device->handle;
+ sas_address_parent = sas_device->sas_address_parent;
+ sas_address = sas_device->sas_address;
+ list_move_tail(&sas_device->list, &ioc->sas_device_list);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ if (!mpt3sas_transport_port_add(ioc, handle,
+ sas_address_parent)) {
+ _scsih_sas_device_remove(ioc, sas_device);
+ } else if (!sas_device->starget) {
+ if (!ioc->is_driver_loading)
+ mpt3sas_transport_port_remove(ioc, sas_address,
+ sas_address_parent);
+ _scsih_sas_device_remove(ioc, sas_device);
+ }
+ }
+}
+
+/**
+ * _scsih_probe_raid - reporting raid volumes to scsi-ml
+ * @ioc: per adapter object
+ *
+ * Called during initial loading of the driver.
+ */
+static void
+_scsih_probe_raid(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct _raid_device *raid_device, *raid_next;
+ int rc;
+
+ list_for_each_entry_safe(raid_device, raid_next,
+ &ioc->raid_device_list, list) {
+ if (raid_device->starget)
+ continue;
+ rc = scsi_add_device(ioc->shost, RAID_CHANNEL,
+ raid_device->id, 0);
+ if (rc)
+ _scsih_raid_device_remove(ioc, raid_device);
+ }
+}
+
+/**
+ * _scsih_probe_sas - reporting sas devices to sas transport
+ * @ioc: per adapter object
+ *
+ * Called during initial loading of the driver.
+ */
+static void
+_scsih_probe_sas(struct MPT3SAS_ADAPTER *ioc)
+{
+ struct _sas_device *sas_device, *next;
+ unsigned long flags;
+
+ /* SAS Device List */
+ list_for_each_entry_safe(sas_device, next, &ioc->sas_device_init_list,
+ list) {
+
+ if (!mpt3sas_transport_port_add(ioc, sas_device->handle,
+ sas_device->sas_address_parent)) {
+ list_del(&sas_device->list);
+ kfree(sas_device);
+ continue;
+ } else if (!sas_device->starget) {
+ /*
+ * When asyn scanning is enabled, its not possible to
+ * remove devices while scanning is turned on due to an
+ * oops in scsi_sysfs_add_sdev()->add_device()->
+ * sysfs_addrm_start()
+ */
+ if (!ioc->is_driver_loading)
+ mpt3sas_transport_port_remove(ioc,
+ sas_device->sas_address,
+ sas_device->sas_address_parent);
+ list_del(&sas_device->list);
+ kfree(sas_device);
+ continue;
+ }
+
+ 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);
+ }
+}
+
+/**
+ * _scsih_probe_devices - probing for devices
+ * @ioc: per adapter object
+ *
+ * Called during initial loading of the driver.
+ */
+static void
+_scsih_probe_devices(struct MPT3SAS_ADAPTER *ioc)
+{
+ u16 volume_mapping_flags;
+
+ if (!(ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR))
+ return; /* return when IOC doesn't support initiator mode */
+
+ _scsih_probe_boot_devices(ioc);
+
+ if (ioc->ir_firmware) {
+ volume_mapping_flags =
+ le16_to_cpu(ioc->ioc_pg8.IRVolumeMappingFlags) &
+ MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE;
+ if (volume_mapping_flags ==
+ MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING) {
+ _scsih_probe_raid(ioc);
+ _scsih_probe_sas(ioc);
+ } else {
+ _scsih_probe_sas(ioc);
+ _scsih_probe_raid(ioc);
+ }
+ } else
+ _scsih_probe_sas(ioc);
+}
+
+/**
+ * _scsih_scan_start - scsi lld callback for .scan_start
+ * @shost: SCSI host pointer
+ *
+ * The shost has the ability to discover targets on its own instead
+ * of scanning the entire bus. In our implemention, we will kick off
+ * firmware discovery.
+ */
+static void
+_scsih_scan_start(struct Scsi_Host *shost)
+{
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ int rc;
+ if (diag_buffer_enable != -1 && diag_buffer_enable != 0)
+ mpt3sas_enable_diag_buffer(ioc, diag_buffer_enable);
+
+ if (disable_discovery > 0)
+ return;
+
+ ioc->start_scan = 1;
+ rc = mpt3sas_port_enable(ioc);
+
+ if (rc != 0)
+ pr_info(MPT3SAS_FMT "port enable: FAILED\n", ioc->name);
+}
+
+/**
+ * _scsih_scan_finished - scsi lld callback for .scan_finished
+ * @shost: SCSI host pointer
+ * @time: elapsed time of the scan in jiffies
+ *
+ * This function will be called periodicallyn until it returns 1 with the
+ * scsi_host and the elapsed time of the scan in jiffies. In our implemention,
+ * we wait for firmware discovery to complete, then return 1.
+ */
+static int
+_scsih_scan_finished(struct Scsi_Host *shost, unsigned long time)
+{
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ if (disable_discovery > 0) {
+ ioc->is_driver_loading = 0;
+ ioc->wait_for_discovery_to_complete = 0;
+ return 1;
+ }
+
+ if (time >= (300 * HZ)) {
+ ioc->base_cmds.status = MPT3_CMD_NOT_USED;
+ pr_info(MPT3SAS_FMT
+ "port enable: FAILED with timeout (timeout=300s)\n",
+ ioc->name);
+ ioc->is_driver_loading = 0;
+ return 1;
+ }
+
+ if (ioc->start_scan)
+ return 0;
+
+ if (ioc->start_scan_failed) {
+ pr_info(MPT3SAS_FMT
+ "port enable: FAILED with (ioc_status=0x%08x)\n",
+ ioc->name, ioc->start_scan_failed);
+ ioc->is_driver_loading = 0;
+ ioc->wait_for_discovery_to_complete = 0;
+ ioc->remove_host = 1;
+ return 1;
+ }
+
+ pr_info(MPT3SAS_FMT "port enable: SUCCESS\n", ioc->name);
+ ioc->base_cmds.status = MPT3_CMD_NOT_USED;
+
+ if (ioc->wait_for_discovery_to_complete) {
+ ioc->wait_for_discovery_to_complete = 0;
+ _scsih_probe_devices(ioc);
+ }
+ mpt3sas_base_start_watchdog(ioc);
+ ioc->is_driver_loading = 0;
+ return 1;
+}
+
+/**
+ * _scsih_probe - attach and add scsi host
+ * @pdev: PCI device struct
+ * @id: pci device id
+ *
+ * Returns 0 success, anything else error.
+ */
+static int
+_scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct MPT3SAS_ADAPTER *ioc;
+ struct Scsi_Host *shost;
+
+ shost = scsi_host_alloc(&scsih_driver_template,
+ sizeof(struct MPT3SAS_ADAPTER));
+ if (!shost)
+ return -ENODEV;
+
+ /* init local params */
+ ioc = shost_priv(shost);
+ memset(ioc, 0, sizeof(struct MPT3SAS_ADAPTER));
+ INIT_LIST_HEAD(&ioc->list);
+ list_add_tail(&ioc->list, &mpt3sas_ioc_list);
+ ioc->shost = shost;
+ ioc->id = mpt_ids++;
+ sprintf(ioc->name, "%s%d", MPT3SAS_DRIVER_NAME, ioc->id);
+ ioc->pdev = pdev;
+ ioc->scsi_io_cb_idx = scsi_io_cb_idx;
+ ioc->tm_cb_idx = tm_cb_idx;
+ ioc->ctl_cb_idx = ctl_cb_idx;
+ ioc->base_cb_idx = base_cb_idx;
+ ioc->port_enable_cb_idx = port_enable_cb_idx;
+ ioc->transport_cb_idx = transport_cb_idx;
+ ioc->scsih_cb_idx = scsih_cb_idx;
+ ioc->config_cb_idx = config_cb_idx;
+ ioc->tm_tr_cb_idx = tm_tr_cb_idx;
+ ioc->tm_tr_volume_cb_idx = tm_tr_volume_cb_idx;
+ ioc->tm_sas_control_cb_idx = tm_sas_control_cb_idx;
+ ioc->logging_level = logging_level;
+ ioc->schedule_dead_ioc_flush_running_cmds = &_scsih_flush_running_cmds;
+ /* misc semaphores and spin locks */
+ mutex_init(&ioc->reset_in_progress_mutex);
+ spin_lock_init(&ioc->ioc_reset_in_progress_lock);
+ spin_lock_init(&ioc->scsi_lookup_lock);
+ spin_lock_init(&ioc->sas_device_lock);
+ spin_lock_init(&ioc->sas_node_lock);
+ spin_lock_init(&ioc->fw_event_lock);
+ spin_lock_init(&ioc->raid_device_lock);
+ spin_lock_init(&ioc->diag_trigger_lock);
+
+ INIT_LIST_HEAD(&ioc->sas_device_list);
+ INIT_LIST_HEAD(&ioc->sas_device_init_list);
+ INIT_LIST_HEAD(&ioc->sas_expander_list);
+ INIT_LIST_HEAD(&ioc->fw_event_list);
+ INIT_LIST_HEAD(&ioc->raid_device_list);
+ INIT_LIST_HEAD(&ioc->sas_hba.sas_port_list);
+ INIT_LIST_HEAD(&ioc->delayed_tr_list);
+ INIT_LIST_HEAD(&ioc->delayed_tr_volume_list);
+
+ /* init shost parameters */
+ shost->max_cmd_len = 32;
+ shost->max_lun = max_lun;
+ shost->transportt = mpt3sas_transport_template;
+ shost->unique_id = ioc->id;
+
+ if (max_sectors != 0xFFFF) {
+ if (max_sectors < 64) {
+ shost->max_sectors = 64;
+ pr_warn(MPT3SAS_FMT "Invalid value %d passed " \
+ "for max_sectors, range is 64 to 32767. Assigning "
+ "value of 64.\n", ioc->name, max_sectors);
+ } else if (max_sectors > 32767) {
+ shost->max_sectors = 32767;
+ pr_warn(MPT3SAS_FMT "Invalid value %d passed " \
+ "for max_sectors, range is 64 to 32767. Assigning "
+ "default value of 32767.\n", ioc->name,
+ max_sectors);
+ } else {
+ shost->max_sectors = max_sectors & 0xFFFE;
+ pr_info(MPT3SAS_FMT
+ "The max_sectors value is set to %d\n",
+ ioc->name, shost->max_sectors);
+ }
+ }
+
+ if ((scsi_add_host(shost, &pdev->dev))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ list_del(&ioc->list);
+ goto out_add_shost_fail;
+ }
+
+ /* register EEDP capabilities with SCSI layer */
+ if (prot_mask > 0)
+ scsi_host_set_prot(shost, prot_mask);
+ else
+ scsi_host_set_prot(shost, SHOST_DIF_TYPE1_PROTECTION
+ | SHOST_DIF_TYPE2_PROTECTION
+ | SHOST_DIF_TYPE3_PROTECTION);
+
+ scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC);
+
+ /* event thread */
+ snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name),
+ "fw_event%d", ioc->id);
+ ioc->firmware_event_thread = create_singlethread_workqueue(
+ ioc->firmware_event_name);
+ if (!ioc->firmware_event_thread) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out_thread_fail;
+ }
+
+ ioc->is_driver_loading = 1;
+ if ((mpt3sas_base_attach(ioc))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out_attach_fail;
+ }
+ scsi_scan_host(shost);
+ return 0;
+
+ out_attach_fail:
+ destroy_workqueue(ioc->firmware_event_thread);
+ out_thread_fail:
+ list_del(&ioc->list);
+ scsi_remove_host(shost);
+ out_add_shost_fail:
+ scsi_host_put(shost);
+ return -ENODEV;
+}
+
+#ifdef CONFIG_PM
+/**
+ * _scsih_suspend - power management suspend main entry point
+ * @pdev: PCI device struct
+ * @state: PM state change to (usually PCI_D3)
+ *
+ * Returns 0 success, anything else error.
+ */
+static int
+_scsih_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ pci_power_t device_state;
+
+ mpt3sas_base_stop_watchdog(ioc);
+ flush_scheduled_work();
+ scsi_block_requests(shost);
+ device_state = pci_choose_state(pdev, state);
+ pr_info(MPT3SAS_FMT
+ "pdev=0x%p, slot=%s, entering operating state [D%d]\n",
+ ioc->name, pdev, pci_name(pdev), device_state);
+
+ pci_save_state(pdev);
+ mpt3sas_base_free_resources(ioc);
+ pci_set_power_state(pdev, device_state);
+ return 0;
+}
+
+/**
+ * _scsih_resume - power management resume main entry point
+ * @pdev: PCI device struct
+ *
+ * Returns 0 success, anything else error.
+ */
+static int
+_scsih_resume(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ pci_power_t device_state = pdev->current_state;
+ int r;
+
+ pr_info(MPT3SAS_FMT
+ "pdev=0x%p, slot=%s, previous operating state [D%d]\n",
+ ioc->name, pdev, pci_name(pdev), device_state);
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_enable_wake(pdev, PCI_D0, 0);
+ pci_restore_state(pdev);
+ ioc->pdev = pdev;
+ r = mpt3sas_base_map_resources(ioc);
+ if (r)
+ return r;
+
+ mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, SOFT_RESET);
+ scsi_unblock_requests(shost);
+ mpt3sas_base_start_watchdog(ioc);
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+/**
+ * _scsih_pci_error_detected - Called when a PCI error is detected.
+ * @pdev: PCI device struct
+ * @state: PCI channel state
+ *
+ * Description: Called when a PCI error is detected.
+ *
+ * Return value:
+ * PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT
+ */
+static pci_ers_result_t
+_scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ pr_info(MPT3SAS_FMT "PCI error: detected callback, state(%d)!!\n",
+ ioc->name, state);
+
+ switch (state) {
+ case pci_channel_io_normal:
+ return PCI_ERS_RESULT_CAN_RECOVER;
+ case pci_channel_io_frozen:
+ /* Fatal error, prepare for slot reset */
+ ioc->pci_error_recovery = 1;
+ scsi_block_requests(ioc->shost);
+ mpt3sas_base_stop_watchdog(ioc);
+ mpt3sas_base_free_resources(ioc);
+ return PCI_ERS_RESULT_NEED_RESET;
+ case pci_channel_io_perm_failure:
+ /* Permanent error, prepare for device removal */
+ ioc->pci_error_recovery = 1;
+ mpt3sas_base_stop_watchdog(ioc);
+ _scsih_flush_running_cmds(ioc);
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+/**
+ * _scsih_pci_slot_reset - Called when PCI slot has been reset.
+ * @pdev: PCI device struct
+ *
+ * Description: This routine is called by the pci error recovery
+ * code after the PCI slot has been reset, just before we
+ * should resume normal operations.
+ */
+static pci_ers_result_t
+_scsih_pci_slot_reset(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ int rc;
+
+ pr_info(MPT3SAS_FMT "PCI error: slot reset callback!!\n",
+ ioc->name);
+
+ ioc->pci_error_recovery = 0;
+ ioc->pdev = pdev;
+ pci_restore_state(pdev);
+ rc = mpt3sas_base_map_resources(ioc);
+ if (rc)
+ return PCI_ERS_RESULT_DISCONNECT;
+
+ rc = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+
+ pr_warn(MPT3SAS_FMT "hard reset: %s\n", ioc->name,
+ (rc == 0) ? "success" : "failed");
+
+ if (!rc)
+ return PCI_ERS_RESULT_RECOVERED;
+ else
+ return PCI_ERS_RESULT_DISCONNECT;
+}
+
+/**
+ * _scsih_pci_resume() - resume normal ops after PCI reset
+ * @pdev: pointer to PCI device
+ *
+ * Called when the error recovery driver tells us that its
+ * OK to resume normal operation. Use completion to allow
+ * halted scsi ops to resume.
+ */
+static void
+_scsih_pci_resume(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ pr_info(MPT3SAS_FMT "PCI error: resume callback!!\n", ioc->name);
+
+ pci_cleanup_aer_uncorrect_error_status(pdev);
+ mpt3sas_base_start_watchdog(ioc);
+ scsi_unblock_requests(ioc->shost);
+}
+
+/**
+ * _scsih_pci_mmio_enabled - Enable MMIO and dump debug registers
+ * @pdev: pointer to PCI device
+ */
+static pci_ers_result_t
+_scsih_pci_mmio_enabled(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+
+ pr_info(MPT3SAS_FMT "PCI error: mmio enabled callback!!\n",
+ ioc->name);
+
+ /* TODO - dump whatever for debugging purposes */
+
+ /* Request a slot reset. */
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+/* raid transport support */
+static struct raid_function_template mpt3sas_raid_functions = {
+ .cookie = &scsih_driver_template,
+ .is_raid = _scsih_is_raid,
+ .get_resync = _scsih_get_resync,
+ .get_state = _scsih_get_state,
+};
+
+static struct pci_error_handlers _scsih_err_handler = {
+ .error_detected = _scsih_pci_error_detected,
+ .mmio_enabled = _scsih_pci_mmio_enabled,
+ .slot_reset = _scsih_pci_slot_reset,
+ .resume = _scsih_pci_resume,
+};
+
+static struct pci_driver scsih_driver = {
+ .name = MPT3SAS_DRIVER_NAME,
+ .id_table = scsih_pci_table,
+ .probe = _scsih_probe,
+ .remove = __devexit_p(_scsih_remove),
+ .shutdown = _scsih_shutdown,
+ .err_handler = &_scsih_err_handler,
+#ifdef CONFIG_PM
+ .suspend = _scsih_suspend,
+ .resume = _scsih_resume,
+#endif
+};
+
+
+/**
+ * _scsih_init - main entry point for this driver.
+ *
+ * Returns 0 success, anything else error.
+ */
+static int __init
+_scsih_init(void)
+{
+ int error;
+
+ mpt_ids = 0;
+
+ pr_info("%s version %s loaded\n", MPT3SAS_DRIVER_NAME,
+ MPT3SAS_DRIVER_VERSION);
+
+ mpt3sas_transport_template =
+ sas_attach_transport(&mpt3sas_transport_functions);
+ if (!mpt3sas_transport_template)
+ return -ENODEV;
+
+/* raid transport support */
+ mpt3sas_raid_template = raid_class_attach(&mpt3sas_raid_functions);
+ if (!mpt3sas_raid_template) {
+ sas_release_transport(mpt3sas_transport_template);
+ return -ENODEV;
+ }
+
+ mpt3sas_base_initialize_callback_handler();
+
+ /* queuecommand callback hander */
+ scsi_io_cb_idx = mpt3sas_base_register_callback_handler(_scsih_io_done);
+
+ /* task managment callback handler */
+ tm_cb_idx = mpt3sas_base_register_callback_handler(_scsih_tm_done);
+
+ /* base internal commands callback handler */
+ base_cb_idx = mpt3sas_base_register_callback_handler(mpt3sas_base_done);
+ port_enable_cb_idx = mpt3sas_base_register_callback_handler(
+ mpt3sas_port_enable_done);
+
+ /* transport internal commands callback handler */
+ transport_cb_idx = mpt3sas_base_register_callback_handler(
+ mpt3sas_transport_done);
+
+ /* scsih internal commands callback handler */
+ scsih_cb_idx = mpt3sas_base_register_callback_handler(_scsih_done);
+
+ /* configuration page API internal commands callback handler */
+ config_cb_idx = mpt3sas_base_register_callback_handler(
+ mpt3sas_config_done);
+
+ /* ctl module callback handler */
+ ctl_cb_idx = mpt3sas_base_register_callback_handler(mpt3sas_ctl_done);
+
+ tm_tr_cb_idx = mpt3sas_base_register_callback_handler(
+ _scsih_tm_tr_complete);
+
+ tm_tr_volume_cb_idx = mpt3sas_base_register_callback_handler(
+ _scsih_tm_volume_tr_complete);
+
+ tm_sas_control_cb_idx = mpt3sas_base_register_callback_handler(
+ _scsih_sas_control_complete);
+
+ mpt3sas_ctl_init();
+
+ error = pci_register_driver(&scsih_driver);
+ if (error) {
+ /* raid transport support */
+ raid_class_release(mpt3sas_raid_template);
+ sas_release_transport(mpt3sas_transport_template);
+ }
+
+ return error;
+}
+
+/**
+ * _scsih_exit - exit point for this driver (when it is a module).
+ *
+ * Returns 0 success, anything else error.
+ */
+static void __exit
+_scsih_exit(void)
+{
+ pr_info("mpt3sas version %s unloading\n",
+ MPT3SAS_DRIVER_VERSION);
+
+ mpt3sas_ctl_exit();
+
+ pci_unregister_driver(&scsih_driver);
+
+
+ mpt3sas_base_release_callback_handler(scsi_io_cb_idx);
+ mpt3sas_base_release_callback_handler(tm_cb_idx);
+ mpt3sas_base_release_callback_handler(base_cb_idx);
+ mpt3sas_base_release_callback_handler(port_enable_cb_idx);
+ mpt3sas_base_release_callback_handler(transport_cb_idx);
+ mpt3sas_base_release_callback_handler(scsih_cb_idx);
+ mpt3sas_base_release_callback_handler(config_cb_idx);
+ mpt3sas_base_release_callback_handler(ctl_cb_idx);
+
+ mpt3sas_base_release_callback_handler(tm_tr_cb_idx);
+ mpt3sas_base_release_callback_handler(tm_tr_volume_cb_idx);
+ mpt3sas_base_release_callback_handler(tm_sas_control_cb_idx);
+
+/* raid transport support */
+ raid_class_release(mpt3sas_raid_template);
+ sas_release_transport(mpt3sas_transport_template);
+}
+
+module_init(_scsih_init);
+module_exit(_scsih_exit);
diff --git a/drivers/scsi/mpt3sas/mpt3sas_transport.c b/drivers/scsi/mpt3sas/mpt3sas_transport.c
new file mode 100644
index 00000000000..87ca2b7287c
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_transport.c
@@ -0,0 +1,2128 @@
+/*
+ * SAS Transport Layer for MPT (Message Passing Technology) based controllers
+ *
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_transport.c
+ * Copyright (C) 2012 LSI Corporation
+ * (mailto:DL-MPTFusionLinux@lsi.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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_transport_sas.h>
+#include <scsi/scsi_dbg.h>
+
+#include "mpt3sas_base.h"
+
+/**
+ * _transport_sas_node_find_by_sas_address - sas node search
+ * @ioc: per adapter object
+ * @sas_address: sas address of expander or sas host
+ * Context: Calling function should acquire ioc->sas_node_lock.
+ *
+ * Search for either hba phys or expander device based on handle, then returns
+ * the sas_node object.
+ */
+static struct _sas_node *
+_transport_sas_node_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc,
+ u64 sas_address)
+{
+ if (ioc->sas_hba.sas_address == sas_address)
+ return &ioc->sas_hba;
+ else
+ return mpt3sas_scsih_expander_find_by_sas_address(ioc,
+ sas_address);
+}
+
+/**
+ * _transport_convert_phy_link_rate -
+ * @link_rate: link rate returned from mpt firmware
+ *
+ * Convert link_rate from mpi fusion into sas_transport form.
+ */
+static enum sas_linkrate
+_transport_convert_phy_link_rate(u8 link_rate)
+{
+ enum sas_linkrate rc;
+
+ switch (link_rate) {
+ case MPI2_SAS_NEG_LINK_RATE_1_5:
+ rc = SAS_LINK_RATE_1_5_GBPS;
+ break;
+ case MPI2_SAS_NEG_LINK_RATE_3_0:
+ rc = SAS_LINK_RATE_3_0_GBPS;
+ break;
+ case MPI2_SAS_NEG_LINK_RATE_6_0:
+ rc = SAS_LINK_RATE_6_0_GBPS;
+ break;
+ case MPI25_SAS_NEG_LINK_RATE_12_0:
+ rc = SAS_LINK_RATE_12_0_GBPS;
+ break;
+ case MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED:
+ rc = SAS_PHY_DISABLED;
+ break;
+ case MPI2_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED:
+ rc = SAS_LINK_RATE_FAILED;
+ break;
+ case MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR:
+ rc = SAS_SATA_PORT_SELECTOR;
+ break;
+ case MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS:
+ rc = SAS_PHY_RESET_IN_PROGRESS;
+ break;
+
+ default:
+ case MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE:
+ case MPI2_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE:
+ rc = SAS_LINK_RATE_UNKNOWN;
+ break;
+ }
+ return rc;
+}
+
+/**
+ * _transport_set_identify - set identify for phys and end devices
+ * @ioc: per adapter object
+ * @handle: device handle
+ * @identify: sas identify info
+ *
+ * Populates sas identify info.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_transport_set_identify(struct MPT3SAS_ADAPTER *ioc, u16 handle,
+ struct sas_identify *identify)
+{
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u32 device_info;
+ u32 ioc_status;
+
+ if (ioc->shost_recovery || ioc->pci_error_recovery) {
+ pr_info(MPT3SAS_FMT "%s: host reset in progress!\n",
+ __func__, ioc->name);
+ return -EFAULT;
+ }
+
+ if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -ENXIO;
+ }
+
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_err(MPT3SAS_FMT
+ "handle(0x%04x), ioc_status(0x%04x)\nfailure at %s:%d/%s()!\n",
+ ioc->name, handle, ioc_status,
+ __FILE__, __LINE__, __func__);
+ return -EIO;
+ }
+
+ memset(identify, 0, sizeof(struct sas_identify));
+ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
+
+ /* sas_address */
+ identify->sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
+
+ /* phy number of the parent device this device is linked to */
+ identify->phy_identifier = sas_device_pg0.PhyNum;
+
+ /* device_type */
+ switch (device_info & MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) {
+ case MPI2_SAS_DEVICE_INFO_NO_DEVICE:
+ identify->device_type = SAS_PHY_UNUSED;
+ break;
+ case MPI2_SAS_DEVICE_INFO_END_DEVICE:
+ identify->device_type = SAS_END_DEVICE;
+ break;
+ case MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER:
+ identify->device_type = SAS_EDGE_EXPANDER_DEVICE;
+ break;
+ case MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER:
+ identify->device_type = SAS_FANOUT_EXPANDER_DEVICE;
+ break;
+ }
+
+ /* initiator_port_protocols */
+ if (device_info & MPI2_SAS_DEVICE_INFO_SSP_INITIATOR)
+ identify->initiator_port_protocols |= SAS_PROTOCOL_SSP;
+ if (device_info & MPI2_SAS_DEVICE_INFO_STP_INITIATOR)
+ identify->initiator_port_protocols |= SAS_PROTOCOL_STP;
+ if (device_info & MPI2_SAS_DEVICE_INFO_SMP_INITIATOR)
+ identify->initiator_port_protocols |= SAS_PROTOCOL_SMP;
+ if (device_info & MPI2_SAS_DEVICE_INFO_SATA_HOST)
+ identify->initiator_port_protocols |= SAS_PROTOCOL_SATA;
+
+ /* target_port_protocols */
+ if (device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET)
+ identify->target_port_protocols |= SAS_PROTOCOL_SSP;
+ if (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET)
+ identify->target_port_protocols |= SAS_PROTOCOL_STP;
+ if (device_info & MPI2_SAS_DEVICE_INFO_SMP_TARGET)
+ identify->target_port_protocols |= SAS_PROTOCOL_SMP;
+ if (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
+ identify->target_port_protocols |= SAS_PROTOCOL_SATA;
+
+ return 0;
+}
+
+/**
+ * mpt3sas_transport_done - internal transport layer callback handler.
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Callback handler when sending internal generated transport cmds.
+ * The callback index passed is `ioc->transport_cb_idx`
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ * 0 means the mf is freed from this function.
+ */
+u8
+mpt3sas_transport_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
+ u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+
+ mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);
+ if (ioc->transport_cmds.status == MPT3_CMD_NOT_USED)
+ return 1;
+ if (ioc->transport_cmds.smid != smid)
+ return 1;
+ ioc->transport_cmds.status |= MPT3_CMD_COMPLETE;
+ if (mpi_reply) {
+ memcpy(ioc->transport_cmds.reply, mpi_reply,
+ mpi_reply->MsgLength*4);
+ ioc->transport_cmds.status |= MPT3_CMD_REPLY_VALID;
+ }
+ ioc->transport_cmds.status &= ~MPT3_CMD_PENDING;
+ complete(&ioc->transport_cmds.done);
+ return 1;
+}
+
+/* report manufacture request structure */
+struct rep_manu_request {
+ u8 smp_frame_type;
+ u8 function;
+ u8 reserved;
+ u8 request_length;
+};
+
+/* report manufacture reply structure */
+struct rep_manu_reply {
+ u8 smp_frame_type; /* 0x41 */
+ u8 function; /* 0x01 */
+ u8 function_result;
+ u8 response_length;
+ u16 expander_change_count;
+ u8 reserved0[2];
+ u8 sas_format;
+ u8 reserved2[3];
+ u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN];
+ u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN];
+ u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN];
+ u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN];
+ u16 component_id;
+ u8 component_revision_id;
+ u8 reserved3;
+ u8 vendor_specific[8];
+};
+
+/**
+ * transport_expander_report_manufacture - obtain SMP report_manufacture
+ * @ioc: per adapter object
+ * @sas_address: expander sas address
+ * @edev: the sas_expander_device object
+ *
+ * Fills in the sas_expander_device object when SMP port is created.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_transport_expander_report_manufacture(struct MPT3SAS_ADAPTER *ioc,
+ u64 sas_address, struct sas_expander_device *edev)
+{
+ Mpi2SmpPassthroughRequest_t *mpi_request;
+ Mpi2SmpPassthroughReply_t *mpi_reply;
+ struct rep_manu_reply *manufacture_reply;
+ struct rep_manu_request *manufacture_request;
+ int rc;
+ u16 smid;
+ u32 ioc_state;
+ unsigned long timeleft;
+ void *psge;
+ u8 issue_reset = 0;
+ void *data_out = NULL;
+ dma_addr_t data_out_dma;
+ dma_addr_t data_in_dma;
+ size_t data_in_sz;
+ size_t data_out_sz;
+ u16 wait_state_count;
+
+ if (ioc->shost_recovery || ioc->pci_error_recovery) {
+ pr_info(MPT3SAS_FMT "%s: host reset in progress!\n",
+ __func__, ioc->name);
+ return -EFAULT;
+ }
+
+ mutex_lock(&ioc->transport_cmds.mutex);
+
+ if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) {
+ pr_err(MPT3SAS_FMT "%s: transport_cmds in use\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+ ioc->transport_cmds.status = MPT3_CMD_PENDING;
+
+ wait_state_count = 0;
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ if (wait_state_count++ == 10) {
+ pr_err(MPT3SAS_FMT
+ "%s: failed due to ioc not operational\n",
+ ioc->name, __func__);
+ rc = -EFAULT;
+ goto out;
+ }
+ ssleep(1);
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ pr_info(MPT3SAS_FMT
+ "%s: waiting for operational state(count=%d)\n",
+ ioc->name, __func__, wait_state_count);
+ }
+ if (wait_state_count)
+ pr_info(MPT3SAS_FMT "%s: ioc is operational\n",
+ ioc->name, __func__);
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ rc = 0;
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->transport_cmds.smid = smid;
+
+ data_out_sz = sizeof(struct rep_manu_request);
+ data_in_sz = sizeof(struct rep_manu_reply);
+ data_out = pci_alloc_consistent(ioc->pdev, data_out_sz + data_in_sz,
+ &data_out_dma);
+
+ if (!data_out) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ rc = -ENOMEM;
+ mpt3sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+
+ data_in_dma = data_out_dma + sizeof(struct rep_manu_request);
+
+ manufacture_request = data_out;
+ manufacture_request->smp_frame_type = 0x40;
+ manufacture_request->function = 1;
+ manufacture_request->reserved = 0;
+ manufacture_request->request_length = 0;
+
+ memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
+ mpi_request->PhysicalPort = 0xFF;
+ mpi_request->SASAddress = cpu_to_le64(sas_address);
+ mpi_request->RequestDataLength = cpu_to_le16(data_out_sz);
+ psge = &mpi_request->SGL;
+
+ ioc->build_sg(ioc, psge, data_out_dma, data_out_sz, data_in_dma,
+ data_in_sz);
+
+ dtransportprintk(ioc, pr_info(MPT3SAS_FMT
+ "report_manufacture - send to sas_addr(0x%016llx)\n",
+ ioc->name, (unsigned long long)sas_address));
+ init_completion(&ioc->transport_cmds.done);
+ mpt3sas_base_put_smid_default(ioc, smid);
+ timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done,
+ 10*HZ);
+
+ if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
+ pr_err(MPT3SAS_FMT "%s: timeout\n",
+ ioc->name, __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2SmpPassthroughRequest_t)/4);
+ if (!(ioc->transport_cmds.status & MPT3_CMD_RESET))
+ issue_reset = 1;
+ goto issue_host_reset;
+ }
+
+ dtransportprintk(ioc, pr_info(MPT3SAS_FMT
+ "report_manufacture - complete\n", ioc->name));
+
+ if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) {
+ u8 *tmp;
+
+ mpi_reply = ioc->transport_cmds.reply;
+
+ dtransportprintk(ioc, pr_info(MPT3SAS_FMT
+ "report_manufacture - reply data transfer size(%d)\n",
+ ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength)));
+
+ if (le16_to_cpu(mpi_reply->ResponseDataLength) !=
+ sizeof(struct rep_manu_reply))
+ goto out;
+
+ manufacture_reply = data_out + sizeof(struct rep_manu_request);
+ strncpy(edev->vendor_id, manufacture_reply->vendor_id,
+ SAS_EXPANDER_VENDOR_ID_LEN);
+ strncpy(edev->product_id, manufacture_reply->product_id,
+ SAS_EXPANDER_PRODUCT_ID_LEN);
+ strncpy(edev->product_rev, manufacture_reply->product_rev,
+ SAS_EXPANDER_PRODUCT_REV_LEN);
+ edev->level = manufacture_reply->sas_format & 1;
+ if (edev->level) {
+ strncpy(edev->component_vendor_id,
+ manufacture_reply->component_vendor_id,
+ SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN);
+ tmp = (u8 *)&manufacture_reply->component_id;
+ edev->component_id = tmp[0] << 8 | tmp[1];
+ edev->component_revision_id =
+ manufacture_reply->component_revision_id;
+ }
+ } else
+ dtransportprintk(ioc, pr_info(MPT3SAS_FMT
+ "report_manufacture - no reply\n", ioc->name));
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ out:
+ ioc->transport_cmds.status = MPT3_CMD_NOT_USED;
+ if (data_out)
+ pci_free_consistent(ioc->pdev, data_out_sz + data_in_sz,
+ data_out, data_out_dma);
+
+ mutex_unlock(&ioc->transport_cmds.mutex);
+ return rc;
+}
+
+
+/**
+ * _transport_delete_port - helper function to removing a port
+ * @ioc: per adapter object
+ * @mpt3sas_port: mpt3sas per port object
+ *
+ * Returns nothing.
+ */
+static void
+_transport_delete_port(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_port *mpt3sas_port)
+{
+ u64 sas_address = mpt3sas_port->remote_identify.sas_address;
+ enum sas_device_type device_type =
+ mpt3sas_port->remote_identify.device_type;
+
+ dev_printk(KERN_INFO, &mpt3sas_port->port->dev,
+ "remove: sas_addr(0x%016llx)\n",
+ (unsigned long long) sas_address);
+
+ ioc->logging_level |= MPT_DEBUG_TRANSPORT;
+ if (device_type == SAS_END_DEVICE)
+ mpt3sas_device_remove_by_sas_address(ioc, sas_address);
+ else if (device_type == SAS_EDGE_EXPANDER_DEVICE ||
+ device_type == SAS_FANOUT_EXPANDER_DEVICE)
+ mpt3sas_expander_remove(ioc, sas_address);
+ ioc->logging_level &= ~MPT_DEBUG_TRANSPORT;
+}
+
+/**
+ * _transport_delete_phy - helper function to removing single phy from port
+ * @ioc: per adapter object
+ * @mpt3sas_port: mpt3sas per port object
+ * @mpt3sas_phy: mpt3sas per phy object
+ *
+ * Returns nothing.
+ */
+static void
+_transport_delete_phy(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_port *mpt3sas_port, struct _sas_phy *mpt3sas_phy)
+{
+ u64 sas_address = mpt3sas_port->remote_identify.sas_address;
+
+ dev_printk(KERN_INFO, &mpt3sas_phy->phy->dev,
+ "remove: sas_addr(0x%016llx), phy(%d)\n",
+ (unsigned long long) sas_address, mpt3sas_phy->phy_id);
+
+ list_del(&mpt3sas_phy->port_siblings);
+ mpt3sas_port->num_phys--;
+ sas_port_delete_phy(mpt3sas_port->port, mpt3sas_phy->phy);
+ mpt3sas_phy->phy_belongs_to_port = 0;
+}
+
+/**
+ * _transport_add_phy - helper function to adding single phy to port
+ * @ioc: per adapter object
+ * @mpt3sas_port: mpt3sas per port object
+ * @mpt3sas_phy: mpt3sas per phy object
+ *
+ * Returns nothing.
+ */
+static void
+_transport_add_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_port *mpt3sas_port,
+ struct _sas_phy *mpt3sas_phy)
+{
+ u64 sas_address = mpt3sas_port->remote_identify.sas_address;
+
+ dev_printk(KERN_INFO, &mpt3sas_phy->phy->dev,
+ "add: sas_addr(0x%016llx), phy(%d)\n", (unsigned long long)
+ sas_address, mpt3sas_phy->phy_id);
+
+ list_add_tail(&mpt3sas_phy->port_siblings, &mpt3sas_port->phy_list);
+ mpt3sas_port->num_phys++;
+ sas_port_add_phy(mpt3sas_port->port, mpt3sas_phy->phy);
+ mpt3sas_phy->phy_belongs_to_port = 1;
+}
+
+/**
+ * _transport_add_phy_to_an_existing_port - adding new phy to existing port
+ * @ioc: per adapter object
+ * @sas_node: sas node object (either expander or sas host)
+ * @mpt3sas_phy: mpt3sas per phy object
+ * @sas_address: sas address of device/expander were phy needs to be added to
+ *
+ * Returns nothing.
+ */
+static void
+_transport_add_phy_to_an_existing_port(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_node *sas_node, struct _sas_phy *mpt3sas_phy,
+ u64 sas_address)
+{
+ struct _sas_port *mpt3sas_port;
+ struct _sas_phy *phy_srch;
+
+ if (mpt3sas_phy->phy_belongs_to_port == 1)
+ return;
+
+ list_for_each_entry(mpt3sas_port, &sas_node->sas_port_list,
+ port_list) {
+ if (mpt3sas_port->remote_identify.sas_address !=
+ sas_address)
+ continue;
+ list_for_each_entry(phy_srch, &mpt3sas_port->phy_list,
+ port_siblings) {
+ if (phy_srch == mpt3sas_phy)
+ return;
+ }
+ _transport_add_phy(ioc, mpt3sas_port, mpt3sas_phy);
+ return;
+ }
+
+}
+
+/**
+ * _transport_del_phy_from_an_existing_port - delete phy from existing port
+ * @ioc: per adapter object
+ * @sas_node: sas node object (either expander or sas host)
+ * @mpt3sas_phy: mpt3sas per phy object
+ *
+ * Returns nothing.
+ */
+static void
+_transport_del_phy_from_an_existing_port(struct MPT3SAS_ADAPTER *ioc,
+ struct _sas_node *sas_node, struct _sas_phy *mpt3sas_phy)
+{
+ struct _sas_port *mpt3sas_port, *next;
+ struct _sas_phy *phy_srch;
+
+ if (mpt3sas_phy->phy_belongs_to_port == 0)
+ return;
+
+ list_for_each_entry_safe(mpt3sas_port, next, &sas_node->sas_port_list,
+ port_list) {
+ list_for_each_entry(phy_srch, &mpt3sas_port->phy_list,
+ port_siblings) {
+ if (phy_srch != mpt3sas_phy)
+ continue;
+
+ if (mpt3sas_port->num_phys == 1)
+ _transport_delete_port(ioc, mpt3sas_port);
+ else
+ _transport_delete_phy(ioc, mpt3sas_port,
+ mpt3sas_phy);
+ return;
+ }
+ }
+}
+
+/**
+ * _transport_sanity_check - sanity check when adding a new port
+ * @ioc: per adapter object
+ * @sas_node: sas node object (either expander or sas host)
+ * @sas_address: sas address of device being added
+ *
+ * See the explanation above from _transport_delete_duplicate_port
+ */
+static void
+_transport_sanity_check(struct MPT3SAS_ADAPTER *ioc, struct _sas_node *sas_node,
+ u64 sas_address)
+{
+ int i;
+
+ for (i = 0; i < sas_node->num_phys; i++) {
+ if (sas_node->phy[i].remote_identify.sas_address != sas_address)
+ continue;
+ if (sas_node->phy[i].phy_belongs_to_port == 1)
+ _transport_del_phy_from_an_existing_port(ioc, sas_node,
+ &sas_node->phy[i]);
+ }
+}
+
+/**
+ * mpt3sas_transport_port_add - insert port to the list
+ * @ioc: per adapter object
+ * @handle: handle of attached device
+ * @sas_address: sas address of parent expander or sas host
+ * Context: This function will acquire ioc->sas_node_lock.
+ *
+ * Adding new port object to the sas_node->sas_port_list.
+ *
+ * Returns mpt3sas_port.
+ */
+struct _sas_port *
+mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle,
+ u64 sas_address)
+{
+ struct _sas_phy *mpt3sas_phy, *next;
+ struct _sas_port *mpt3sas_port;
+ unsigned long flags;
+ struct _sas_node *sas_node;
+ struct sas_rphy *rphy;
+ int i;
+ struct sas_port *port;
+
+ mpt3sas_port = kzalloc(sizeof(struct _sas_port),
+ GFP_KERNEL);
+ if (!mpt3sas_port) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&mpt3sas_port->port_list);
+ INIT_LIST_HEAD(&mpt3sas_port->phy_list);
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ if (!sas_node) {
+ pr_err(MPT3SAS_FMT
+ "%s: Could not find parent sas_address(0x%016llx)!\n",
+ ioc->name, __func__, (unsigned long long)sas_address);
+ goto out_fail;
+ }
+
+ if ((_transport_set_identify(ioc, handle,
+ &mpt3sas_port->remote_identify))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out_fail;
+ }
+
+ if (mpt3sas_port->remote_identify.device_type == SAS_PHY_UNUSED) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out_fail;
+ }
+
+ _transport_sanity_check(ioc, sas_node,
+ mpt3sas_port->remote_identify.sas_address);
+
+ for (i = 0; i < sas_node->num_phys; i++) {
+ if (sas_node->phy[i].remote_identify.sas_address !=
+ mpt3sas_port->remote_identify.sas_address)
+ continue;
+ list_add_tail(&sas_node->phy[i].port_siblings,
+ &mpt3sas_port->phy_list);
+ mpt3sas_port->num_phys++;
+ }
+
+ if (!mpt3sas_port->num_phys) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out_fail;
+ }
+
+ port = sas_port_alloc_num(sas_node->parent_dev);
+ if ((sas_port_add(port))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ goto out_fail;
+ }
+
+ list_for_each_entry(mpt3sas_phy, &mpt3sas_port->phy_list,
+ port_siblings) {
+ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
+ dev_printk(KERN_INFO, &port->dev,
+ "add: handle(0x%04x), sas_addr(0x%016llx), phy(%d)\n",
+ handle, (unsigned long long)
+ mpt3sas_port->remote_identify.sas_address,
+ mpt3sas_phy->phy_id);
+ sas_port_add_phy(port, mpt3sas_phy->phy);
+ mpt3sas_phy->phy_belongs_to_port = 1;
+ }
+
+ mpt3sas_port->port = port;
+ if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE)
+ rphy = sas_end_device_alloc(port);
+ else
+ rphy = sas_expander_alloc(port,
+ mpt3sas_port->remote_identify.device_type);
+
+ rphy->identify = mpt3sas_port->remote_identify;
+ if ((sas_rphy_add(rphy))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ }
+ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
+ dev_printk(KERN_INFO, &rphy->dev,
+ "add: handle(0x%04x), sas_addr(0x%016llx)\n",
+ handle, (unsigned long long)
+ mpt3sas_port->remote_identify.sas_address);
+ mpt3sas_port->rphy = rphy;
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ list_add_tail(&mpt3sas_port->port_list, &sas_node->sas_port_list);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ /* fill in report manufacture */
+ if (mpt3sas_port->remote_identify.device_type ==
+ MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER ||
+ mpt3sas_port->remote_identify.device_type ==
+ MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER)
+ _transport_expander_report_manufacture(ioc,
+ mpt3sas_port->remote_identify.sas_address,
+ rphy_to_expander_device(rphy));
+ return mpt3sas_port;
+
+ out_fail:
+ list_for_each_entry_safe(mpt3sas_phy, next, &mpt3sas_port->phy_list,
+ port_siblings)
+ list_del(&mpt3sas_phy->port_siblings);
+ kfree(mpt3sas_port);
+ return NULL;
+}
+
+/**
+ * mpt3sas_transport_port_remove - remove port from the list
+ * @ioc: per adapter object
+ * @sas_address: sas address of attached device
+ * @sas_address_parent: sas address of parent expander or sas host
+ * Context: This function will acquire ioc->sas_node_lock.
+ *
+ * Removing object and freeing associated memory from the
+ * ioc->sas_port_list.
+ *
+ * Return nothing.
+ */
+void
+mpt3sas_transport_port_remove(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
+ u64 sas_address_parent)
+{
+ int i;
+ unsigned long flags;
+ struct _sas_port *mpt3sas_port, *next;
+ struct _sas_node *sas_node;
+ u8 found = 0;
+ struct _sas_phy *mpt3sas_phy, *next_phy;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_node = _transport_sas_node_find_by_sas_address(ioc,
+ sas_address_parent);
+ if (!sas_node) {
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ return;
+ }
+ list_for_each_entry_safe(mpt3sas_port, next, &sas_node->sas_port_list,
+ port_list) {
+ if (mpt3sas_port->remote_identify.sas_address != sas_address)
+ continue;
+ found = 1;
+ list_del(&mpt3sas_port->port_list);
+ goto out;
+ }
+ out:
+ if (!found) {
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ return;
+ }
+
+ for (i = 0; i < sas_node->num_phys; i++) {
+ if (sas_node->phy[i].remote_identify.sas_address == sas_address)
+ memset(&sas_node->phy[i].remote_identify, 0 ,
+ sizeof(struct sas_identify));
+ }
+
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ list_for_each_entry_safe(mpt3sas_phy, next_phy,
+ &mpt3sas_port->phy_list, port_siblings) {
+ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
+ dev_printk(KERN_INFO, &mpt3sas_port->port->dev,
+ "remove: sas_addr(0x%016llx), phy(%d)\n",
+ (unsigned long long)
+ mpt3sas_port->remote_identify.sas_address,
+ mpt3sas_phy->phy_id);
+ mpt3sas_phy->phy_belongs_to_port = 0;
+ sas_port_delete_phy(mpt3sas_port->port, mpt3sas_phy->phy);
+ list_del(&mpt3sas_phy->port_siblings);
+ }
+ sas_port_delete(mpt3sas_port->port);
+ kfree(mpt3sas_port);
+}
+
+/**
+ * mpt3sas_transport_add_host_phy - report sas_host phy to transport
+ * @ioc: per adapter object
+ * @mpt3sas_phy: mpt3sas per phy object
+ * @phy_pg0: sas phy page 0
+ * @parent_dev: parent device class object
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_transport_add_host_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy
+ *mpt3sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev)
+{
+ struct sas_phy *phy;
+ int phy_index = mpt3sas_phy->phy_id;
+
+
+ INIT_LIST_HEAD(&mpt3sas_phy->port_siblings);
+ phy = sas_phy_alloc(parent_dev, phy_index);
+ if (!phy) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -1;
+ }
+ if ((_transport_set_identify(ioc, mpt3sas_phy->handle,
+ &mpt3sas_phy->identify))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ sas_phy_free(phy);
+ return -1;
+ }
+ phy->identify = mpt3sas_phy->identify;
+ mpt3sas_phy->attached_handle = le16_to_cpu(phy_pg0.AttachedDevHandle);
+ if (mpt3sas_phy->attached_handle)
+ _transport_set_identify(ioc, mpt3sas_phy->attached_handle,
+ &mpt3sas_phy->remote_identify);
+ phy->identify.phy_identifier = mpt3sas_phy->phy_id;
+ phy->negotiated_linkrate = _transport_convert_phy_link_rate(
+ phy_pg0.NegotiatedLinkRate & MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
+ phy->minimum_linkrate_hw = _transport_convert_phy_link_rate(
+ phy_pg0.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK);
+ phy->maximum_linkrate_hw = _transport_convert_phy_link_rate(
+ phy_pg0.HwLinkRate >> 4);
+ phy->minimum_linkrate = _transport_convert_phy_link_rate(
+ phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
+ phy->maximum_linkrate = _transport_convert_phy_link_rate(
+ phy_pg0.ProgrammedLinkRate >> 4);
+
+ if ((sas_phy_add(phy))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ sas_phy_free(phy);
+ return -1;
+ }
+ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
+ dev_printk(KERN_INFO, &phy->dev,
+ "add: handle(0x%04x), sas_addr(0x%016llx)\n"
+ "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
+ mpt3sas_phy->handle, (unsigned long long)
+ mpt3sas_phy->identify.sas_address,
+ mpt3sas_phy->attached_handle,
+ (unsigned long long)
+ mpt3sas_phy->remote_identify.sas_address);
+ mpt3sas_phy->phy = phy;
+ return 0;
+}
+
+
+/**
+ * mpt3sas_transport_add_expander_phy - report expander phy to transport
+ * @ioc: per adapter object
+ * @mpt3sas_phy: mpt3sas per phy object
+ * @expander_pg1: expander page 1
+ * @parent_dev: parent device class object
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+mpt3sas_transport_add_expander_phy(struct MPT3SAS_ADAPTER *ioc, struct _sas_phy
+ *mpt3sas_phy, Mpi2ExpanderPage1_t expander_pg1,
+ struct device *parent_dev)
+{
+ struct sas_phy *phy;
+ int phy_index = mpt3sas_phy->phy_id;
+
+ INIT_LIST_HEAD(&mpt3sas_phy->port_siblings);
+ phy = sas_phy_alloc(parent_dev, phy_index);
+ if (!phy) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -1;
+ }
+ if ((_transport_set_identify(ioc, mpt3sas_phy->handle,
+ &mpt3sas_phy->identify))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ sas_phy_free(phy);
+ return -1;
+ }
+ phy->identify = mpt3sas_phy->identify;
+ mpt3sas_phy->attached_handle =
+ le16_to_cpu(expander_pg1.AttachedDevHandle);
+ if (mpt3sas_phy->attached_handle)
+ _transport_set_identify(ioc, mpt3sas_phy->attached_handle,
+ &mpt3sas_phy->remote_identify);
+ phy->identify.phy_identifier = mpt3sas_phy->phy_id;
+ phy->negotiated_linkrate = _transport_convert_phy_link_rate(
+ expander_pg1.NegotiatedLinkRate &
+ MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
+ phy->minimum_linkrate_hw = _transport_convert_phy_link_rate(
+ expander_pg1.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK);
+ phy->maximum_linkrate_hw = _transport_convert_phy_link_rate(
+ expander_pg1.HwLinkRate >> 4);
+ phy->minimum_linkrate = _transport_convert_phy_link_rate(
+ expander_pg1.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
+ phy->maximum_linkrate = _transport_convert_phy_link_rate(
+ expander_pg1.ProgrammedLinkRate >> 4);
+
+ if ((sas_phy_add(phy))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ sas_phy_free(phy);
+ return -1;
+ }
+ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
+ dev_printk(KERN_INFO, &phy->dev,
+ "add: handle(0x%04x), sas_addr(0x%016llx)\n"
+ "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
+ mpt3sas_phy->handle, (unsigned long long)
+ mpt3sas_phy->identify.sas_address,
+ mpt3sas_phy->attached_handle,
+ (unsigned long long)
+ mpt3sas_phy->remote_identify.sas_address);
+ mpt3sas_phy->phy = phy;
+ return 0;
+}
+
+/**
+ * mpt3sas_transport_update_links - refreshing phy link changes
+ * @ioc: per adapter object
+ * @sas_address: sas address of parent expander or sas host
+ * @handle: attached device handle
+ * @phy_numberv: phy number
+ * @link_rate: new link rate
+ *
+ * Returns nothing.
+ */
+void
+mpt3sas_transport_update_links(struct MPT3SAS_ADAPTER *ioc,
+ u64 sas_address, u16 handle, u8 phy_number, u8 link_rate)
+{
+ unsigned long flags;
+ struct _sas_node *sas_node;
+ struct _sas_phy *mpt3sas_phy;
+
+ if (ioc->shost_recovery || ioc->pci_error_recovery)
+ return;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address);
+ if (!sas_node) {
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ return;
+ }
+
+ mpt3sas_phy = &sas_node->phy[phy_number];
+ mpt3sas_phy->attached_handle = handle;
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ if (handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)) {
+ _transport_set_identify(ioc, handle,
+ &mpt3sas_phy->remote_identify);
+ _transport_add_phy_to_an_existing_port(ioc, sas_node,
+ mpt3sas_phy, mpt3sas_phy->remote_identify.sas_address);
+ } else
+ memset(&mpt3sas_phy->remote_identify, 0 , sizeof(struct
+ sas_identify));
+
+ if (mpt3sas_phy->phy)
+ mpt3sas_phy->phy->negotiated_linkrate =
+ _transport_convert_phy_link_rate(link_rate);
+
+ if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
+ dev_printk(KERN_INFO, &mpt3sas_phy->phy->dev,
+ "refresh: parent sas_addr(0x%016llx),\n"
+ "\tlink_rate(0x%02x), phy(%d)\n"
+ "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
+ (unsigned long long)sas_address,
+ link_rate, phy_number, handle, (unsigned long long)
+ mpt3sas_phy->remote_identify.sas_address);
+}
+
+static inline void *
+phy_to_ioc(struct sas_phy *phy)
+{
+ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
+ return shost_priv(shost);
+}
+
+static inline void *
+rphy_to_ioc(struct sas_rphy *rphy)
+{
+ struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent);
+ return shost_priv(shost);
+}
+
+/* report phy error log structure */
+struct phy_error_log_request {
+ u8 smp_frame_type; /* 0x40 */
+ u8 function; /* 0x11 */
+ u8 allocated_response_length;
+ u8 request_length; /* 02 */
+ u8 reserved_1[5];
+ u8 phy_identifier;
+ u8 reserved_2[2];
+};
+
+/* report phy error log reply structure */
+struct phy_error_log_reply {
+ u8 smp_frame_type; /* 0x41 */
+ u8 function; /* 0x11 */
+ u8 function_result;
+ u8 response_length;
+ __be16 expander_change_count;
+ u8 reserved_1[3];
+ u8 phy_identifier;
+ u8 reserved_2[2];
+ __be32 invalid_dword;
+ __be32 running_disparity_error;
+ __be32 loss_of_dword_sync;
+ __be32 phy_reset_problem;
+};
+
+/**
+ * _transport_get_expander_phy_error_log - return expander counters
+ * @ioc: per adapter object
+ * @phy: The sas phy object
+ *
+ * Returns 0 for success, non-zero for failure.
+ *
+ */
+static int
+_transport_get_expander_phy_error_log(struct MPT3SAS_ADAPTER *ioc,
+ struct sas_phy *phy)
+{
+ Mpi2SmpPassthroughRequest_t *mpi_request;
+ Mpi2SmpPassthroughReply_t *mpi_reply;
+ struct phy_error_log_request *phy_error_log_request;
+ struct phy_error_log_reply *phy_error_log_reply;
+ int rc;
+ u16 smid;
+ u32 ioc_state;
+ unsigned long timeleft;
+ void *psge;
+ u8 issue_reset = 0;
+ void *data_out = NULL;
+ dma_addr_t data_out_dma;
+ u32 sz;
+ u16 wait_state_count;
+
+ if (ioc->shost_recovery || ioc->pci_error_recovery) {
+ pr_info(MPT3SAS_FMT "%s: host reset in progress!\n",
+ __func__, ioc->name);
+ return -EFAULT;
+ }
+
+ mutex_lock(&ioc->transport_cmds.mutex);
+
+ if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) {
+ pr_err(MPT3SAS_FMT "%s: transport_cmds in use\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+ ioc->transport_cmds.status = MPT3_CMD_PENDING;
+
+ wait_state_count = 0;
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ if (wait_state_count++ == 10) {
+ pr_err(MPT3SAS_FMT
+ "%s: failed due to ioc not operational\n",
+ ioc->name, __func__);
+ rc = -EFAULT;
+ goto out;
+ }
+ ssleep(1);
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ pr_info(MPT3SAS_FMT
+ "%s: waiting for operational state(count=%d)\n",
+ ioc->name, __func__, wait_state_count);
+ }
+ if (wait_state_count)
+ pr_info(MPT3SAS_FMT "%s: ioc is operational\n",
+ ioc->name, __func__);
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->transport_cmds.smid = smid;
+
+ sz = sizeof(struct phy_error_log_request) +
+ sizeof(struct phy_error_log_reply);
+ data_out = pci_alloc_consistent(ioc->pdev, sz, &data_out_dma);
+ if (!data_out) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ rc = -ENOMEM;
+ mpt3sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+
+ rc = -EINVAL;
+ memset(data_out, 0, sz);
+ phy_error_log_request = data_out;
+ phy_error_log_request->smp_frame_type = 0x40;
+ phy_error_log_request->function = 0x11;
+ phy_error_log_request->request_length = 2;
+ phy_error_log_request->allocated_response_length = 0;
+ phy_error_log_request->phy_identifier = phy->number;
+
+ memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
+ mpi_request->PhysicalPort = 0xFF;
+ mpi_request->VF_ID = 0; /* TODO */
+ mpi_request->VP_ID = 0;
+ mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address);
+ mpi_request->RequestDataLength =
+ cpu_to_le16(sizeof(struct phy_error_log_request));
+ psge = &mpi_request->SGL;
+
+ ioc->build_sg(ioc, psge, data_out_dma,
+ sizeof(struct phy_error_log_request),
+ data_out_dma + sizeof(struct phy_error_log_request),
+ sizeof(struct phy_error_log_reply));
+
+ dtransportprintk(ioc, pr_info(MPT3SAS_FMT
+ "phy_error_log - send to sas_addr(0x%016llx), phy(%d)\n",
+ ioc->name, (unsigned long long)phy->identify.sas_address,
+ phy->number));
+ init_completion(&ioc->transport_cmds.done);
+ mpt3sas_base_put_smid_default(ioc, smid);
+ timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done,
+ 10*HZ);
+
+ if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
+ pr_err(MPT3SAS_FMT "%s: timeout\n",
+ ioc->name, __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2SmpPassthroughRequest_t)/4);
+ if (!(ioc->transport_cmds.status & MPT3_CMD_RESET))
+ issue_reset = 1;
+ goto issue_host_reset;
+ }
+
+ dtransportprintk(ioc, pr_info(MPT3SAS_FMT
+ "phy_error_log - complete\n", ioc->name));
+
+ if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) {
+
+ mpi_reply = ioc->transport_cmds.reply;
+
+ dtransportprintk(ioc, pr_info(MPT3SAS_FMT
+ "phy_error_log - reply data transfer size(%d)\n",
+ ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength)));
+
+ if (le16_to_cpu(mpi_reply->ResponseDataLength) !=
+ sizeof(struct phy_error_log_reply))
+ goto out;
+
+ phy_error_log_reply = data_out +
+ sizeof(struct phy_error_log_request);
+
+ dtransportprintk(ioc, pr_info(MPT3SAS_FMT
+ "phy_error_log - function_result(%d)\n",
+ ioc->name, phy_error_log_reply->function_result));
+
+ phy->invalid_dword_count =
+ be32_to_cpu(phy_error_log_reply->invalid_dword);
+ phy->running_disparity_error_count =
+ be32_to_cpu(phy_error_log_reply->running_disparity_error);
+ phy->loss_of_dword_sync_count =
+ be32_to_cpu(phy_error_log_reply->loss_of_dword_sync);
+ phy->phy_reset_problem_count =
+ be32_to_cpu(phy_error_log_reply->phy_reset_problem);
+ rc = 0;
+ } else
+ dtransportprintk(ioc, pr_info(MPT3SAS_FMT
+ "phy_error_log - no reply\n", ioc->name));
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ out:
+ ioc->transport_cmds.status = MPT3_CMD_NOT_USED;
+ if (data_out)
+ pci_free_consistent(ioc->pdev, sz, data_out, data_out_dma);
+
+ mutex_unlock(&ioc->transport_cmds.mutex);
+ return rc;
+}
+
+/**
+ * _transport_get_linkerrors - return phy counters for both hba and expanders
+ * @phy: The sas phy object
+ *
+ * Returns 0 for success, non-zero for failure.
+ *
+ */
+static int
+_transport_get_linkerrors(struct sas_phy *phy)
+{
+ struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy);
+ unsigned long flags;
+ Mpi2ConfigReply_t mpi_reply;
+ Mpi2SasPhyPage1_t phy_pg1;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ if (_transport_sas_node_find_by_sas_address(ioc,
+ phy->identify.sas_address) == NULL) {
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ if (phy->identify.sas_address != ioc->sas_hba.sas_address)
+ return _transport_get_expander_phy_error_log(ioc, phy);
+
+ /* get hba phy error logs */
+ if ((mpt3sas_config_get_phy_pg1(ioc, &mpi_reply, &phy_pg1,
+ phy->number))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -ENXIO;
+ }
+
+ if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo)
+ pr_info(MPT3SAS_FMT
+ "phy(%d), ioc_status (0x%04x), loginfo(0x%08x)\n",
+ ioc->name, phy->number,
+ le16_to_cpu(mpi_reply.IOCStatus),
+ le32_to_cpu(mpi_reply.IOCLogInfo));
+
+ phy->invalid_dword_count = le32_to_cpu(phy_pg1.InvalidDwordCount);
+ phy->running_disparity_error_count =
+ le32_to_cpu(phy_pg1.RunningDisparityErrorCount);
+ phy->loss_of_dword_sync_count =
+ le32_to_cpu(phy_pg1.LossDwordSynchCount);
+ phy->phy_reset_problem_count =
+ le32_to_cpu(phy_pg1.PhyResetProblemCount);
+ return 0;
+}
+
+/**
+ * _transport_get_enclosure_identifier -
+ * @phy: The sas phy object
+ *
+ * Obtain the enclosure logical id for an expander.
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier)
+{
+ struct MPT3SAS_ADAPTER *ioc = rphy_to_ioc(rphy);
+ struct _sas_device *sas_device;
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+ rphy->identify.sas_address);
+ if (sas_device) {
+ *identifier = sas_device->enclosure_logical_id;
+ rc = 0;
+ } else {
+ *identifier = 0;
+ rc = -ENXIO;
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ return rc;
+}
+
+/**
+ * _transport_get_bay_identifier -
+ * @phy: The sas phy object
+ *
+ * Returns the slot id for a device that resides inside an enclosure.
+ */
+static int
+_transport_get_bay_identifier(struct sas_rphy *rphy)
+{
+ struct MPT3SAS_ADAPTER *ioc = rphy_to_ioc(rphy);
+ struct _sas_device *sas_device;
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&ioc->sas_device_lock, flags);
+ sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+ rphy->identify.sas_address);
+ if (sas_device)
+ rc = sas_device->slot;
+ else
+ rc = -ENXIO;
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ return rc;
+}
+
+/* phy control request structure */
+struct phy_control_request {
+ u8 smp_frame_type; /* 0x40 */
+ u8 function; /* 0x91 */
+ u8 allocated_response_length;
+ u8 request_length; /* 0x09 */
+ u16 expander_change_count;
+ u8 reserved_1[3];
+ u8 phy_identifier;
+ u8 phy_operation;
+ u8 reserved_2[13];
+ u64 attached_device_name;
+ u8 programmed_min_physical_link_rate;
+ u8 programmed_max_physical_link_rate;
+ u8 reserved_3[6];
+};
+
+/* phy control reply structure */
+struct phy_control_reply {
+ u8 smp_frame_type; /* 0x41 */
+ u8 function; /* 0x11 */
+ u8 function_result;
+ u8 response_length;
+};
+
+#define SMP_PHY_CONTROL_LINK_RESET (0x01)
+#define SMP_PHY_CONTROL_HARD_RESET (0x02)
+#define SMP_PHY_CONTROL_DISABLE (0x03)
+
+/**
+ * _transport_expander_phy_control - expander phy control
+ * @ioc: per adapter object
+ * @phy: The sas phy object
+ *
+ * Returns 0 for success, non-zero for failure.
+ *
+ */
+static int
+_transport_expander_phy_control(struct MPT3SAS_ADAPTER *ioc,
+ struct sas_phy *phy, u8 phy_operation)
+{
+ Mpi2SmpPassthroughRequest_t *mpi_request;
+ Mpi2SmpPassthroughReply_t *mpi_reply;
+ struct phy_control_request *phy_control_request;
+ struct phy_control_reply *phy_control_reply;
+ int rc;
+ u16 smid;
+ u32 ioc_state;
+ unsigned long timeleft;
+ void *psge;
+ u32 sgl_flags;
+ u8 issue_reset = 0;
+ void *data_out = NULL;
+ dma_addr_t data_out_dma;
+ u32 sz;
+ u16 wait_state_count;
+
+ if (ioc->shost_recovery || ioc->pci_error_recovery) {
+ pr_info(MPT3SAS_FMT "%s: host reset in progress!\n",
+ __func__, ioc->name);
+ return -EFAULT;
+ }
+
+ mutex_lock(&ioc->transport_cmds.mutex);
+
+ if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) {
+ pr_err(MPT3SAS_FMT "%s: transport_cmds in use\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+ ioc->transport_cmds.status = MPT3_CMD_PENDING;
+
+ wait_state_count = 0;
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ if (wait_state_count++ == 10) {
+ pr_err(MPT3SAS_FMT
+ "%s: failed due to ioc not operational\n",
+ ioc->name, __func__);
+ rc = -EFAULT;
+ goto out;
+ }
+ ssleep(1);
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ pr_info(MPT3SAS_FMT
+ "%s: waiting for operational state(count=%d)\n",
+ ioc->name, __func__, wait_state_count);
+ }
+ if (wait_state_count)
+ pr_info(MPT3SAS_FMT "%s: ioc is operational\n",
+ ioc->name, __func__);
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->transport_cmds.smid = smid;
+
+ sz = sizeof(struct phy_control_request) +
+ sizeof(struct phy_control_reply);
+ data_out = pci_alloc_consistent(ioc->pdev, sz, &data_out_dma);
+ if (!data_out) {
+ pr_err("failure at %s:%d/%s()!\n", __FILE__,
+ __LINE__, __func__);
+ rc = -ENOMEM;
+ mpt3sas_base_free_smid(ioc, smid);
+ goto out;
+ }
+
+ rc = -EINVAL;
+ memset(data_out, 0, sz);
+ phy_control_request = data_out;
+ phy_control_request->smp_frame_type = 0x40;
+ phy_control_request->function = 0x91;
+ phy_control_request->request_length = 9;
+ phy_control_request->allocated_response_length = 0;
+ phy_control_request->phy_identifier = phy->number;
+ phy_control_request->phy_operation = phy_operation;
+ phy_control_request->programmed_min_physical_link_rate =
+ phy->minimum_linkrate << 4;
+ phy_control_request->programmed_max_physical_link_rate =
+ phy->maximum_linkrate << 4;
+
+ memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
+ mpi_request->PhysicalPort = 0xFF;
+ mpi_request->VF_ID = 0; /* TODO */
+ mpi_request->VP_ID = 0;
+ mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address);
+ mpi_request->RequestDataLength =
+ cpu_to_le16(sizeof(struct phy_error_log_request));
+ psge = &mpi_request->SGL;
+
+ /* WRITE sgel first */
+ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC);
+ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
+ ioc->base_add_sg_single(psge, sgl_flags |
+ sizeof(struct phy_control_request), data_out_dma);
+
+ /* incr sgel */
+ psge += ioc->sge_size;
+
+ /* READ sgel last */
+ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT |
+ MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER |
+ MPI2_SGE_FLAGS_END_OF_LIST);
+ sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT;
+ ioc->base_add_sg_single(psge, sgl_flags |
+ sizeof(struct phy_control_reply), data_out_dma +
+ sizeof(struct phy_control_request));
+
+ dtransportprintk(ioc, pr_info(MPT3SAS_FMT
+ "phy_control - send to sas_addr(0x%016llx), phy(%d), opcode(%d)\n",
+ ioc->name, (unsigned long long)phy->identify.sas_address,
+ phy->number, phy_operation));
+ init_completion(&ioc->transport_cmds.done);
+ mpt3sas_base_put_smid_default(ioc, smid);
+ timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done,
+ 10*HZ);
+
+ if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
+ pr_err(MPT3SAS_FMT "%s: timeout\n",
+ ioc->name, __func__);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2SmpPassthroughRequest_t)/4);
+ if (!(ioc->transport_cmds.status & MPT3_CMD_RESET))
+ issue_reset = 1;
+ goto issue_host_reset;
+ }
+
+ dtransportprintk(ioc, pr_info(MPT3SAS_FMT
+ "phy_control - complete\n", ioc->name));
+
+ if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) {
+
+ mpi_reply = ioc->transport_cmds.reply;
+
+ dtransportprintk(ioc, pr_info(MPT3SAS_FMT
+ "phy_control - reply data transfer size(%d)\n",
+ ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength)));
+
+ if (le16_to_cpu(mpi_reply->ResponseDataLength) !=
+ sizeof(struct phy_control_reply))
+ goto out;
+
+ phy_control_reply = data_out +
+ sizeof(struct phy_control_request);
+
+ dtransportprintk(ioc, pr_info(MPT3SAS_FMT
+ "phy_control - function_result(%d)\n",
+ ioc->name, phy_control_reply->function_result));
+
+ rc = 0;
+ } else
+ dtransportprintk(ioc, pr_info(MPT3SAS_FMT
+ "phy_control - no reply\n", ioc->name));
+
+ issue_host_reset:
+ if (issue_reset)
+ mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ out:
+ ioc->transport_cmds.status = MPT3_CMD_NOT_USED;
+ if (data_out)
+ pci_free_consistent(ioc->pdev, sz, data_out, data_out_dma);
+
+ mutex_unlock(&ioc->transport_cmds.mutex);
+ return rc;
+}
+
+/**
+ * _transport_phy_reset -
+ * @phy: The sas phy object
+ * @hard_reset:
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_transport_phy_reset(struct sas_phy *phy, int hard_reset)
+{
+ struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy);
+ Mpi2SasIoUnitControlReply_t mpi_reply;
+ Mpi2SasIoUnitControlRequest_t mpi_request;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ if (_transport_sas_node_find_by_sas_address(ioc,
+ phy->identify.sas_address) == NULL) {
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ /* handle expander phys */
+ if (phy->identify.sas_address != ioc->sas_hba.sas_address)
+ return _transport_expander_phy_control(ioc, phy,
+ (hard_reset == 1) ? SMP_PHY_CONTROL_HARD_RESET :
+ SMP_PHY_CONTROL_LINK_RESET);
+
+ /* handle hba phys */
+ memset(&mpi_request, 0, sizeof(Mpi2SasIoUnitControlReply_t));
+ mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
+ mpi_request.Operation = hard_reset ?
+ MPI2_SAS_OP_PHY_HARD_RESET : MPI2_SAS_OP_PHY_LINK_RESET;
+ mpi_request.PhyNum = phy->number;
+
+ if ((mpt3sas_base_sas_iounit_control(ioc, &mpi_reply, &mpi_request))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ return -ENXIO;
+ }
+
+ if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo)
+ pr_info(MPT3SAS_FMT
+ "phy(%d), ioc_status(0x%04x), loginfo(0x%08x)\n",
+ ioc->name, phy->number, le16_to_cpu(mpi_reply.IOCStatus),
+ le32_to_cpu(mpi_reply.IOCLogInfo));
+
+ return 0;
+}
+
+/**
+ * _transport_phy_enable - enable/disable phys
+ * @phy: The sas phy object
+ * @enable: enable phy when true
+ *
+ * Only support sas_host direct attached phys.
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_transport_phy_enable(struct sas_phy *phy, int enable)
+{
+ struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy);
+ Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
+ Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 ioc_status;
+ u16 sz;
+ int rc = 0;
+ unsigned long flags;
+ int i, discovery_active;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ if (_transport_sas_node_find_by_sas_address(ioc,
+ phy->identify.sas_address) == NULL) {
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ /* handle expander phys */
+ if (phy->identify.sas_address != ioc->sas_hba.sas_address)
+ return _transport_expander_phy_control(ioc, phy,
+ (enable == 1) ? SMP_PHY_CONTROL_LINK_RESET :
+ SMP_PHY_CONTROL_DISABLE);
+
+ /* handle hba phys */
+
+ /* read sas_iounit page 0 */
+ sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys *
+ sizeof(Mpi2SasIOUnit0PhyData_t));
+ sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg0) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -ENOMEM;
+ goto out;
+ }
+ if ((mpt3sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
+ sas_iounit_pg0, sz))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -ENXIO;
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -EIO;
+ goto out;
+ }
+
+ /* unable to enable/disable phys when when discovery is active */
+ for (i = 0, discovery_active = 0; i < ioc->sas_hba.num_phys ; i++) {
+ if (sas_iounit_pg0->PhyData[i].PortFlags &
+ MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS) {
+ pr_err(MPT3SAS_FMT "discovery is active on " \
+ "port = %d, phy = %d: unable to enable/disable "
+ "phys, try again later!\n", ioc->name,
+ sas_iounit_pg0->PhyData[i].Port, i);
+ discovery_active = 1;
+ }
+ }
+
+ if (discovery_active) {
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ /* read sas_iounit page 1 */
+ sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys *
+ sizeof(Mpi2SasIOUnit1PhyData_t));
+ sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg1) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -ENOMEM;
+ goto out;
+ }
+ if ((mpt3sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
+ sas_iounit_pg1, sz))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -ENXIO;
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -EIO;
+ goto out;
+ }
+
+ /* copy Port/PortFlags/PhyFlags from page 0 */
+ for (i = 0; i < ioc->sas_hba.num_phys ; i++) {
+ sas_iounit_pg1->PhyData[i].Port =
+ sas_iounit_pg0->PhyData[i].Port;
+ sas_iounit_pg1->PhyData[i].PortFlags =
+ (sas_iounit_pg0->PhyData[i].PortFlags &
+ MPI2_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG);
+ sas_iounit_pg1->PhyData[i].PhyFlags =
+ (sas_iounit_pg0->PhyData[i].PhyFlags &
+ (MPI2_SASIOUNIT0_PHYFLAGS_ZONING_ENABLED +
+ MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED));
+ }
+
+ if (enable)
+ sas_iounit_pg1->PhyData[phy->number].PhyFlags
+ &= ~MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
+ else
+ sas_iounit_pg1->PhyData[phy->number].PhyFlags
+ |= MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
+
+ mpt3sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, sz);
+
+ /* link reset */
+ if (enable)
+ _transport_phy_reset(phy, 0);
+
+ out:
+ kfree(sas_iounit_pg1);
+ kfree(sas_iounit_pg0);
+ return rc;
+}
+
+/**
+ * _transport_phy_speed - set phy min/max link rates
+ * @phy: The sas phy object
+ * @rates: rates defined in sas_phy_linkrates
+ *
+ * Only support sas_host direct attached phys.
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates)
+{
+ struct MPT3SAS_ADAPTER *ioc = phy_to_ioc(phy);
+ Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
+ Mpi2SasPhyPage0_t phy_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u16 ioc_status;
+ u16 sz;
+ int i;
+ int rc = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ if (_transport_sas_node_find_by_sas_address(ioc,
+ phy->identify.sas_address) == NULL) {
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+
+ if (!rates->minimum_linkrate)
+ rates->minimum_linkrate = phy->minimum_linkrate;
+ else if (rates->minimum_linkrate < phy->minimum_linkrate_hw)
+ rates->minimum_linkrate = phy->minimum_linkrate_hw;
+
+ if (!rates->maximum_linkrate)
+ rates->maximum_linkrate = phy->maximum_linkrate;
+ else if (rates->maximum_linkrate > phy->maximum_linkrate_hw)
+ rates->maximum_linkrate = phy->maximum_linkrate_hw;
+
+ /* handle expander phys */
+ if (phy->identify.sas_address != ioc->sas_hba.sas_address) {
+ phy->minimum_linkrate = rates->minimum_linkrate;
+ phy->maximum_linkrate = rates->maximum_linkrate;
+ return _transport_expander_phy_control(ioc, phy,
+ SMP_PHY_CONTROL_LINK_RESET);
+ }
+
+ /* handle hba phys */
+
+ /* sas_iounit page 1 */
+ sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys *
+ sizeof(Mpi2SasIOUnit1PhyData_t));
+ sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
+ if (!sas_iounit_pg1) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -ENOMEM;
+ goto out;
+ }
+ if ((mpt3sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
+ sas_iounit_pg1, sz))) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -ENXIO;
+ goto out;
+ }
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+ MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -EIO;
+ goto out;
+ }
+
+ for (i = 0; i < ioc->sas_hba.num_phys; i++) {
+ if (phy->number != i) {
+ sas_iounit_pg1->PhyData[i].MaxMinLinkRate =
+ (ioc->sas_hba.phy[i].phy->minimum_linkrate +
+ (ioc->sas_hba.phy[i].phy->maximum_linkrate << 4));
+ } else {
+ sas_iounit_pg1->PhyData[i].MaxMinLinkRate =
+ (rates->minimum_linkrate +
+ (rates->maximum_linkrate << 4));
+ }
+ }
+
+ if (mpt3sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1,
+ sz)) {
+ pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
+ ioc->name, __FILE__, __LINE__, __func__);
+ rc = -ENXIO;
+ goto out;
+ }
+
+ /* link reset */
+ _transport_phy_reset(phy, 0);
+
+ /* read phy page 0, then update the rates in the sas transport phy */
+ if (!mpt3sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0,
+ phy->number)) {
+ phy->minimum_linkrate = _transport_convert_phy_link_rate(
+ phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
+ phy->maximum_linkrate = _transport_convert_phy_link_rate(
+ phy_pg0.ProgrammedLinkRate >> 4);
+ phy->negotiated_linkrate = _transport_convert_phy_link_rate(
+ phy_pg0.NegotiatedLinkRate &
+ MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
+ }
+
+ out:
+ kfree(sas_iounit_pg1);
+ return rc;
+}
+
+/**
+ * _transport_smp_handler - transport portal for smp passthru
+ * @shost: shost object
+ * @rphy: sas transport rphy object
+ * @req:
+ *
+ * This used primarily for smp_utils.
+ * Example:
+ * smp_rep_general /sys/class/bsg/expander-5:0
+ */
+static int
+_transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
+ struct request *req)
+{
+ struct MPT3SAS_ADAPTER *ioc = shost_priv(shost);
+ Mpi2SmpPassthroughRequest_t *mpi_request;
+ Mpi2SmpPassthroughReply_t *mpi_reply;
+ int rc, i;
+ u16 smid;
+ u32 ioc_state;
+ unsigned long timeleft;
+ void *psge;
+ u8 issue_reset = 0;
+ dma_addr_t dma_addr_in = 0;
+ dma_addr_t dma_addr_out = 0;
+ dma_addr_t pci_dma_in = 0;
+ dma_addr_t pci_dma_out = 0;
+ void *pci_addr_in = NULL;
+ void *pci_addr_out = NULL;
+ u16 wait_state_count;
+ struct request *rsp = req->next_rq;
+ struct bio_vec *bvec = NULL;
+
+ if (!rsp) {
+ pr_err(MPT3SAS_FMT "%s: the smp response space is missing\n",
+ ioc->name, __func__);
+ return -EINVAL;
+ }
+
+ if (ioc->shost_recovery || ioc->pci_error_recovery) {
+ pr_info(MPT3SAS_FMT "%s: host reset in progress!\n",
+ __func__, ioc->name);
+ return -EFAULT;
+ }
+
+ rc = mutex_lock_interruptible(&ioc->transport_cmds.mutex);
+ if (rc)
+ return rc;
+
+ if (ioc->transport_cmds.status != MPT3_CMD_NOT_USED) {
+ pr_err(MPT3SAS_FMT "%s: transport_cmds in use\n", ioc->name,
+ __func__);
+ rc = -EAGAIN;
+ goto out;
+ }
+ ioc->transport_cmds.status = MPT3_CMD_PENDING;
+
+ /* Check if the request is split across multiple segments */
+ if (req->bio->bi_vcnt > 1) {
+ u32 offset = 0;
+
+ /* Allocate memory and copy the request */
+ pci_addr_out = pci_alloc_consistent(ioc->pdev,
+ blk_rq_bytes(req), &pci_dma_out);
+ if (!pci_addr_out) {
+ pr_info(MPT3SAS_FMT "%s(): PCI Addr out = NULL\n",
+ ioc->name, __func__);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ bio_for_each_segment(bvec, req->bio, i) {
+ memcpy(pci_addr_out + offset,
+ page_address(bvec->bv_page) + bvec->bv_offset,
+ bvec->bv_len);
+ offset += bvec->bv_len;
+ }
+ } else {
+ dma_addr_out = pci_map_single(ioc->pdev, bio_data(req->bio),
+ blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL);
+ if (!dma_addr_out) {
+ pr_info(MPT3SAS_FMT "%s(): DMA Addr out = NULL\n",
+ ioc->name, __func__);
+ rc = -ENOMEM;
+ goto free_pci;
+ }
+ }
+
+ /* Check if the response needs to be populated across
+ * multiple segments */
+ if (rsp->bio->bi_vcnt > 1) {
+ pci_addr_in = pci_alloc_consistent(ioc->pdev, blk_rq_bytes(rsp),
+ &pci_dma_in);
+ if (!pci_addr_in) {
+ pr_info(MPT3SAS_FMT "%s(): PCI Addr in = NULL\n",
+ ioc->name, __func__);
+ rc = -ENOMEM;
+ goto unmap;
+ }
+ } else {
+ dma_addr_in = pci_map_single(ioc->pdev, bio_data(rsp->bio),
+ blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL);
+ if (!dma_addr_in) {
+ pr_info(MPT3SAS_FMT "%s(): DMA Addr in = NULL\n",
+ ioc->name, __func__);
+ rc = -ENOMEM;
+ goto unmap;
+ }
+ }
+
+ wait_state_count = 0;
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) {
+ if (wait_state_count++ == 10) {
+ pr_err(MPT3SAS_FMT
+ "%s: failed due to ioc not operational\n",
+ ioc->name, __func__);
+ rc = -EFAULT;
+ goto unmap;
+ }
+ ssleep(1);
+ ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
+ pr_info(MPT3SAS_FMT
+ "%s: waiting for operational state(count=%d)\n",
+ ioc->name, __func__, wait_state_count);
+ }
+ if (wait_state_count)
+ pr_info(MPT3SAS_FMT "%s: ioc is operational\n",
+ ioc->name, __func__);
+
+ smid = mpt3sas_base_get_smid(ioc, ioc->transport_cb_idx);
+ if (!smid) {
+ pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ rc = -EAGAIN;
+ goto unmap;
+ }
+
+ rc = 0;
+ mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
+ ioc->transport_cmds.smid = smid;
+
+ memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t));
+ mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
+ mpi_request->PhysicalPort = 0xFF;
+ mpi_request->SASAddress = (rphy) ?
+ cpu_to_le64(rphy->identify.sas_address) :
+ cpu_to_le64(ioc->sas_hba.sas_address);
+ mpi_request->RequestDataLength = cpu_to_le16(blk_rq_bytes(req) - 4);
+ psge = &mpi_request->SGL;
+
+ if (req->bio->bi_vcnt > 1)
+ ioc->build_sg(ioc, psge, pci_dma_out, (blk_rq_bytes(req) - 4),
+ pci_dma_in, (blk_rq_bytes(rsp) + 4));
+ else
+ ioc->build_sg(ioc, psge, dma_addr_out, (blk_rq_bytes(req) - 4),
+ dma_addr_in, (blk_rq_bytes(rsp) + 4));
+
+ dtransportprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s - sending smp request\n", ioc->name, __func__));
+
+ init_completion(&ioc->transport_cmds.done);
+ mpt3sas_base_put_smid_default(ioc, smid);
+ timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done,
+ 10*HZ);
+
+ if (!(ioc->transport_cmds.status & MPT3_CMD_COMPLETE)) {
+ pr_err(MPT3SAS_FMT "%s : timeout\n",
+ __func__, ioc->name);
+ _debug_dump_mf(mpi_request,
+ sizeof(Mpi2SmpPassthroughRequest_t)/4);
+ if (!(ioc->transport_cmds.status & MPT3_CMD_RESET))
+ issue_reset = 1;
+ goto issue_host_reset;
+ }
+
+ dtransportprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s - complete\n", ioc->name, __func__));
+
+ if (ioc->transport_cmds.status & MPT3_CMD_REPLY_VALID) {
+
+ mpi_reply = ioc->transport_cmds.reply;
+
+ dtransportprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s - reply data transfer size(%d)\n",
+ ioc->name, __func__,
+ le16_to_cpu(mpi_reply->ResponseDataLength)));
+
+ memcpy(req->sense, mpi_reply, sizeof(*mpi_reply));
+ req->sense_len = sizeof(*mpi_reply);
+ req->resid_len = 0;
+ rsp->resid_len -=
+ le16_to_cpu(mpi_reply->ResponseDataLength);
+
+ /* check if the resp needs to be copied from the allocated
+ * pci mem */
+ if (rsp->bio->bi_vcnt > 1) {
+ u32 offset = 0;
+ u32 bytes_to_copy =
+ le16_to_cpu(mpi_reply->ResponseDataLength);
+ bio_for_each_segment(bvec, rsp->bio, i) {
+ if (bytes_to_copy <= bvec->bv_len) {
+ memcpy(page_address(bvec->bv_page) +
+ bvec->bv_offset, pci_addr_in +
+ offset, bytes_to_copy);
+ break;
+ } else {
+ memcpy(page_address(bvec->bv_page) +
+ bvec->bv_offset, pci_addr_in +
+ offset, bvec->bv_len);
+ bytes_to_copy -= bvec->bv_len;
+ }
+ offset += bvec->bv_len;
+ }
+ }
+ } else {
+ dtransportprintk(ioc, pr_info(MPT3SAS_FMT
+ "%s - no reply\n", ioc->name, __func__));
+ rc = -ENXIO;
+ }
+
+ issue_host_reset:
+ if (issue_reset) {
+ mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP,
+ FORCE_BIG_HAMMER);
+ rc = -ETIMEDOUT;
+ }
+
+ unmap:
+ if (dma_addr_out)
+ pci_unmap_single(ioc->pdev, dma_addr_out, blk_rq_bytes(req),
+ PCI_DMA_BIDIRECTIONAL);
+ if (dma_addr_in)
+ pci_unmap_single(ioc->pdev, dma_addr_in, blk_rq_bytes(rsp),
+ PCI_DMA_BIDIRECTIONAL);
+
+ free_pci:
+ if (pci_addr_out)
+ pci_free_consistent(ioc->pdev, blk_rq_bytes(req), pci_addr_out,
+ pci_dma_out);
+
+ if (pci_addr_in)
+ pci_free_consistent(ioc->pdev, blk_rq_bytes(rsp), pci_addr_in,
+ pci_dma_in);
+
+ out:
+ ioc->transport_cmds.status = MPT3_CMD_NOT_USED;
+ mutex_unlock(&ioc->transport_cmds.mutex);
+ return rc;
+}
+
+struct sas_function_template mpt3sas_transport_functions = {
+ .get_linkerrors = _transport_get_linkerrors,
+ .get_enclosure_identifier = _transport_get_enclosure_identifier,
+ .get_bay_identifier = _transport_get_bay_identifier,
+ .phy_reset = _transport_phy_reset,
+ .phy_enable = _transport_phy_enable,
+ .set_phy_speed = _transport_phy_speed,
+ .smp_handler = _transport_smp_handler,
+};
+
+struct scsi_transport_template *mpt3sas_transport_template;
diff --git a/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c b/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c
new file mode 100644
index 00000000000..da6c5f25749
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c
@@ -0,0 +1,434 @@
+/*
+ * This module provides common API to set Diagnostic trigger for MPT
+ * (Message Passing Technology) based controllers
+ *
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_trigger_diag.c
+ * Copyright (C) 2012 LSI Corporation
+ * (mailto:DL-MPTFusionLinux@lsi.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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/compat.h>
+#include <linux/poll.h>
+
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include "mpt3sas_base.h"
+
+/**
+ * _mpt3sas_raise_sigio - notifiy app
+ * @ioc: per adapter object
+ * @event_data:
+ */
+static void
+_mpt3sas_raise_sigio(struct MPT3SAS_ADAPTER *ioc,
+ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data)
+{
+ Mpi2EventNotificationReply_t *mpi_reply;
+ u16 sz, event_data_sz;
+ unsigned long flags;
+
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n",
+ ioc->name, __func__));
+
+ sz = offsetof(Mpi2EventNotificationReply_t, EventData) +
+ sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T) + 4;
+ mpi_reply = kzalloc(sz, GFP_KERNEL);
+ if (!mpi_reply)
+ goto out;
+ mpi_reply->Event = cpu_to_le16(MPI3_EVENT_DIAGNOSTIC_TRIGGER_FIRED);
+ event_data_sz = (sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T) + 4) / 4;
+ mpi_reply->EventDataLength = cpu_to_le16(event_data_sz);
+ memcpy(&mpi_reply->EventData, event_data,
+ sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T));
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: add to driver event log\n",
+ ioc->name, __func__));
+ mpt3sas_ctl_add_to_event_log(ioc, mpi_reply);
+ kfree(mpi_reply);
+ out:
+
+ /* clearing the diag_trigger_active flag */
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: clearing diag_trigger_active flag\n",
+ ioc->name, __func__));
+ ioc->diag_trigger_active = 0;
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name,
+ __func__));
+}
+
+/**
+ * mpt3sas_process_trigger_data - process the event data for the trigger
+ * @ioc: per adapter object
+ * @event_data:
+ */
+void
+mpt3sas_process_trigger_data(struct MPT3SAS_ADAPTER *ioc,
+ struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data)
+{
+ u8 issue_reset = 0;
+
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: enter\n",
+ ioc->name, __func__));
+
+ /* release the diag buffer trace */
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_RELEASED) == 0) {
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: release trace diag buffer\n", ioc->name, __func__));
+ mpt3sas_send_diag_release(ioc, MPI2_DIAG_BUF_TYPE_TRACE,
+ &issue_reset);
+ }
+
+ _mpt3sas_raise_sigio(ioc, event_data);
+
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name,
+ __func__));
+}
+
+/**
+ * mpt3sas_trigger_master - Master trigger handler
+ * @ioc: per adapter object
+ * @trigger_bitmask:
+ *
+ */
+void
+mpt3sas_trigger_master(struct MPT3SAS_ADAPTER *ioc, u32 trigger_bitmask)
+{
+ struct SL_WH_TRIGGERS_EVENT_DATA_T event_data;
+ unsigned long flags;
+ u8 found_match = 0;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+
+ if (trigger_bitmask & MASTER_TRIGGER_FW_FAULT ||
+ trigger_bitmask & MASTER_TRIGGER_ADAPTER_RESET)
+ goto by_pass_checks;
+
+ /* check to see if trace buffers are currently registered */
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return;
+ }
+
+ /* check to see if trace buffers are currently released */
+ if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_RELEASED) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return;
+ }
+
+ by_pass_checks:
+
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: enter - trigger_bitmask = 0x%08x\n",
+ ioc->name, __func__, trigger_bitmask));
+
+ /* don't send trigger if an trigger is currently active */
+ if (ioc->diag_trigger_active) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ goto out;
+ }
+
+ /* check for the trigger condition */
+ if (ioc->diag_trigger_master.MasterData & trigger_bitmask) {
+ found_match = 1;
+ ioc->diag_trigger_active = 1;
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: setting diag_trigger_active flag\n",
+ ioc->name, __func__));
+ }
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+
+ if (!found_match)
+ goto out;
+
+ memset(&event_data, 0, sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T));
+ event_data.trigger_type = MPT3SAS_TRIGGER_MASTER;
+ event_data.u.master.MasterData = trigger_bitmask;
+
+ if (trigger_bitmask & MASTER_TRIGGER_FW_FAULT ||
+ trigger_bitmask & MASTER_TRIGGER_ADAPTER_RESET)
+ _mpt3sas_raise_sigio(ioc, &event_data);
+ else
+ mpt3sas_send_trigger_data_event(ioc, &event_data);
+
+ out:
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name,
+ __func__));
+}
+
+/**
+ * mpt3sas_trigger_event - Event trigger handler
+ * @ioc: per adapter object
+ * @event:
+ * @log_entry_qualifier:
+ *
+ */
+void
+mpt3sas_trigger_event(struct MPT3SAS_ADAPTER *ioc, u16 event,
+ u16 log_entry_qualifier)
+{
+ struct SL_WH_TRIGGERS_EVENT_DATA_T event_data;
+ struct SL_WH_EVENT_TRIGGER_T *event_trigger;
+ int i;
+ unsigned long flags;
+ u8 found_match;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+
+ /* check to see if trace buffers are currently registered */
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return;
+ }
+
+ /* check to see if trace buffers are currently released */
+ if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_RELEASED) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return;
+ }
+
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: enter - event = 0x%04x, log_entry_qualifier = 0x%04x\n",
+ ioc->name, __func__, event, log_entry_qualifier));
+
+ /* don't send trigger if an trigger is currently active */
+ if (ioc->diag_trigger_active) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ goto out;
+ }
+
+ /* check for the trigger condition */
+ event_trigger = ioc->diag_trigger_event.EventTriggerEntry;
+ for (i = 0 , found_match = 0; i < ioc->diag_trigger_event.ValidEntries
+ && !found_match; i++, event_trigger++) {
+ if (event_trigger->EventValue != event)
+ continue;
+ if (event == MPI2_EVENT_LOG_ENTRY_ADDED) {
+ if (event_trigger->LogEntryQualifier ==
+ log_entry_qualifier)
+ found_match = 1;
+ continue;
+ }
+ found_match = 1;
+ ioc->diag_trigger_active = 1;
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: setting diag_trigger_active flag\n",
+ ioc->name, __func__));
+ }
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+
+ if (!found_match)
+ goto out;
+
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: setting diag_trigger_active flag\n",
+ ioc->name, __func__));
+ memset(&event_data, 0, sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T));
+ event_data.trigger_type = MPT3SAS_TRIGGER_EVENT;
+ event_data.u.event.EventValue = event;
+ event_data.u.event.LogEntryQualifier = log_entry_qualifier;
+ mpt3sas_send_trigger_data_event(ioc, &event_data);
+ out:
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name,
+ __func__));
+}
+
+/**
+ * mpt3sas_trigger_scsi - SCSI trigger handler
+ * @ioc: per adapter object
+ * @sense_key:
+ * @asc:
+ * @ascq:
+ *
+ */
+void
+mpt3sas_trigger_scsi(struct MPT3SAS_ADAPTER *ioc, u8 sense_key, u8 asc,
+ u8 ascq)
+{
+ struct SL_WH_TRIGGERS_EVENT_DATA_T event_data;
+ struct SL_WH_SCSI_TRIGGER_T *scsi_trigger;
+ int i;
+ unsigned long flags;
+ u8 found_match;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+
+ /* check to see if trace buffers are currently registered */
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return;
+ }
+
+ /* check to see if trace buffers are currently released */
+ if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_RELEASED) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return;
+ }
+
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: enter - sense_key = 0x%02x, asc = 0x%02x, ascq = 0x%02x\n",
+ ioc->name, __func__, sense_key, asc, ascq));
+
+ /* don't send trigger if an trigger is currently active */
+ if (ioc->diag_trigger_active) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ goto out;
+ }
+
+ /* check for the trigger condition */
+ scsi_trigger = ioc->diag_trigger_scsi.SCSITriggerEntry;
+ for (i = 0 , found_match = 0; i < ioc->diag_trigger_scsi.ValidEntries
+ && !found_match; i++, scsi_trigger++) {
+ if (scsi_trigger->SenseKey != sense_key)
+ continue;
+ if (!(scsi_trigger->ASC == 0xFF || scsi_trigger->ASC == asc))
+ continue;
+ if (!(scsi_trigger->ASCQ == 0xFF || scsi_trigger->ASCQ == ascq))
+ continue;
+ found_match = 1;
+ ioc->diag_trigger_active = 1;
+ }
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+
+ if (!found_match)
+ goto out;
+
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: setting diag_trigger_active flag\n",
+ ioc->name, __func__));
+ memset(&event_data, 0, sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T));
+ event_data.trigger_type = MPT3SAS_TRIGGER_SCSI;
+ event_data.u.scsi.SenseKey = sense_key;
+ event_data.u.scsi.ASC = asc;
+ event_data.u.scsi.ASCQ = ascq;
+ mpt3sas_send_trigger_data_event(ioc, &event_data);
+ out:
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name,
+ __func__));
+}
+
+/**
+ * mpt3sas_trigger_mpi - MPI trigger handler
+ * @ioc: per adapter object
+ * @ioc_status:
+ * @loginfo:
+ *
+ */
+void
+mpt3sas_trigger_mpi(struct MPT3SAS_ADAPTER *ioc, u16 ioc_status, u32 loginfo)
+{
+ struct SL_WH_TRIGGERS_EVENT_DATA_T event_data;
+ struct SL_WH_MPI_TRIGGER_T *mpi_trigger;
+ int i;
+ unsigned long flags;
+ u8 found_match;
+
+ spin_lock_irqsave(&ioc->diag_trigger_lock, flags);
+
+ /* check to see if trace buffers are currently registered */
+ if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_REGISTERED) == 0) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return;
+ }
+
+ /* check to see if trace buffers are currently released */
+ if (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] &
+ MPT3_DIAG_BUFFER_IS_RELEASED) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ return;
+ }
+
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: enter - ioc_status = 0x%04x, loginfo = 0x%08x\n",
+ ioc->name, __func__, ioc_status, loginfo));
+
+ /* don't send trigger if an trigger is currently active */
+ if (ioc->diag_trigger_active) {
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+ goto out;
+ }
+
+ /* check for the trigger condition */
+ mpi_trigger = ioc->diag_trigger_mpi.MPITriggerEntry;
+ for (i = 0 , found_match = 0; i < ioc->diag_trigger_mpi.ValidEntries
+ && !found_match; i++, mpi_trigger++) {
+ if (mpi_trigger->IOCStatus != ioc_status)
+ continue;
+ if (!(mpi_trigger->IocLogInfo == 0xFFFFFFFF ||
+ mpi_trigger->IocLogInfo == loginfo))
+ continue;
+ found_match = 1;
+ ioc->diag_trigger_active = 1;
+ }
+ spin_unlock_irqrestore(&ioc->diag_trigger_lock, flags);
+
+ if (!found_match)
+ goto out;
+
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT
+ "%s: setting diag_trigger_active flag\n",
+ ioc->name, __func__));
+ memset(&event_data, 0, sizeof(struct SL_WH_TRIGGERS_EVENT_DATA_T));
+ event_data.trigger_type = MPT3SAS_TRIGGER_MPI;
+ event_data.u.mpi.IOCStatus = ioc_status;
+ event_data.u.mpi.IocLogInfo = loginfo;
+ mpt3sas_send_trigger_data_event(ioc, &event_data);
+ out:
+ dTriggerDiagPrintk(ioc, pr_info(MPT3SAS_FMT "%s: exit\n", ioc->name,
+ __func__));
+}
diff --git a/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.h b/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.h
new file mode 100644
index 00000000000..a10c3090739
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_trigger_diag.h
@@ -0,0 +1,193 @@
+/*
+ * This is the Fusion MPT base driver providing common API layer interface
+ * to set Diagnostic triggers for MPT (Message Passing Technology) based
+ * controllers
+ *
+ * This code is based on drivers/scsi/mpt3sas/mpt3sas_base.h
+ * Copyright (C) 2012 LSI Corporation
+ * (mailto:DL-MPTFusionLinux@lsi.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.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+ /* Diagnostic Trigger Configuration Data Structures */
+
+#ifndef MPT3SAS_TRIGGER_DIAG_H_INCLUDED
+#define MPT3SAS_TRIGGER_DIAG_H_INCLUDED
+
+/* limitation on number of entries */
+#define NUM_VALID_ENTRIES (20)
+
+/* trigger types */
+#define MPT3SAS_TRIGGER_MASTER (1)
+#define MPT3SAS_TRIGGER_EVENT (2)
+#define MPT3SAS_TRIGGER_SCSI (3)
+#define MPT3SAS_TRIGGER_MPI (4)
+
+/* trigger names */
+#define MASTER_TRIGGER_FILE_NAME "diag_trigger_master"
+#define EVENT_TRIGGERS_FILE_NAME "diag_trigger_event"
+#define SCSI_TRIGGERS_FILE_NAME "diag_trigger_scsi"
+#define MPI_TRIGGER_FILE_NAME "diag_trigger_mpi"
+
+/* master trigger bitmask */
+#define MASTER_TRIGGER_FW_FAULT (0x00000001)
+#define MASTER_TRIGGER_ADAPTER_RESET (0x00000002)
+#define MASTER_TRIGGER_TASK_MANAGMENT (0x00000004)
+#define MASTER_TRIGGER_DEVICE_REMOVAL (0x00000008)
+
+/* fake firmware event for tigger */
+#define MPI3_EVENT_DIAGNOSTIC_TRIGGER_FIRED (0x6E)
+
+/**
+ * MasterTrigger is a single U32 passed to/from sysfs.
+ *
+ * Bit Flags (enables) include:
+ * 1. FW Faults
+ * 2. Adapter Reset issued by driver
+ * 3. TMs
+ * 4. Device Remove Event sent by FW
+ */
+
+struct SL_WH_MASTER_TRIGGER_T {
+ uint32_t MasterData;
+};
+
+/**
+ * struct SL_WH_EVENT_TRIGGER_T - Definition of an event trigger element
+ * @EventValue: Event Code to trigger on
+ * @LogEntryQualifier: Type of FW event that logged (Log Entry Added Event only)
+ *
+ * Defines an event that should induce a DIAG_TRIGGER driver event if observed.
+ */
+struct SL_WH_EVENT_TRIGGER_T {
+ uint16_t EventValue;
+ uint16_t LogEntryQualifier;
+};
+
+/**
+ * struct SL_WH_EVENT_TRIGGERS_T - Structure passed to/from sysfs containing a
+ * list of Event Triggers to be monitored for.
+ * @ValidEntries: Number of _SL_WH_EVENT_TRIGGER_T structures contained in this
+ * structure.
+ * @EventTriggerEntry: List of Event trigger elements.
+ *
+ * This binary structure is transferred via sysfs to get/set Event Triggers
+ * in the Linux Driver.
+ */
+
+struct SL_WH_EVENT_TRIGGERS_T {
+ uint32_t ValidEntries;
+ struct SL_WH_EVENT_TRIGGER_T EventTriggerEntry[NUM_VALID_ENTRIES];
+};
+
+/**
+ * struct SL_WH_SCSI_TRIGGER_T - Definition of a SCSI trigger element
+ * @ASCQ: Additional Sense Code Qualifier. Can be specific or 0xFF for
+ * wildcard.
+ * @ASC: Additional Sense Code. Can be specific or 0xFF for wildcard
+ * @SenseKey: SCSI Sense Key
+ *
+ * Defines a sense key (single or many variants) that should induce a
+ * DIAG_TRIGGER driver event if observed.
+ */
+struct SL_WH_SCSI_TRIGGER_T {
+ U8 ASCQ;
+ U8 ASC;
+ U8 SenseKey;
+ U8 Reserved;
+};
+
+/**
+ * struct SL_WH_SCSI_TRIGGERS_T - Structure passed to/from sysfs containing a
+ * list of SCSI sense codes that should trigger a DIAG_SERVICE event when
+ * observed.
+ * @ValidEntries: Number of _SL_WH_SCSI_TRIGGER_T structures contained in this
+ * structure.
+ * @SCSITriggerEntry: List of SCSI Sense Code trigger elements.
+ *
+ * This binary structure is transferred via sysfs to get/set SCSI Sense Code
+ * Triggers in the Linux Driver.
+ */
+struct SL_WH_SCSI_TRIGGERS_T {
+ uint32_t ValidEntries;
+ struct SL_WH_SCSI_TRIGGER_T SCSITriggerEntry[NUM_VALID_ENTRIES];
+};
+
+/**
+ * struct SL_WH_MPI_TRIGGER_T - Definition of an MPI trigger element
+ * @IOCStatus: MPI IOCStatus
+ * @IocLogInfo: MPI IocLogInfo. Can be specific or 0xFFFFFFFF for wildcard
+ *
+ * Defines a MPI IOCStatus/IocLogInfo pair that should induce a DIAG_TRIGGER
+ * driver event if observed.
+ */
+struct SL_WH_MPI_TRIGGER_T {
+ uint16_t IOCStatus;
+ uint16_t Reserved;
+ uint32_t IocLogInfo;
+};
+
+/**
+ * struct SL_WH_MPI_TRIGGERS_T - Structure passed to/from sysfs containing a
+ * list of MPI IOCStatus/IocLogInfo pairs that should trigger a DIAG_SERVICE
+ * event when observed.
+ * @ValidEntries: Number of _SL_WH_MPI_TRIGGER_T structures contained in this
+ * structure.
+ * @MPITriggerEntry: List of MPI IOCStatus/IocLogInfo trigger elements.
+ *
+ * This binary structure is transferred via sysfs to get/set MPI Error Triggers
+ * in the Linux Driver.
+ */
+struct SL_WH_MPI_TRIGGERS_T {
+ uint32_t ValidEntries;
+ struct SL_WH_MPI_TRIGGER_T MPITriggerEntry[NUM_VALID_ENTRIES];
+};
+
+/**
+ * struct SL_WH_TRIGGERS_EVENT_DATA_T - event data for trigger
+ * @trigger_type: trigger type (see MPT3SAS_TRIGGER_XXXX)
+ * @u: trigger condition that caused trigger to be sent
+ */
+struct SL_WH_TRIGGERS_EVENT_DATA_T {
+ uint32_t trigger_type;
+ union {
+ struct SL_WH_MASTER_TRIGGER_T master;
+ struct SL_WH_EVENT_TRIGGER_T event;
+ struct SL_WH_SCSI_TRIGGER_T scsi;
+ struct SL_WH_MPI_TRIGGER_T mpi;
+ } u;
+};
+#endif /* MPT3SAS_TRIGGER_DIAG_H_INCLUDED */
diff --git a/drivers/scsi/mvsas/mv_94xx.h b/drivers/scsi/mvsas/mv_94xx.h
index 8f7eb4f2114..487aa6f9741 100644
--- a/drivers/scsi/mvsas/mv_94xx.h
+++ b/drivers/scsi/mvsas/mv_94xx.h
@@ -258,21 +258,11 @@ enum sas_sata_phy_regs {
#define SPI_ADDR_VLD_94XX (1U << 1)
#define SPI_CTRL_SpiStart_94XX (1U << 0)
-#define mv_ffc(x) ffz(x)
-
static inline int
mv_ffc64(u64 v)
{
- int i;
- i = mv_ffc((u32)v);
- if (i >= 0)
- return i;
- i = mv_ffc((u32)(v>>32));
-
- if (i != 0)
- return 32 + i;
-
- return -1;
+ u64 x = ~v;
+ return x ? __ffs64(x) : -1;
}
#define r_reg_set_enable(i) \
diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h
index c04a4f5b597..da249553858 100644
--- a/drivers/scsi/mvsas/mv_sas.h
+++ b/drivers/scsi/mvsas/mv_sas.h
@@ -69,7 +69,7 @@ extern struct kmem_cache *mvs_task_list_cache;
#define DEV_IS_EXPANDER(type) \
((type == EDGE_DEV) || (type == FANOUT_DEV))
-#define bit(n) ((u32)1 << n)
+#define bit(n) ((u64)1 << n)
#define for_each_phy(__lseq_mask, __mc, __lseq) \
for ((__mc) = (__lseq_mask), (__lseq) = 0; \
diff --git a/drivers/scsi/osd/osd_uld.c b/drivers/scsi/osd/osd_uld.c
index d4ed9eb5265..43754176a7b 100644
--- a/drivers/scsi/osd/osd_uld.c
+++ b/drivers/scsi/osd/osd_uld.c
@@ -97,9 +97,37 @@ struct osd_dev_handle {
static DEFINE_IDA(osd_minor_ida);
+/*
+ * scsi sysfs attribute operations
+ */
+static ssize_t osdname_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct osd_uld_device *ould = container_of(dev, struct osd_uld_device,
+ class_dev);
+ return sprintf(buf, "%s\n", ould->odi.osdname);
+}
+
+static ssize_t systemid_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct osd_uld_device *ould = container_of(dev, struct osd_uld_device,
+ class_dev);
+
+ memcpy(buf, ould->odi.systemid, ould->odi.systemid_len);
+ return ould->odi.systemid_len;
+}
+
+static struct device_attribute osd_uld_attrs[] = {
+ __ATTR(osdname, S_IRUGO, osdname_show, NULL),
+ __ATTR(systemid, S_IRUGO, systemid_show, NULL),
+ __ATTR_NULL,
+};
+
static struct class osd_uld_class = {
.owner = THIS_MODULE,
.name = "scsi_osd",
+ .dev_attrs = osd_uld_attrs,
};
/*
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 1c28215f8be..83d798428c1 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -1615,8 +1615,7 @@ qla2x00_terminate_rport_io(struct fc_rport *rport)
* At this point all fcport's software-states are cleared. Perform any
* final cleanup of firmware resources (PCBs and XCBs).
*/
- if (fcport->loop_id != FC_NO_LOOP_ID &&
- !test_bit(UNLOADING, &fcport->vha->dpc_flags)) {
+ if (fcport->loop_id != FC_NO_LOOP_ID) {
if (IS_FWI2_CAPABLE(fcport->vha->hw))
fcport->vha->hw->isp_ops->fabric_logout(fcport->vha,
fcport->loop_id, fcport->d_id.b.domain,
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index 2f9bddd3c61..9f34dedcdad 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -219,7 +219,8 @@ qla24xx_proc_fcp_prio_cfg_cmd(struct fc_bsg_job *bsg_job)
break;
}
exit_fcp_prio_cfg:
- bsg_job->job_done(bsg_job);
+ if (!ret)
+ bsg_job->job_done(bsg_job);
return ret;
}
@@ -741,9 +742,8 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job)
if (qla81xx_get_port_config(vha, config)) {
ql_log(ql_log_warn, vha, 0x701f,
"Get port config failed.\n");
- bsg_job->reply->result = (DID_ERROR << 16);
rval = -EPERM;
- goto done_free_dma_req;
+ goto done_free_dma_rsp;
}
ql_dbg(ql_dbg_user, vha, 0x70c0,
@@ -761,9 +761,8 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job)
new_config, elreq.options);
if (rval) {
- bsg_job->reply->result = (DID_ERROR << 16);
rval = -EPERM;
- goto done_free_dma_req;
+ goto done_free_dma_rsp;
}
type = "FC_BSG_HST_VENDOR_LOOPBACK";
@@ -795,9 +794,8 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job)
"MPI reset failed.\n");
}
- bsg_job->reply->result = (DID_ERROR << 16);
rval = -EIO;
- goto done_free_dma_req;
+ goto done_free_dma_rsp;
}
} else {
type = "FC_BSG_HST_VENDOR_LOOPBACK";
@@ -812,34 +810,27 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job)
ql_log(ql_log_warn, vha, 0x702c,
"Vendor request %s failed.\n", type);
- fw_sts_ptr = ((uint8_t *)bsg_job->req->sense) +
- sizeof(struct fc_bsg_reply);
-
- memcpy(fw_sts_ptr, response, sizeof(response));
- fw_sts_ptr += sizeof(response);
- *fw_sts_ptr = command_sent;
rval = 0;
bsg_job->reply->result = (DID_ERROR << 16);
+ bsg_job->reply->reply_payload_rcv_len = 0;
} else {
ql_dbg(ql_dbg_user, vha, 0x702d,
"Vendor request %s completed.\n", type);
-
- bsg_job->reply_len = sizeof(struct fc_bsg_reply) +
- sizeof(response) + sizeof(uint8_t);
- bsg_job->reply->reply_payload_rcv_len =
- bsg_job->reply_payload.payload_len;
- fw_sts_ptr = ((uint8_t *)bsg_job->req->sense) +
- sizeof(struct fc_bsg_reply);
- memcpy(fw_sts_ptr, response, sizeof(response));
- fw_sts_ptr += sizeof(response);
- *fw_sts_ptr = command_sent;
- bsg_job->reply->result = DID_OK;
+ bsg_job->reply->result = (DID_OK << 16);
sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
bsg_job->reply_payload.sg_cnt, rsp_data,
rsp_data_len);
}
- bsg_job->job_done(bsg_job);
+ bsg_job->reply_len = sizeof(struct fc_bsg_reply) +
+ sizeof(response) + sizeof(uint8_t);
+ fw_sts_ptr = ((uint8_t *)bsg_job->req->sense) +
+ sizeof(struct fc_bsg_reply);
+ memcpy(fw_sts_ptr, response, sizeof(response));
+ fw_sts_ptr += sizeof(response);
+ *fw_sts_ptr = command_sent;
+
+done_free_dma_rsp:
dma_free_coherent(&ha->pdev->dev, rsp_data_len,
rsp_data, rsp_data_dma);
done_free_dma_req:
@@ -853,6 +844,8 @@ done_unmap_req_sg:
dma_unmap_sg(&ha->pdev->dev,
bsg_job->request_payload.sg_list,
bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ if (!rval)
+ bsg_job->job_done(bsg_job);
return rval;
}
@@ -877,16 +870,15 @@ qla84xx_reset(struct fc_bsg_job *bsg_job)
if (rval) {
ql_log(ql_log_warn, vha, 0x7030,
"Vendor request 84xx reset failed.\n");
- rval = 0;
- bsg_job->reply->result = (DID_ERROR << 16);
+ rval = (DID_ERROR << 16);
} else {
ql_dbg(ql_dbg_user, vha, 0x7031,
"Vendor request 84xx reset completed.\n");
bsg_job->reply->result = DID_OK;
+ bsg_job->job_done(bsg_job);
}
- bsg_job->job_done(bsg_job);
return rval;
}
@@ -976,8 +968,7 @@ qla84xx_updatefw(struct fc_bsg_job *bsg_job)
ql_log(ql_log_warn, vha, 0x7037,
"Vendor request 84xx updatefw failed.\n");
- rval = 0;
- bsg_job->reply->result = (DID_ERROR << 16);
+ rval = (DID_ERROR << 16);
} else {
ql_dbg(ql_dbg_user, vha, 0x7038,
"Vendor request 84xx updatefw completed.\n");
@@ -986,7 +977,6 @@ qla84xx_updatefw(struct fc_bsg_job *bsg_job)
bsg_job->reply->result = DID_OK;
}
- bsg_job->job_done(bsg_job);
dma_pool_free(ha->s_dma_pool, mn, mn_dma);
done_free_fw_buf:
@@ -996,6 +986,8 @@ done_unmap_sg:
dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ if (!rval)
+ bsg_job->job_done(bsg_job);
return rval;
}
@@ -1163,8 +1155,7 @@ qla84xx_mgmt_cmd(struct fc_bsg_job *bsg_job)
ql_log(ql_log_warn, vha, 0x7043,
"Vendor request 84xx mgmt failed.\n");
- rval = 0;
- bsg_job->reply->result = (DID_ERROR << 16);
+ rval = (DID_ERROR << 16);
} else {
ql_dbg(ql_dbg_user, vha, 0x7044,
@@ -1184,8 +1175,6 @@ qla84xx_mgmt_cmd(struct fc_bsg_job *bsg_job)
}
}
- bsg_job->job_done(bsg_job);
-
done_unmap_sg:
if (mgmt_b)
dma_free_coherent(&ha->pdev->dev, data_len, mgmt_b, mgmt_dma);
@@ -1200,6 +1189,8 @@ done_unmap_sg:
exit_mgmt:
dma_pool_free(ha->s_dma_pool, mn, mn_dma);
+ if (!rval)
+ bsg_job->job_done(bsg_job);
return rval;
}
@@ -1276,9 +1267,7 @@ qla24xx_iidma(struct fc_bsg_job *bsg_job)
fcport->port_name[3], fcport->port_name[4],
fcport->port_name[5], fcport->port_name[6],
fcport->port_name[7], rval, fcport->fp_speed, mb[0], mb[1]);
- rval = 0;
- bsg_job->reply->result = (DID_ERROR << 16);
-
+ rval = (DID_ERROR << 16);
} else {
if (!port_param->mode) {
bsg_job->reply_len = sizeof(struct fc_bsg_reply) +
@@ -1292,9 +1281,9 @@ qla24xx_iidma(struct fc_bsg_job *bsg_job)
}
bsg_job->reply->result = DID_OK;
+ bsg_job->job_done(bsg_job);
}
- bsg_job->job_done(bsg_job);
return rval;
}
@@ -1887,8 +1876,6 @@ qla2x00_process_vendor_specific(struct fc_bsg_job *bsg_job)
return qla24xx_process_bidir_cmd(bsg_job);
default:
- bsg_job->reply->result = (DID_ERROR << 16);
- bsg_job->job_done(bsg_job);
return -ENOSYS;
}
}
@@ -1919,8 +1906,6 @@ qla24xx_bsg_request(struct fc_bsg_job *bsg_job)
ql_dbg(ql_dbg_user, vha, 0x709f,
"BSG: ISP abort active/needed -- cmd=%d.\n",
bsg_job->request->msgcode);
- bsg_job->reply->result = (DID_ERROR << 16);
- bsg_job->job_done(bsg_job);
return -EBUSY;
}
@@ -1943,7 +1928,6 @@ qla24xx_bsg_request(struct fc_bsg_job *bsg_job)
case FC_BSG_RPT_CT:
default:
ql_log(ql_log_warn, vha, 0x705a, "Unsupported BSG request.\n");
- bsg_job->reply->result = ret;
break;
}
return ret;
diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
index 44efe3cc79e..53f9e492f9d 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.c
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -11,7 +11,7 @@
* ----------------------------------------------------------------------
* | Level | Last Value Used | Holes |
* ----------------------------------------------------------------------
- * | Module Init and Probe | 0x0124 | 0x4b,0xba,0xfa |
+ * | Module Init and Probe | 0x0125 | 0x4b,0xba,0xfa |
* | Mailbox commands | 0x114f | 0x111a-0x111b |
* | | | 0x112c-0x112e |
* | | | 0x113a |
@@ -526,8 +526,8 @@ qla25xx_copy_mq(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
ha->max_req_queues : ha->max_rsp_queues;
mq->count = htonl(que_cnt);
for (cnt = 0; cnt < que_cnt; cnt++) {
- reg = (struct device_reg_25xxmq *) ((void *)
- ha->mqiobase + cnt * QLA_QUE_PAGE);
+ reg = (struct device_reg_25xxmq __iomem *)
+ (ha->mqiobase + cnt * QLA_QUE_PAGE);
que_idx = cnt * 4;
mq->qregs[que_idx] = htonl(RD_REG_DWORD(&reg->req_q_in));
mq->qregs[que_idx+1] = htonl(RD_REG_DWORD(&reg->req_q_out));
@@ -2268,7 +2268,7 @@ qla83xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
if (!cnt) {
nxt = fw->code_ram;
- nxt += sizeof(fw->code_ram),
+ nxt += sizeof(fw->code_ram);
nxt += (ha->fw_memory_size - 0x100000 + 1);
goto copy_queue;
} else
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index a9725bf5527..6e7727f46d4 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -2486,9 +2486,9 @@ struct bidi_statistics {
#define QLA_MAX_QUEUES 256
#define ISP_QUE_REG(ha, id) \
((ha->mqenable || IS_QLA83XX(ha)) ? \
- ((void *)(ha->mqiobase) +\
+ ((device_reg_t __iomem *)(ha->mqiobase) +\
(QLA_QUE_PAGE * id)) :\
- ((void *)(ha->iobase)))
+ ((device_reg_t __iomem *)(ha->iobase)))
#define QLA_REQ_QUE_ID(tag) \
((tag < QLA_MAX_QUEUES && tag > 0) ? tag : 0)
#define QLA_DEFAULT_QUE_QOS 5
diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
index 59524aa0ab3..be6d61a89ed 100644
--- a/drivers/scsi/qla2xxx/qla_fw.h
+++ b/drivers/scsi/qla2xxx/qla_fw.h
@@ -1092,6 +1092,27 @@ struct device_reg_24xx {
uint32_t unused_6[2]; /* Gap. */
uint32_t iobase_sdata;
};
+/* RISC-RISC semaphore register PCI offet */
+#define RISC_REGISTER_BASE_OFFSET 0x7010
+#define RISC_REGISTER_WINDOW_OFFET 0x6
+
+/* RISC-RISC semaphore/flag register (risc address 0x7016) */
+
+#define RISC_SEMAPHORE 0x1UL
+#define RISC_SEMAPHORE_WE (RISC_SEMAPHORE << 16)
+#define RISC_SEMAPHORE_CLR (RISC_SEMAPHORE_WE | 0x0UL)
+#define RISC_SEMAPHORE_SET (RISC_SEMAPHORE_WE | RISC_SEMAPHORE)
+
+#define RISC_SEMAPHORE_FORCE 0x8000UL
+#define RISC_SEMAPHORE_FORCE_WE (RISC_SEMAPHORE_FORCE << 16)
+#define RISC_SEMAPHORE_FORCE_CLR (RISC_SEMAPHORE_FORCE_WE | 0x0UL)
+#define RISC_SEMAPHORE_FORCE_SET \
+ (RISC_SEMAPHORE_FORCE_WE | RISC_SEMAPHORE_FORCE)
+
+/* RISC semaphore timeouts (ms) */
+#define TIMEOUT_SEMAPHORE 2500
+#define TIMEOUT_SEMAPHORE_FORCE 2000
+#define TIMEOUT_TOTAL_ELAPSED 4500
/* Trace Control *************************************************************/
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 6acb39785a4..2411d1a12b2 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -416,7 +416,7 @@ extern int qla2x00_request_irqs(struct qla_hw_data *, struct rsp_que *);
extern void qla2x00_free_irqs(scsi_qla_host_t *);
extern int qla2x00_get_data_rate(scsi_qla_host_t *);
-extern char *qla2x00_get_link_speed_str(struct qla_hw_data *);
+extern const char *qla2x00_get_link_speed_str(struct qla_hw_data *, uint16_t);
/*
* Global Function Prototypes in qla_sup.c source file.
@@ -598,7 +598,6 @@ extern void qla82xx_init_flags(struct qla_hw_data *);
/* ISP 8021 hardware related */
extern void qla82xx_set_drv_active(scsi_qla_host_t *);
-extern void qla82xx_crb_win_unlock(struct qla_hw_data *);
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);
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index f4e4bd7c3f4..01efc0e9cc3 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -218,6 +218,9 @@ qla2x00_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport)
memcpy(fcport->port_name, ct_rsp->rsp.ga_nxt.port_name,
WWN_SIZE);
+ fcport->fc4_type = (ct_rsp->rsp.ga_nxt.fc4_types[2] & BIT_0) ?
+ FC4_TYPE_FCP_SCSI : FC4_TYPE_OTHER;
+
if (ct_rsp->rsp.ga_nxt.port_type != NS_N_PORT_TYPE &&
ct_rsp->rsp.ga_nxt.port_type != NS_NL_PORT_TYPE)
fcport->d_id.b.domain = 0xf0;
@@ -1930,6 +1933,9 @@ qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list)
case BIT_11:
list[i].fp_speed = PORT_SPEED_8GB;
break;
+ case BIT_10:
+ list[i].fp_speed = PORT_SPEED_16GB;
+ break;
}
ql_dbg(ql_dbg_disc, vha, 0x205b,
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 48fca47384b..563eee3fa92 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -429,7 +429,7 @@ qla2x00_async_adisc_done(struct scsi_qla_host *vha, fc_port_t *fcport,
/* QLogic ISP2x00 Hardware Support Functions. */
/****************************************************************************/
-int
+static int
qla83xx_nic_core_fw_load(scsi_qla_host_t *vha)
{
int rval = QLA_SUCCESS;
@@ -997,7 +997,7 @@ qla2x00_reset_chip(scsi_qla_host_t *vha)
*
* Returns 0 on success.
*/
-int
+static int
qla81xx_reset_mpi(scsi_qla_host_t *vha)
{
uint16_t mb[4] = {0x1010, 0, 1, 0};
@@ -1095,6 +1095,83 @@ qla24xx_reset_risc(scsi_qla_host_t *vha)
ha->isp_ops->enable_intrs(ha);
}
+static void
+qla25xx_read_risc_sema_reg(scsi_qla_host_t *vha, uint32_t *data)
+{
+ struct device_reg_24xx __iomem *reg = &vha->hw->iobase->isp24;
+
+ WRT_REG_DWORD(&reg->iobase_addr, RISC_REGISTER_BASE_OFFSET);
+ *data = RD_REG_DWORD(&reg->iobase_window + RISC_REGISTER_WINDOW_OFFET);
+
+}
+
+static void
+qla25xx_write_risc_sema_reg(scsi_qla_host_t *vha, uint32_t data)
+{
+ struct device_reg_24xx __iomem *reg = &vha->hw->iobase->isp24;
+
+ WRT_REG_DWORD(&reg->iobase_addr, RISC_REGISTER_BASE_OFFSET);
+ WRT_REG_DWORD(&reg->iobase_window + RISC_REGISTER_WINDOW_OFFET, data);
+}
+
+static void
+qla25xx_manipulate_risc_semaphore(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint32_t wd32 = 0;
+ uint delta_msec = 100;
+ uint elapsed_msec = 0;
+ uint timeout_msec;
+ ulong n;
+
+ if (!IS_QLA25XX(ha) && !IS_QLA2031(ha))
+ return;
+
+attempt:
+ timeout_msec = TIMEOUT_SEMAPHORE;
+ n = timeout_msec / delta_msec;
+ while (n--) {
+ qla25xx_write_risc_sema_reg(vha, RISC_SEMAPHORE_SET);
+ qla25xx_read_risc_sema_reg(vha, &wd32);
+ if (wd32 & RISC_SEMAPHORE)
+ break;
+ msleep(delta_msec);
+ elapsed_msec += delta_msec;
+ if (elapsed_msec > TIMEOUT_TOTAL_ELAPSED)
+ goto force;
+ }
+
+ if (!(wd32 & RISC_SEMAPHORE))
+ goto force;
+
+ if (!(wd32 & RISC_SEMAPHORE_FORCE))
+ goto acquired;
+
+ qla25xx_write_risc_sema_reg(vha, RISC_SEMAPHORE_CLR);
+ timeout_msec = TIMEOUT_SEMAPHORE_FORCE;
+ n = timeout_msec / delta_msec;
+ while (n--) {
+ qla25xx_read_risc_sema_reg(vha, &wd32);
+ if (!(wd32 & RISC_SEMAPHORE_FORCE))
+ break;
+ msleep(delta_msec);
+ elapsed_msec += delta_msec;
+ if (elapsed_msec > TIMEOUT_TOTAL_ELAPSED)
+ goto force;
+ }
+
+ if (wd32 & RISC_SEMAPHORE_FORCE)
+ qla25xx_write_risc_sema_reg(vha, RISC_SEMAPHORE_FORCE_CLR);
+
+ goto attempt;
+
+force:
+ qla25xx_write_risc_sema_reg(vha, RISC_SEMAPHORE_FORCE_SET);
+
+acquired:
+ return;
+}
+
/**
* qla24xx_reset_chip() - Reset ISP24xx chip.
* @ha: HA context
@@ -1113,6 +1190,8 @@ qla24xx_reset_chip(scsi_qla_host_t *vha)
ha->isp_ops->disable_intrs(ha);
+ qla25xx_manipulate_risc_semaphore(vha);
+
/* Perform RISC reset. */
qla24xx_reset_risc(vha);
}
@@ -1888,10 +1967,6 @@ qla2x00_init_rings(scsi_qla_host_t *vha)
qla2x00_init_response_q_entries(rsp);
}
- spin_lock(&ha->vport_slock);
-
- spin_unlock(&ha->vport_slock);
-
ha->tgt.atio_ring_ptr = ha->tgt.atio_ring;
ha->tgt.atio_ring_index = 0;
/* Initialize ATIO queue entries */
@@ -1971,6 +2046,7 @@ qla2x00_fw_ready(scsi_qla_host_t *vha)
"Waiting for LIP to complete.\n");
do {
+ memset(state, -1, sizeof(state));
rval = qla2x00_get_firmware_state(vha, state);
if (rval == QLA_SUCCESS) {
if (state[0] < FSTATE_LOSS_OF_SYNC) {
@@ -2907,7 +2983,6 @@ cleanup_allocation:
static void
qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
{
- char *link_speed;
int rval;
uint16_t mb[4];
struct qla_hw_data *ha = vha->hw;
@@ -2934,10 +3009,10 @@ qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
fcport->port_name[6], fcport->port_name[7], rval,
fcport->fp_speed, mb[0], mb[1]);
} else {
- link_speed = qla2x00_get_link_speed_str(ha);
ql_dbg(ql_dbg_disc, vha, 0x2005,
"iIDMA adjusted to %s GB/s "
- "on %02x%02x%02x%02x%02x%02x%02x%02x.\n", link_speed,
+ "on %02x%02x%02x%02x%02x%02x%02x%02x.\n",
+ qla2x00_get_link_speed_str(ha, fcport->fp_speed),
fcport->port_name[0], fcport->port_name[1],
fcport->port_name[2], fcport->port_name[3],
fcport->port_name[4], fcport->port_name[5],
@@ -3007,10 +3082,10 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
fcport->login_retry = 0;
fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT);
+ qla2x00_set_fcport_state(fcport, FCS_ONLINE);
qla2x00_iidma_fcport(vha, fcport);
qla24xx_update_fcport_fcp_prio(vha, fcport);
qla2x00_reg_remote_port(vha, fcport);
- qla2x00_set_fcport_state(fcport, FCS_ONLINE);
}
/*
@@ -3868,7 +3943,7 @@ qla83xx_reset_ownership(scsi_qla_host_t *vha)
}
}
-int
+static int
__qla83xx_set_drv_ack(scsi_qla_host_t *vha)
{
int rval = QLA_SUCCESS;
@@ -3884,19 +3959,7 @@ __qla83xx_set_drv_ack(scsi_qla_host_t *vha)
return rval;
}
-int
-qla83xx_set_drv_ack(scsi_qla_host_t *vha)
-{
- int rval = QLA_SUCCESS;
-
- qla83xx_idc_lock(vha, 0);
- rval = __qla83xx_set_drv_ack(vha);
- qla83xx_idc_unlock(vha, 0);
-
- return rval;
-}
-
-int
+static int
__qla83xx_clear_drv_ack(scsi_qla_host_t *vha)
{
int rval = QLA_SUCCESS;
@@ -3912,19 +3975,7 @@ __qla83xx_clear_drv_ack(scsi_qla_host_t *vha)
return rval;
}
-int
-qla83xx_clear_drv_ack(scsi_qla_host_t *vha)
-{
- int rval = QLA_SUCCESS;
-
- qla83xx_idc_lock(vha, 0);
- rval = __qla83xx_clear_drv_ack(vha);
- qla83xx_idc_unlock(vha, 0);
-
- return rval;
-}
-
-const char *
+static const char *
qla83xx_dev_state_to_string(uint32_t dev_state)
{
switch (dev_state) {
@@ -3978,7 +4029,7 @@ qla83xx_idc_audit(scsi_qla_host_t *vha, int audit_type)
}
/* Assumes idc_lock always held on entry */
-int
+static int
qla83xx_initiating_reset(scsi_qla_host_t *vha)
{
struct qla_hw_data *ha = vha->hw;
@@ -4026,36 +4077,12 @@ __qla83xx_set_idc_control(scsi_qla_host_t *vha, uint32_t idc_control)
}
int
-qla83xx_set_idc_control(scsi_qla_host_t *vha, uint32_t idc_control)
-{
- int rval = QLA_SUCCESS;
-
- qla83xx_idc_lock(vha, 0);
- rval = __qla83xx_set_idc_control(vha, idc_control);
- qla83xx_idc_unlock(vha, 0);
-
- return rval;
-}
-
-int
__qla83xx_get_idc_control(scsi_qla_host_t *vha, uint32_t *idc_control)
{
return qla83xx_rd_reg(vha, QLA83XX_IDC_CONTROL, idc_control);
}
-int
-qla83xx_get_idc_control(scsi_qla_host_t *vha, uint32_t *idc_control)
-{
- int rval = QLA_SUCCESS;
-
- qla83xx_idc_lock(vha, 0);
- rval = __qla83xx_get_idc_control(vha, idc_control);
- qla83xx_idc_unlock(vha, 0);
-
- return rval;
-}
-
-int
+static int
qla83xx_check_driver_presence(scsi_qla_host_t *vha)
{
uint32_t drv_presence = 0;
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 03b75263283..a481684479c 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -520,7 +520,7 @@ __qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req,
mrk24 = NULL;
req = ha->req_q_map[0];
- mrk = (mrk_entry_t *)qla2x00_alloc_iocbs(vha, 0);
+ mrk = (mrk_entry_t *)qla2x00_alloc_iocbs(vha, NULL);
if (mrk == NULL) {
ql_log(ql_log_warn, base_vha, 0x3026,
"Failed to allocate Marker IOCB.\n");
@@ -2551,7 +2551,7 @@ sufficient_dsds:
(unsigned long __iomem *)ha->nxdb_wr_ptr,
dbval);
wmb();
- while (RD_REG_DWORD(ha->nxdb_rd_ptr) != dbval) {
+ while (RD_REG_DWORD((void __iomem *)ha->nxdb_rd_ptr) != dbval) {
WRT_REG_DWORD(
(unsigned long __iomem *)ha->nxdb_wr_ptr,
dbval);
@@ -2748,7 +2748,6 @@ qla2x00_start_bidir(srb_t *sp, struct scsi_qla_host *vha, uint32_t tot_dsds)
struct rsp_que *rsp;
struct req_que *req;
int rval = EXT_STATUS_OK;
- device_reg_t __iomem *reg = ISP_QUE_REG(ha, vha->req->id);
rval = QLA_SUCCESS;
@@ -2786,15 +2785,7 @@ qla2x00_start_bidir(srb_t *sp, struct scsi_qla_host *vha, uint32_t tot_dsds)
/* Check for room on request queue. */
if (req->cnt < req_cnt + 2) {
- if (ha->mqenable)
- cnt = RD_REG_DWORD(&reg->isp25mq.req_q_out);
- else if (IS_QLA82XX(ha))
- cnt = RD_REG_DWORD(&reg->isp82.req_q_out);
- else if (IS_FWI2_CAPABLE(ha))
- cnt = RD_REG_DWORD(&reg->isp24.req_q_out);
- else
- cnt = qla2x00_debounce_register(
- ISP_REQ_Q_OUT(ha, &reg->isp));
+ cnt = RD_REG_DWORD_RELAXED(req->req_q_out);
if (req->ring_index < cnt)
req->cnt = cnt - req->ring_index;
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 5733811ce8e..873c82014b1 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -251,7 +251,7 @@ qla2x00_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0)
/* Read all mbox registers? */
mboxes = (1 << ha->mbx_count) - 1;
if (!ha->mcp)
- ql_dbg(ql_dbg_async, vha, 0x5001, "MBX pointer ERRROR.\n");
+ ql_dbg(ql_dbg_async, vha, 0x5001, "MBX pointer ERROR.\n");
else
mboxes = ha->mcp->in_mb;
@@ -316,28 +316,24 @@ qla81xx_idc_event(scsi_qla_host_t *vha, uint16_t aen, uint16_t descr)
}
#define LS_UNKNOWN 2
-char *
-qla2x00_get_link_speed_str(struct qla_hw_data *ha)
+const char *
+qla2x00_get_link_speed_str(struct qla_hw_data *ha, uint16_t speed)
{
- static char *link_speeds[] = {"1", "2", "?", "4", "8", "16", "10"};
- char *link_speed;
- int fw_speed = ha->link_data_rate;
+ static const char * const link_speeds[] = {
+ "1", "2", "?", "4", "8", "16", "10"
+ };
if (IS_QLA2100(ha) || IS_QLA2200(ha))
- link_speed = link_speeds[0];
- else if (fw_speed == 0x13)
- link_speed = link_speeds[6];
- else {
- link_speed = link_speeds[LS_UNKNOWN];
- if (fw_speed < 6)
- link_speed =
- link_speeds[fw_speed];
- }
-
- return link_speed;
+ return link_speeds[0];
+ else if (speed == 0x13)
+ return link_speeds[6];
+ else if (speed < 6)
+ return link_speeds[speed];
+ else
+ return link_speeds[LS_UNKNOWN];
}
-void
+static void
qla83xx_handle_8200_aen(scsi_qla_host_t *vha, uint16_t *mb)
{
struct qla_hw_data *ha = vha->hw;
@@ -671,7 +667,7 @@ skip_rio:
ql_dbg(ql_dbg_async, vha, 0x500a,
"LOOP UP detected (%s Gbps).\n",
- qla2x00_get_link_speed_str(ha));
+ qla2x00_get_link_speed_str(ha, ha->link_data_rate));
vha->flags.management_server_logged_in = 0;
qla2x00_post_aen_work(vha, FCH_EVT_LINKUP, ha->link_data_rate);
@@ -860,7 +856,7 @@ skip_rio:
mb[1], mb[2], mb[3]);
ql_log(ql_log_warn, vha, 0x505f,
"Link is operational (%s Gbps).\n",
- qla2x00_get_link_speed_str(ha));
+ qla2x00_get_link_speed_str(ha, ha->link_data_rate));
/*
* Mark all devices as missing so we will login again.
@@ -2318,7 +2314,7 @@ qla24xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0)
/* Read all mbox registers? */
mboxes = (1 << ha->mbx_count) - 1;
if (!ha->mcp)
- ql_dbg(ql_dbg_async, vha, 0x504e, "MBX pointer ERRROR.\n");
+ ql_dbg(ql_dbg_async, vha, 0x504e, "MBX pointer ERROR.\n");
else
mboxes = ha->mcp->in_mb;
@@ -2944,7 +2940,9 @@ skip_msi:
"Failed to reserve interrupt %d already in use.\n",
ha->pdev->irq);
goto fail;
- }
+ } else if (!ha->flags.msi_enabled)
+ ql_dbg(ql_dbg_init, vha, 0x0125,
+ "INTa mode: Enabled.\n");
clear_risc_ints:
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 18c509fae55..68c55eaa318 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -3122,7 +3122,7 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
if (vp_idx == 0 && (MSB(stat) != 1))
goto reg_needed;
- if (MSB(stat) != 0) {
+ if (MSB(stat) != 0 && MSB(stat) != 2) {
ql_dbg(ql_dbg_mbx, vha, 0x10ba,
"Could not acquire ID for VP[%d].\n", vp_idx);
return;
@@ -3536,7 +3536,7 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req)
if (IS_QLA83XX(ha))
mcp->mb[15] = 0;
- reg = (struct device_reg_25xxmq *)((void *)(ha->mqiobase) +
+ reg = (struct device_reg_25xxmq __iomem *)((ha->mqiobase) +
QLA_QUE_PAGE * req->id);
mcp->mb[4] = req->id;
@@ -3605,7 +3605,7 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
if (IS_QLA83XX(ha))
mcp->mb[15] = 0;
- reg = (struct device_reg_25xxmq *)((void *)(ha->mqiobase) +
+ reg = (struct device_reg_25xxmq __iomem *)((ha->mqiobase) +
QLA_QUE_PAGE * rsp->id);
mcp->mb[4] = rsp->id;
diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c
index 14cd361742f..3e3f593bada 100644
--- a/drivers/scsi/qla2xxx/qla_nx.c
+++ b/drivers/scsi/qla2xxx/qla_nx.c
@@ -36,7 +36,7 @@
#define MAX_CRB_XFORM 60
static unsigned long crb_addr_xform[MAX_CRB_XFORM];
-int qla82xx_crb_table_initialized;
+static int qla82xx_crb_table_initialized;
#define qla82xx_crb_addr_transform(name) \
(crb_addr_xform[QLA82XX_HW_PX_MAP_CRB_##name] = \
@@ -102,7 +102,7 @@ static void qla82xx_crb_addr_transform_setup(void)
qla82xx_crb_table_initialized = 1;
}
-struct crb_128M_2M_block_map crb_128M_2M_map[64] = {
+static struct crb_128M_2M_block_map crb_128M_2M_map[64] = {
{{{0, 0, 0, 0} } },
{{{1, 0x0100000, 0x0102000, 0x120000},
{1, 0x0110000, 0x0120000, 0x130000},
@@ -262,7 +262,7 @@ struct crb_128M_2M_block_map crb_128M_2M_map[64] = {
/*
* top 12 bits of crb internal address (hub, agent)
*/
-unsigned qla82xx_crb_hub_agt[64] = {
+static unsigned qla82xx_crb_hub_agt[64] = {
0,
QLA82XX_HW_CRB_HUB_AGT_ADR_PS,
QLA82XX_HW_CRB_HUB_AGT_ADR_MN,
@@ -330,7 +330,7 @@ unsigned qla82xx_crb_hub_agt[64] = {
};
/* Device states */
-char *q_dev_state[] = {
+static char *q_dev_state[] = {
"Unknown",
"Cold",
"Initializing",
@@ -359,12 +359,13 @@ qla82xx_pci_set_crbwindow_2M(struct qla_hw_data *ha, ulong *off)
ha->crb_win = CRB_HI(*off);
writel(ha->crb_win,
- (void *)(CRB_WINDOW_2M + ha->nx_pcibase));
+ (void __iomem *)(CRB_WINDOW_2M + ha->nx_pcibase));
/* Read back value to make sure write has gone through before trying
* to use it.
*/
- win_read = RD_REG_DWORD((void *)(CRB_WINDOW_2M + ha->nx_pcibase));
+ win_read = RD_REG_DWORD((void __iomem *)
+ (CRB_WINDOW_2M + ha->nx_pcibase));
if (win_read != ha->crb_win) {
ql_dbg(ql_dbg_p3p, vha, 0xb000,
"%s: Written crbwin (0x%x) "
@@ -567,7 +568,7 @@ qla82xx_pci_mem_bound_check(struct qla_hw_data *ha,
return 1;
}
-int qla82xx_pci_set_window_warning_count;
+static int qla82xx_pci_set_window_warning_count;
static unsigned long
qla82xx_pci_set_window(struct qla_hw_data *ha, unsigned long long addr)
@@ -677,10 +678,10 @@ static int qla82xx_pci_mem_read_direct(struct qla_hw_data *ha,
u64 off, void *data, int size)
{
unsigned long flags;
- void *addr = NULL;
+ void __iomem *addr = NULL;
int ret = 0;
u64 start;
- uint8_t *mem_ptr = NULL;
+ uint8_t __iomem *mem_ptr = NULL;
unsigned long mem_base;
unsigned long mem_page;
scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
@@ -712,7 +713,7 @@ static int qla82xx_pci_mem_read_direct(struct qla_hw_data *ha,
mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE * 2);
else
mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE);
- if (mem_ptr == 0UL) {
+ if (mem_ptr == NULL) {
*(u8 *)data = 0;
return -1;
}
@@ -749,10 +750,10 @@ qla82xx_pci_mem_write_direct(struct qla_hw_data *ha,
u64 off, void *data, int size)
{
unsigned long flags;
- void *addr = NULL;
+ void __iomem *addr = NULL;
int ret = 0;
u64 start;
- uint8_t *mem_ptr = NULL;
+ uint8_t __iomem *mem_ptr = NULL;
unsigned long mem_base;
unsigned long mem_page;
scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
@@ -784,7 +785,7 @@ qla82xx_pci_mem_write_direct(struct qla_hw_data *ha,
mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE*2);
else
mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE);
- if (mem_ptr == 0UL)
+ if (mem_ptr == NULL)
return -1;
addr = mem_ptr;
@@ -908,24 +909,24 @@ qla82xx_wait_rom_done(struct qla_hw_data *ha)
return 0;
}
-int
+static int
qla82xx_md_rw_32(struct qla_hw_data *ha, uint32_t off, u32 data, uint8_t flag)
{
uint32_t off_value, rval = 0;
- WRT_REG_DWORD((void *)(CRB_WINDOW_2M + ha->nx_pcibase),
+ WRT_REG_DWORD((void __iomem *)(CRB_WINDOW_2M + ha->nx_pcibase),
(off & 0xFFFF0000));
/* Read back value to make sure write has gone through */
- RD_REG_DWORD((void *)(CRB_WINDOW_2M + ha->nx_pcibase));
+ RD_REG_DWORD((void __iomem *)(CRB_WINDOW_2M + ha->nx_pcibase));
off_value = (off & 0x0000FFFF);
if (flag)
- WRT_REG_DWORD((void *)
+ WRT_REG_DWORD((void __iomem *)
(off_value + CRB_INDIRECT_2M + ha->nx_pcibase),
data);
else
- rval = RD_REG_DWORD((void *)
+ rval = RD_REG_DWORD((void __iomem *)
(off_value + CRB_INDIRECT_2M + ha->nx_pcibase));
return rval;
@@ -955,7 +956,7 @@ qla82xx_rom_fast_read(struct qla_hw_data *ha, int addr, int *valp)
}
if (loops >= 50000) {
ql_log(ql_log_fatal, vha, 0x00b9,
- "Failed to aquire SEM2 lock.\n");
+ "Failed to acquire SEM2 lock.\n");
return -1;
}
ret = qla82xx_do_rom_fast_read(ha, addr, valp);
@@ -1122,7 +1123,7 @@ qla82xx_pinit_from_rom(scsi_qla_host_t *vha)
long data;
};
- /* Halt all the indiviual PEGs and other blocks of the ISP */
+ /* Halt all the individual PEGs and other blocks of the ISP */
qla82xx_rom_lock(ha);
/* disable all I2Q */
@@ -1654,7 +1655,6 @@ qla82xx_iospace_config(struct qla_hw_data *ha)
if (!ha->nx_pcibase) {
ql_log_pci(ql_log_fatal, ha->pdev, 0x000e,
"Cannot remap pcibase MMIO, aborting.\n");
- pci_release_regions(ha->pdev);
goto iospace_error_exit;
}
@@ -1669,7 +1669,6 @@ qla82xx_iospace_config(struct qla_hw_data *ha)
if (!ha->nxdb_wr_ptr) {
ql_log_pci(ql_log_fatal, ha->pdev, 0x000f,
"Cannot remap MMIO, aborting.\n");
- pci_release_regions(ha->pdev);
goto iospace_error_exit;
}
@@ -1764,14 +1763,6 @@ void qla82xx_config_rings(struct scsi_qla_host *vha)
WRT_REG_DWORD((unsigned long __iomem *)&reg->rsp_q_out[0], 0);
}
-void qla82xx_reset_adapter(struct scsi_qla_host *vha)
-{
- struct qla_hw_data *ha = vha->hw;
- vha->flags.online = 0;
- qla2x00_try_to_stop_firmware(vha);
- ha->isp_ops->disable_intrs(ha);
-}
-
static int
qla82xx_fw_load_from_blob(struct qla_hw_data *ha)
{
@@ -1856,7 +1847,7 @@ qla82xx_set_product_offset(struct qla_hw_data *ha)
return -1;
}
-int
+static int
qla82xx_validate_firmware_blob(scsi_qla_host_t *vha, uint8_t fw_type)
{
__le32 val;
@@ -1961,20 +1952,6 @@ qla82xx_check_rcvpeg_state(struct qla_hw_data *ha)
}
/* ISR related functions */
-uint32_t qla82xx_isr_int_target_mask_enable[8] = {
- ISR_INT_TARGET_MASK, ISR_INT_TARGET_MASK_F1,
- ISR_INT_TARGET_MASK_F2, ISR_INT_TARGET_MASK_F3,
- ISR_INT_TARGET_MASK_F4, ISR_INT_TARGET_MASK_F5,
- ISR_INT_TARGET_MASK_F7, ISR_INT_TARGET_MASK_F7
-};
-
-uint32_t qla82xx_isr_int_target_status[8] = {
- ISR_INT_TARGET_STATUS, ISR_INT_TARGET_STATUS_F1,
- ISR_INT_TARGET_STATUS_F2, ISR_INT_TARGET_STATUS_F3,
- ISR_INT_TARGET_STATUS_F4, ISR_INT_TARGET_STATUS_F5,
- ISR_INT_TARGET_STATUS_F7, ISR_INT_TARGET_STATUS_F7
-};
-
static struct qla82xx_legacy_intr_set legacy_intr[] = \
QLA82XX_LEGACY_INTR_CONFIG;
@@ -2813,7 +2790,7 @@ qla82xx_start_iocbs(scsi_qla_host_t *vha)
else {
WRT_REG_DWORD((unsigned long __iomem *)ha->nxdb_wr_ptr, dbval);
wmb();
- while (RD_REG_DWORD(ha->nxdb_rd_ptr) != dbval) {
+ while (RD_REG_DWORD((void __iomem *)ha->nxdb_rd_ptr) != dbval) {
WRT_REG_DWORD((unsigned long __iomem *)ha->nxdb_wr_ptr,
dbval);
wmb();
@@ -2821,7 +2798,8 @@ qla82xx_start_iocbs(scsi_qla_host_t *vha)
}
}
-void qla82xx_rom_lock_recovery(struct qla_hw_data *ha)
+static void
+qla82xx_rom_lock_recovery(struct qla_hw_data *ha)
{
scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
@@ -3177,7 +3155,7 @@ qla82xx_check_md_needed(scsi_qla_host_t *vha)
}
-int
+static int
qla82xx_check_fw_alive(scsi_qla_host_t *vha)
{
uint32_t fw_heartbeat_counter;
@@ -3817,7 +3795,8 @@ qla82xx_minidump_process_rdocm(scsi_qla_host_t *vha,
loop_cnt = ocm_hdr->op_count;
for (i = 0; i < loop_cnt; i++) {
- r_value = RD_REG_DWORD((void *)(r_addr + ha->nx_pcibase));
+ r_value = RD_REG_DWORD((void __iomem *)
+ (r_addr + ha->nx_pcibase));
*data_ptr++ = cpu_to_le32(r_value);
r_addr += r_stride;
}
@@ -4376,7 +4355,7 @@ qla82xx_md_free(scsi_qla_host_t *vha)
ha->md_tmplt_hdr, ha->md_template_size / 1024);
dma_free_coherent(&ha->pdev->dev, ha->md_template_size,
ha->md_tmplt_hdr, ha->md_tmplt_hdr_dma);
- ha->md_tmplt_hdr = 0;
+ ha->md_tmplt_hdr = NULL;
}
/* Release the template data buffer allocated */
@@ -4386,7 +4365,7 @@ qla82xx_md_free(scsi_qla_host_t *vha)
ha->md_dump, ha->md_dump_size / 1024);
vfree(ha->md_dump);
ha->md_dump_size = 0;
- ha->md_dump = 0;
+ ha->md_dump = NULL;
}
}
@@ -4423,7 +4402,7 @@ qla82xx_md_prep(scsi_qla_host_t *vha)
dma_free_coherent(&ha->pdev->dev,
ha->md_template_size,
ha->md_tmplt_hdr, ha->md_tmplt_hdr_dma);
- ha->md_tmplt_hdr = 0;
+ ha->md_tmplt_hdr = NULL;
}
}
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index d501bf5f806..3a1661cf8c1 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -41,7 +41,7 @@ static struct kmem_cache *ctx_cachep;
*/
int ql_errlev = ql_log_all;
-int ql2xenableclass2;
+static int ql2xenableclass2;
module_param(ql2xenableclass2, int, S_IRUGO|S_IRUSR);
MODULE_PARM_DESC(ql2xenableclass2,
"Specify if Class 2 operations are supported from the very "
@@ -89,6 +89,8 @@ MODULE_PARM_DESC(ql2xextended_error_logging,
"\t\t0x00200000 - AER/EEH. 0x00100000 - Multi Q.\n"
"\t\t0x00080000 - P3P Specific. 0x00040000 - Virtual Port.\n"
"\t\t0x00020000 - Buffer Dump. 0x00010000 - Misc.\n"
+ "\t\t0x00008000 - Verbose. 0x00004000 - Target.\n"
+ "\t\t0x00002000 - Target Mgmt. 0x00001000 - Target TMF.\n"
"\t\t0x7fffffff - For enabling all logs, can be too many logs.\n"
"\t\t0x1e400000 - Preferred value for capturing essential "
"debug information (equivalent to old "
@@ -494,12 +496,20 @@ qla24xx_pci_info_str(struct scsi_qla_host *vha, char *str)
(BIT_4 | BIT_5 | BIT_6 | BIT_7 | BIT_8 | BIT_9)) >> 4;
strcpy(str, "PCIe (");
- if (lspeed == 1)
+ switch (lspeed) {
+ case 1:
strcat(str, "2.5GT/s ");
- else if (lspeed == 2)
+ break;
+ case 2:
strcat(str, "5.0GT/s ");
- else
+ break;
+ case 3:
+ strcat(str, "8.0GT/s ");
+ break;
+ default:
strcat(str, "<unknown> ");
+ break;
+ }
snprintf(lwstr, sizeof(lwstr), "x%d)", lwidth);
strcat(str, lwstr);
@@ -719,7 +729,7 @@ qla2xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
rval = ha->isp_ops->start_scsi(sp);
if (rval != QLA_SUCCESS) {
- ql_dbg(ql_dbg_io, vha, 0x3013,
+ ql_dbg(ql_dbg_io + ql_dbg_verbose, vha, 0x3013,
"Start scsi failed rval=%d for cmd=%p.\n", rval, cmd);
goto qc24_host_busy_free_sp;
}
@@ -2357,7 +2367,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
/* Configure PCI I/O space */
ret = ha->isp_ops->iospace_config(ha);
if (ret)
- goto probe_hw_failed;
+ goto iospace_config_failed;
ql_log_pci(ql_log_info, pdev, 0x001d,
"Found an ISP%04X irq %d iobase 0x%p.\n",
@@ -2668,7 +2678,11 @@ probe_hw_failed:
qla82xx_idc_lock(ha);
qla82xx_clear_drv_active(ha);
qla82xx_idc_unlock(ha);
- iounmap((device_reg_t __iomem *)ha->nx_pcibase);
+ }
+iospace_config_failed:
+ if (IS_QLA82XX(ha)) {
+ if (!ha->nx_pcibase)
+ iounmap((device_reg_t __iomem *)ha->nx_pcibase);
if (!ql2xdbwr)
iounmap((device_reg_t __iomem *)ha->nxdb_wr_ptr);
} else {
@@ -2755,6 +2769,7 @@ qla2x00_remove_one(struct pci_dev *pdev)
ha->flags.host_shutting_down = 1;
+ set_bit(UNLOADING, &base_vha->dpc_flags);
mutex_lock(&ha->vport_lock);
while (ha->cur_vport_count) {
struct Scsi_Host *scsi_host;
@@ -2784,8 +2799,6 @@ qla2x00_remove_one(struct pci_dev *pdev)
"Error while clearing DRV-Presence.\n");
}
- set_bit(UNLOADING, &base_vha->dpc_flags);
-
qla2x00_abort_all_cmds(base_vha, DID_NO_CONNECT << 16);
qla2x00_dfs_remove(base_vha);
@@ -3721,10 +3734,9 @@ void qla2x00_relogin(struct scsi_qla_host *vha)
if (fcport->flags &
FCF_FCP2_DEVICE)
opts |= BIT_1;
- status2 =
- qla2x00_get_port_database(
- vha, fcport,
- opts);
+ status2 =
+ qla2x00_get_port_database(
+ vha, fcport, opts);
if (status2 != QLA_SUCCESS)
status = 1;
}
@@ -3836,7 +3848,7 @@ qla83xx_idc_state_handler_work(struct work_struct *work)
qla83xx_idc_unlock(base_vha, 0);
}
-int
+static int
qla83xx_check_nic_core_fw_alive(scsi_qla_host_t *base_vha)
{
int rval = QLA_SUCCESS;
@@ -3954,7 +3966,7 @@ qla83xx_wait_logic(void)
}
}
-int
+static int
qla83xx_force_lock_recovery(scsi_qla_host_t *base_vha)
{
int rval;
@@ -4013,7 +4025,7 @@ qla83xx_force_lock_recovery(scsi_qla_host_t *base_vha)
return rval;
}
-int
+static int
qla83xx_idc_lock_recovery(scsi_qla_host_t *base_vha)
{
int rval = QLA_SUCCESS;
@@ -4212,7 +4224,7 @@ qla83xx_clear_drv_presence(scsi_qla_host_t *vha)
return rval;
}
-void
+static void
qla83xx_need_reset_handler(scsi_qla_host_t *vha)
{
struct qla_hw_data *ha = vha->hw;
@@ -4224,7 +4236,7 @@ qla83xx_need_reset_handler(scsi_qla_host_t *vha)
while (1) {
qla83xx_rd_reg(vha, QLA83XX_IDC_DRIVER_ACK, &drv_ack);
qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence);
- if (drv_ack == drv_presence)
+ if ((drv_ack & drv_presence) == drv_presence)
break;
if (time_after_eq(jiffies, ack_timeout)) {
@@ -4251,7 +4263,7 @@ qla83xx_need_reset_handler(scsi_qla_host_t *vha)
ql_log(ql_log_info, vha, 0xb068, "HW State: COLD/RE-INIT.\n");
}
-int
+static int
qla83xx_device_bootstrap(scsi_qla_host_t *vha)
{
int rval = QLA_SUCCESS;
@@ -4505,9 +4517,9 @@ qla2x00_do_dpc(void *data)
"ISP abort end.\n");
}
- if (test_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags)) {
+ if (test_and_clear_bit(FCPORT_UPDATE_NEEDED,
+ &base_vha->dpc_flags)) {
qla2x00_update_fcports(base_vha);
- clear_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags);
}
if (test_bit(SCR_PENDING, &base_vha->dpc_flags)) {
@@ -4987,7 +4999,8 @@ qla2xxx_pci_mmio_enabled(struct pci_dev *pdev)
return PCI_ERS_RESULT_RECOVERED;
}
-uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha)
+static uint32_t
+qla82xx_error_recovery(scsi_qla_host_t *base_vha)
{
uint32_t rval = QLA_FUNCTION_FAILED;
uint32_t drv_active = 0;
diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c
index 62aa5584f64..ad1dc14bacc 100644
--- a/drivers/scsi/qla2xxx/qla_target.c
+++ b/drivers/scsi/qla2xxx/qla_target.c
@@ -73,7 +73,7 @@ enum fcp_resp_rsp_codes {
#define FCP_PTA_SIMPLE 0 /* simple task attribute */
#define FCP_PTA_HEADQ 1 /* head of queue task attribute */
#define FCP_PTA_ORDERED 2 /* ordered task attribute */
-#define FCP_PTA_ACA 4 /* auto. contigent allegiance */
+#define FCP_PTA_ACA 4 /* auto. contingent allegiance */
#define FCP_PTA_MASK 7 /* mask for task attribute field */
#define FCP_PRI_SHIFT 3 /* priority field starts in bit 3 */
#define FCP_PRI_RESVD_MASK 0x80 /* reserved bits in priority field */
@@ -1029,7 +1029,7 @@ void qlt_stop_phase2(struct qla_tgt *tgt)
EXPORT_SYMBOL(qlt_stop_phase2);
/* Called from qlt_remove_target() -> qla2x00_remove_one() */
-void qlt_release(struct qla_tgt *tgt)
+static void qlt_release(struct qla_tgt *tgt)
{
struct qla_hw_data *ha = tgt->ha;
@@ -3980,7 +3980,7 @@ void qlt_async_event(uint16_t code, struct scsi_qla_host *vha,
case MBA_RSP_TRANSFER_ERR: /* Response Transfer Error */
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03a,
"qla_target(%d): System error async event %#x "
- "occured", vha->vp_idx, code);
+ "occurred", vha->vp_idx, code);
break;
case MBA_WAKEUP_THRES: /* Request Queue Wake-up. */
set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
@@ -3989,7 +3989,7 @@ void qlt_async_event(uint16_t code, struct scsi_qla_host *vha,
case MBA_LOOP_UP:
{
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03b,
- "qla_target(%d): Async LOOP_UP occured "
+ "qla_target(%d): Async LOOP_UP occurred "
"(m[0]=%x, m[1]=%x, m[2]=%x, m[3]=%x)", vha->vp_idx,
le16_to_cpu(mailbox[0]), le16_to_cpu(mailbox[1]),
le16_to_cpu(mailbox[2]), le16_to_cpu(mailbox[3]));
@@ -4006,7 +4006,7 @@ void qlt_async_event(uint16_t code, struct scsi_qla_host *vha,
case MBA_LIP_RESET:
case MBA_RSCN_UPDATE:
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03c,
- "qla_target(%d): Async event %#x occured "
+ "qla_target(%d): Async event %#x occurred "
"(m[0]=%x, m[1]=%x, m[2]=%x, m[3]=%x)", vha->vp_idx, code,
le16_to_cpu(mailbox[0]), le16_to_cpu(mailbox[1]),
le16_to_cpu(mailbox[2]), le16_to_cpu(mailbox[3]));
@@ -4015,7 +4015,7 @@ void qlt_async_event(uint16_t code, struct scsi_qla_host *vha,
case MBA_PORT_UPDATE:
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03d,
"qla_target(%d): Port update async event %#x "
- "occured: updating the ports database (m[0]=%x, m[1]=%x, "
+ "occurred: updating the ports database (m[0]=%x, m[1]=%x, "
"m[2]=%x, m[3]=%x)", vha->vp_idx, code,
le16_to_cpu(mailbox[0]), le16_to_cpu(mailbox[1]),
le16_to_cpu(mailbox[2]), le16_to_cpu(mailbox[3]));
@@ -4031,7 +4031,7 @@ void qlt_async_event(uint16_t code, struct scsi_qla_host *vha,
default:
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf040,
- "qla_target(%d): Async event %#x occured: "
+ "qla_target(%d): Async event %#x occurred: "
"ignore (m[0]=%x, m[1]=%x, m[2]=%x, m[3]=%x)", vha->vp_idx,
code, le16_to_cpu(mailbox[0]), le16_to_cpu(mailbox[1]),
le16_to_cpu(mailbox[2]), le16_to_cpu(mailbox[3]));
diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h
index cfe934e1af4..49697ca41e7 100644
--- a/drivers/scsi/qla2xxx/qla_version.h
+++ b/drivers/scsi/qla2xxx/qla_version.h
@@ -7,7 +7,7 @@
/*
* Driver version
*/
-#define QLA2XXX_VERSION "8.04.00.07-k"
+#define QLA2XXX_VERSION "8.04.00.08-k"
#define QLA_DRIVER_MAJOR_VER 8
#define QLA_DRIVER_MINOR_VER 4
diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
index 3d74f2f39ae..4372e32bc95 100644
--- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c
+++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
@@ -367,7 +367,7 @@ static struct se_node_acl *tcm_qla2xxx_alloc_fabric_acl(
nacl = kzalloc(sizeof(struct tcm_qla2xxx_nacl), GFP_KERNEL);
if (!nacl) {
- pr_err("Unable to alocate struct tcm_qla2xxx_nacl\n");
+ pr_err("Unable to allocate struct tcm_qla2xxx_nacl\n");
return NULL;
}
diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c
index dc0ad85853e..8f6b12cbd22 100644
--- a/drivers/scsi/scsi_pm.c
+++ b/drivers/scsi/scsi_pm.c
@@ -16,16 +16,14 @@
#include "scsi_priv.h"
-static int scsi_dev_type_suspend(struct device *dev, pm_message_t msg)
+static int scsi_dev_type_suspend(struct device *dev, int (*cb)(struct device *))
{
- struct device_driver *drv;
int err;
err = scsi_device_quiesce(to_scsi_device(dev));
if (err == 0) {
- drv = dev->driver;
- if (drv && drv->suspend) {
- err = drv->suspend(dev, msg);
+ if (cb) {
+ err = cb(dev);
if (err)
scsi_device_resume(to_scsi_device(dev));
}
@@ -34,14 +32,12 @@ static int scsi_dev_type_suspend(struct device *dev, pm_message_t msg)
return err;
}
-static int scsi_dev_type_resume(struct device *dev)
+static int scsi_dev_type_resume(struct device *dev, int (*cb)(struct device *))
{
- struct device_driver *drv;
int err = 0;
- drv = dev->driver;
- if (drv && drv->resume)
- err = drv->resume(dev);
+ if (cb)
+ err = cb(dev);
scsi_device_resume(to_scsi_device(dev));
dev_dbg(dev, "scsi resume: %d\n", err);
return err;
@@ -49,51 +45,39 @@ static int scsi_dev_type_resume(struct device *dev)
#ifdef CONFIG_PM_SLEEP
-static int scsi_bus_suspend_common(struct device *dev, pm_message_t msg)
+static int
+scsi_bus_suspend_common(struct device *dev, int (*cb)(struct device *))
{
int err = 0;
if (scsi_is_sdev_device(dev)) {
/*
- * sd is the only high-level SCSI driver to implement runtime
- * PM, and sd treats runtime suspend, system suspend, and
- * system hibernate identically (but not system freeze).
+ * All the high-level SCSI drivers that implement runtime
+ * PM treat runtime suspend, system suspend, and system
+ * hibernate identically.
*/
- if (pm_runtime_suspended(dev)) {
- if (msg.event == PM_EVENT_SUSPEND ||
- msg.event == PM_EVENT_HIBERNATE)
- return 0; /* already suspended */
+ if (pm_runtime_suspended(dev))
+ return 0;
- /* wake up device so that FREEZE will succeed */
- pm_runtime_resume(dev);
- }
- err = scsi_dev_type_suspend(dev, msg);
+ err = scsi_dev_type_suspend(dev, cb);
}
+
return err;
}
-static int scsi_bus_resume_common(struct device *dev)
+static int
+scsi_bus_resume_common(struct device *dev, int (*cb)(struct device *))
{
int err = 0;
- /*
- * Parent device may have runtime suspended as soon as
- * it is woken up during the system resume.
- *
- * Resume it on behalf of child.
- */
- pm_runtime_get_sync(dev->parent);
-
if (scsi_is_sdev_device(dev))
- err = scsi_dev_type_resume(dev);
+ err = scsi_dev_type_resume(dev, cb);
+
if (err == 0) {
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
}
-
- pm_runtime_put_sync(dev->parent);
-
return err;
}
@@ -112,26 +96,49 @@ static int scsi_bus_prepare(struct device *dev)
static int scsi_bus_suspend(struct device *dev)
{
- return scsi_bus_suspend_common(dev, PMSG_SUSPEND);
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ return scsi_bus_suspend_common(dev, pm ? pm->suspend : NULL);
+}
+
+static int scsi_bus_resume(struct device *dev)
+{
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ return scsi_bus_resume_common(dev, pm ? pm->resume : NULL);
}
static int scsi_bus_freeze(struct device *dev)
{
- return scsi_bus_suspend_common(dev, PMSG_FREEZE);
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ return scsi_bus_suspend_common(dev, pm ? pm->freeze : NULL);
+}
+
+static int scsi_bus_thaw(struct device *dev)
+{
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ return scsi_bus_resume_common(dev, pm ? pm->thaw : NULL);
}
static int scsi_bus_poweroff(struct device *dev)
{
- return scsi_bus_suspend_common(dev, PMSG_HIBERNATE);
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ return scsi_bus_suspend_common(dev, pm ? pm->poweroff : NULL);
+}
+
+static int scsi_bus_restore(struct device *dev)
+{
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ return scsi_bus_resume_common(dev, pm ? pm->restore : NULL);
}
#else /* CONFIG_PM_SLEEP */
-#define scsi_bus_resume_common NULL
#define scsi_bus_prepare NULL
#define scsi_bus_suspend NULL
+#define scsi_bus_resume NULL
#define scsi_bus_freeze NULL
+#define scsi_bus_thaw NULL
#define scsi_bus_poweroff NULL
+#define scsi_bus_restore NULL
#endif /* CONFIG_PM_SLEEP */
@@ -140,10 +147,12 @@ static int scsi_bus_poweroff(struct device *dev)
static int scsi_runtime_suspend(struct device *dev)
{
int err = 0;
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
dev_dbg(dev, "scsi_runtime_suspend\n");
if (scsi_is_sdev_device(dev)) {
- err = scsi_dev_type_suspend(dev, PMSG_AUTO_SUSPEND);
+ err = scsi_dev_type_suspend(dev,
+ pm ? pm->runtime_suspend : NULL);
if (err == -EAGAIN)
pm_schedule_suspend(dev, jiffies_to_msecs(
round_jiffies_up_relative(HZ/10)));
@@ -157,10 +166,11 @@ static int scsi_runtime_suspend(struct device *dev)
static int scsi_runtime_resume(struct device *dev)
{
int err = 0;
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
dev_dbg(dev, "scsi_runtime_resume\n");
if (scsi_is_sdev_device(dev))
- err = scsi_dev_type_resume(dev);
+ err = scsi_dev_type_resume(dev, pm ? pm->runtime_resume : NULL);
/* Insert hooks here for targets, hosts, and transport classes */
@@ -239,11 +249,11 @@ void scsi_autopm_put_host(struct Scsi_Host *shost)
const struct dev_pm_ops scsi_bus_pm_ops = {
.prepare = scsi_bus_prepare,
.suspend = scsi_bus_suspend,
- .resume = scsi_bus_resume_common,
+ .resume = scsi_bus_resume,
.freeze = scsi_bus_freeze,
- .thaw = scsi_bus_resume_common,
+ .thaw = scsi_bus_thaw,
.poweroff = scsi_bus_poweroff,
- .restore = scsi_bus_resume_common,
+ .restore = scsi_bus_restore,
.runtime_suspend = scsi_runtime_suspend,
.runtime_resume = scsi_runtime_resume,
.runtime_idle = scsi_runtime_idle,
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index ce5224c92ed..931a7d95420 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -247,11 +247,11 @@ show_shost_active_mode(struct device *dev,
static DEVICE_ATTR(active_mode, S_IRUGO | S_IWUSR, show_shost_active_mode, NULL);
-static int check_reset_type(char *str)
+static int check_reset_type(const char *str)
{
- if (strncmp(str, "adapter", 10) == 0)
+ if (sysfs_streq(str, "adapter"))
return SCSI_ADAPTER_RESET;
- else if (strncmp(str, "firmware", 10) == 0)
+ else if (sysfs_streq(str, "firmware"))
return SCSI_FIRMWARE_RESET;
else
return 0;
@@ -264,12 +264,9 @@ store_host_reset(struct device *dev, struct device_attribute *attr,
struct Scsi_Host *shost = class_to_shost(dev);
struct scsi_host_template *sht = shost->hostt;
int ret = -EINVAL;
- char str[10];
int type;
- sscanf(buf, "%s", str);
- type = check_reset_type(str);
-
+ type = check_reset_type(buf);
if (!type)
goto exit_store_host_reset;
diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c
index f7565fc4f0e..1b681427dde 100644
--- a/drivers/scsi/scsi_transport_sas.c
+++ b/drivers/scsi/scsi_transport_sas.c
@@ -151,6 +151,7 @@ static struct {
{ SAS_LINK_RATE_1_5_GBPS, "1.5 Gbit" },
{ SAS_LINK_RATE_3_0_GBPS, "3.0 Gbit" },
{ SAS_LINK_RATE_6_0_GBPS, "6.0 Gbit" },
+ { SAS_LINK_RATE_12_0_GBPS, "12.0 Gbit" },
};
sas_bitfield_name_search(linkspeed, sas_linkspeed_names)
sas_bitfield_name_set(linkspeed, sas_linkspeed_names)
diff --git a/drivers/scsi/scsi_transport_srp.c b/drivers/scsi/scsi_transport_srp.c
index 21a045e0559..f379c7f3034 100644
--- a/drivers/scsi/scsi_transport_srp.c
+++ b/drivers/scsi/scsi_transport_srp.c
@@ -38,7 +38,7 @@ struct srp_host_attrs {
#define to_srp_host_attrs(host) ((struct srp_host_attrs *)(host)->shost_data)
#define SRP_HOST_ATTRS 0
-#define SRP_RPORT_ATTRS 2
+#define SRP_RPORT_ATTRS 3
struct srp_internal {
struct scsi_transport_template t;
@@ -47,7 +47,6 @@ struct srp_internal {
struct device_attribute *host_attrs[SRP_HOST_ATTRS + 1];
struct device_attribute *rport_attrs[SRP_RPORT_ATTRS + 1];
- struct device_attribute private_rport_attrs[SRP_RPORT_ATTRS];
struct transport_container rport_attr_cont;
};
@@ -72,24 +71,6 @@ static DECLARE_TRANSPORT_CLASS(srp_host_class, "srp_host", srp_host_setup,
static DECLARE_TRANSPORT_CLASS(srp_rport_class, "srp_remote_ports",
NULL, NULL, NULL);
-#define SETUP_TEMPLATE(attrb, field, perm, test, ro_test, ro_perm) \
- i->private_##attrb[count] = dev_attr_##field; \
- i->private_##attrb[count].attr.mode = perm; \
- if (ro_test) { \
- i->private_##attrb[count].attr.mode = ro_perm; \
- i->private_##attrb[count].store = NULL; \
- } \
- i->attrb[count] = &i->private_##attrb[count]; \
- if (test) \
- count++
-
-#define SETUP_RPORT_ATTRIBUTE_RD(field) \
- SETUP_TEMPLATE(rport_attrs, field, S_IRUGO, 1, 0, 0)
-
-#define SETUP_RPORT_ATTRIBUTE_RW(field) \
- SETUP_TEMPLATE(rport_attrs, field, S_IRUGO | S_IWUSR, \
- 1, 1, S_IRUGO)
-
#define SRP_PID(p) \
(p)->port_id[0], (p)->port_id[1], (p)->port_id[2], (p)->port_id[3], \
(p)->port_id[4], (p)->port_id[5], (p)->port_id[6], (p)->port_id[7], \
@@ -135,6 +116,24 @@ show_srp_rport_roles(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(roles, S_IRUGO, show_srp_rport_roles, NULL);
+static ssize_t store_srp_rport_delete(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct srp_rport *rport = transport_class_to_srp_rport(dev);
+ struct Scsi_Host *shost = dev_to_shost(dev);
+ struct srp_internal *i = to_srp_internal(shost->transportt);
+
+ if (i->f->rport_delete) {
+ i->f->rport_delete(rport);
+ return count;
+ } else {
+ return -ENOSYS;
+ }
+}
+
+static DEVICE_ATTR(delete, S_IWUSR, NULL, store_srp_rport_delete);
+
static void srp_rport_release(struct device *dev)
{
struct srp_rport *rport = dev_to_rport(dev);
@@ -324,12 +323,16 @@ srp_attach_transport(struct srp_function_template *ft)
i->rport_attr_cont.ac.attrs = &i->rport_attrs[0];
i->rport_attr_cont.ac.class = &srp_rport_class.class;
i->rport_attr_cont.ac.match = srp_rport_match;
- transport_container_register(&i->rport_attr_cont);
count = 0;
- SETUP_RPORT_ATTRIBUTE_RD(port_id);
- SETUP_RPORT_ATTRIBUTE_RD(roles);
- i->rport_attrs[count] = NULL;
+ i->rport_attrs[count++] = &dev_attr_port_id;
+ i->rport_attrs[count++] = &dev_attr_roles;
+ if (ft->rport_delete)
+ i->rport_attrs[count++] = &dev_attr_delete;
+ i->rport_attrs[count++] = NULL;
+ BUG_ON(count > ARRAY_SIZE(i->rport_attrs));
+
+ transport_container_register(&i->rport_attr_cont);
i->f = ft;
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 352bc77b7c8..7992635d405 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -105,7 +105,7 @@ static void sd_unlock_native_capacity(struct gendisk *disk);
static int sd_probe(struct device *);
static int sd_remove(struct device *);
static void sd_shutdown(struct device *);
-static int sd_suspend(struct device *, pm_message_t state);
+static int sd_suspend(struct device *);
static int sd_resume(struct device *);
static void sd_rescan(struct device *);
static int sd_done(struct scsi_cmnd *);
@@ -465,15 +465,23 @@ static struct class sd_disk_class = {
.dev_attrs = sd_disk_attrs,
};
+static const struct dev_pm_ops sd_pm_ops = {
+ .suspend = sd_suspend,
+ .resume = sd_resume,
+ .poweroff = sd_suspend,
+ .restore = sd_resume,
+ .runtime_suspend = sd_suspend,
+ .runtime_resume = sd_resume,
+};
+
static struct scsi_driver sd_template = {
.owner = THIS_MODULE,
.gendrv = {
.name = "sd",
.probe = sd_probe,
.remove = sd_remove,
- .suspend = sd_suspend,
- .resume = sd_resume,
.shutdown = sd_shutdown,
+ .pm = &sd_pm_ops,
},
.rescan = sd_rescan,
.done = sd_done,
@@ -1011,7 +1019,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
SCpnt->cmnd[29] = (unsigned char) (this_count >> 16) & 0xff;
SCpnt->cmnd[30] = (unsigned char) (this_count >> 8) & 0xff;
SCpnt->cmnd[31] = (unsigned char) this_count & 0xff;
- } else if (block > 0xffffffff) {
+ } else if (sdp->use_16_for_rw) {
SCpnt->cmnd[0] += READ_16 - READ_6;
SCpnt->cmnd[1] = protect | ((rq->cmd_flags & REQ_FUA) ? 0x8 : 0);
SCpnt->cmnd[2] = sizeof(block) > 4 ? (unsigned char) (block >> 56) & 0xff : 0;
@@ -2203,6 +2211,8 @@ got_data:
}
}
+ sdp->use_16_for_rw = (sdkp->capacity > 0xffffffff);
+
/* Rescale capacity to 512-byte units */
if (sector_size == 4096)
sdkp->capacity <<= 3;
@@ -3052,7 +3062,7 @@ exit:
scsi_disk_put(sdkp);
}
-static int sd_suspend(struct device *dev, pm_message_t mesg)
+static int sd_suspend(struct device *dev)
{
struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev);
int ret = 0;
@@ -3067,7 +3077,7 @@ static int sd_suspend(struct device *dev, pm_message_t mesg)
goto done;
}
- if ((mesg.event & PM_EVENT_SLEEP) && sdkp->device->manage_start_stop) {
+ if (sdkp->device->manage_start_stop) {
sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n");
ret = sd_start_stop_device(sdkp, 0);
}
@@ -3116,10 +3126,6 @@ static int __init init_sd(void)
if (err)
goto err_out;
- err = scsi_register_driver(&sd_template.gendrv);
- if (err)
- goto err_out_class;
-
sd_cdb_cache = kmem_cache_create("sd_ext_cdb", SD_EXT_CDB_SIZE,
0, 0, NULL);
if (!sd_cdb_cache) {
@@ -3133,8 +3139,15 @@ static int __init init_sd(void)
goto err_out_cache;
}
+ err = scsi_register_driver(&sd_template.gendrv);
+ if (err)
+ goto err_out_driver;
+
return 0;
+err_out_driver:
+ mempool_destroy(sd_cdb_pool);
+
err_out_cache:
kmem_cache_destroy(sd_cdb_cache);
@@ -3157,10 +3170,10 @@ static void __exit exit_sd(void)
SCSI_LOG_HLQUEUE(3, printk("exit_sd: exiting sd driver\n"));
+ scsi_unregister_driver(&sd_template.gendrv);
mempool_destroy(sd_cdb_pool);
kmem_cache_destroy(sd_cdb_cache);
- scsi_unregister_driver(&sd_template.gendrv);
class_unregister(&sd_disk_class);
for (i = 0; i < SD_MAJORS; i++)
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
index 595af1ae442..dd8dc27fa32 100644
--- a/drivers/scsi/virtio_scsi.c
+++ b/drivers/scsi/virtio_scsi.c
@@ -469,6 +469,8 @@ static int virtscsi_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc)
sizeof cmd->req.cmd, sizeof cmd->resp.cmd,
GFP_ATOMIC) >= 0)
ret = 0;
+ else
+ mempool_free(cmd, virtscsi_cmd_pool);
out:
return ret;
diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c
index b3dc44146ca..5aedcdf4ac5 100644
--- a/drivers/sh/clk/cpg.c
+++ b/drivers/sh/clk/cpg.c
@@ -401,7 +401,6 @@ static int fsidiv_enable(struct clk *clk)
static int fsidiv_set_rate(struct clk *clk, unsigned long rate)
{
- u32 val;
int idx;
idx = (clk->parent->rate / rate) & 0xffff;
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 1acae359cab..2e188e1127e 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -123,6 +123,13 @@ config SPI_BUTTERFLY
inexpensive battery powered microcontroller evaluation board.
This same cable can be used to flash new firmware.
+config SPI_CLPS711X
+ tristate "CLPS711X host SPI controller"
+ depends on ARCH_CLPS711X
+ help
+ This enables dedicated general purpose SPI/Microwire1-compatible
+ master mode interface (SSI1) for CLPS711X-based CPUs.
+
config SPI_COLDFIRE_QSPI
tristate "Freescale Coldfire QSPI controller"
depends on (M520x || M523x || M5249 || M525x || M527x || M528x || M532x)
@@ -341,10 +348,10 @@ config SPI_SC18IS602
config SPI_SH_MSIOF
tristate "SuperH MSIOF SPI controller"
- depends on SUPERH && HAVE_CLK
+ depends on (SUPERH || ARCH_SHMOBILE) && HAVE_CLK
select SPI_BITBANG
help
- SPI driver for SuperH MSIOF blocks.
+ SPI driver for SuperH and SH Mobile MSIOF blocks.
config SPI_SH
tristate "SuperH SPI controller"
@@ -372,12 +379,6 @@ config SPI_SIRF
help
SPI driver for CSR SiRFprimaII SoCs
-config SPI_STMP3XXX
- tristate "Freescale STMP37xx/378x SPI/SSP controller"
- depends on ARCH_STMP3XXX
- help
- SPI driver for Freescale STMP37xx/378x SoC SSP interface
-
config SPI_MXS
tristate "Freescale MXS SPI controller"
depends on ARCH_MXS
@@ -385,6 +386,20 @@ config SPI_MXS
help
SPI driver for Freescale MXS devices.
+config SPI_TEGRA20_SFLASH
+ tristate "Nvidia Tegra20 Serial flash Controller"
+ depends on ARCH_TEGRA
+ help
+ SPI driver for Nvidia Tegra20 Serial flash Controller interface.
+ The main usecase of this controller is to use spi flash as boot
+ device.
+
+config SPI_TEGRA20_SLINK
+ tristate "Nvidia Tegra20/Tegra30 SLINK Controller"
+ depends on ARCH_TEGRA && TEGRA20_APB_DMA
+ help
+ SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface.
+
config SPI_TI_SSP
tristate "TI Sequencer Serial Port - SPI Support"
depends on MFD_TI_SSP
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index c48df47e4b0..64e970ba261 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o
obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o
obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o
obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o
+obj-$(CONFIG_SPI_CLPS711X) += spi-clps711x.o
obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o
obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o
obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o
@@ -59,11 +60,11 @@ obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o
obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o
obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o
obj-$(CONFIG_SPI_SIRF) += spi-sirf.o
-obj-$(CONFIG_SPI_STMP3XXX) += spi-stmp.o
+obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o
+obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o
obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o
obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o
obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o
obj-$(CONFIG_SPI_TXX9) += spi-txx9.o
obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o
obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o
-
diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c
index f1fec2a19d1..5e7314ac51e 100644
--- a/drivers/spi/spi-altera.c
+++ b/drivers/spi/spi-altera.c
@@ -215,7 +215,7 @@ static irqreturn_t altera_spi_irq(int irq, void *dev)
return IRQ_HANDLED;
}
-static int __devinit altera_spi_probe(struct platform_device *pdev)
+static int altera_spi_probe(struct platform_device *pdev)
{
struct altera_spi_platform_data *platp = pdev->dev.platform_data;
struct altera_spi *hw;
@@ -290,7 +290,7 @@ exit:
return err;
}
-static int __devexit altera_spi_remove(struct platform_device *dev)
+static int altera_spi_remove(struct platform_device *dev)
{
struct altera_spi *hw = platform_get_drvdata(dev);
struct spi_master *master = hw->bitbang.master;
@@ -311,7 +311,7 @@ MODULE_DEVICE_TABLE(of, altera_spi_match);
static struct platform_driver altera_spi_driver = {
.probe = altera_spi_probe,
- .remove = __devexit_p(altera_spi_remove),
+ .remove = altera_spi_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c
index 249077e5cc4..9a5d7791c5f 100644
--- a/drivers/spi/spi-ath79.c
+++ b/drivers/spi/spi-ath79.c
@@ -192,7 +192,7 @@ static u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned nsecs,
return ath79_spi_rr(sp, AR71XX_SPI_REG_RDS);
}
-static __devinit int ath79_spi_probe(struct platform_device *pdev)
+static int ath79_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct ath79_spi *sp;
@@ -251,7 +251,7 @@ err_put_master:
return ret;
}
-static __devexit int ath79_spi_remove(struct platform_device *pdev)
+static int ath79_spi_remove(struct platform_device *pdev)
{
struct ath79_spi *sp = platform_get_drvdata(pdev);
@@ -265,7 +265,7 @@ static __devexit int ath79_spi_remove(struct platform_device *pdev)
static struct platform_driver ath79_spi_driver = {
.probe = ath79_spi_probe,
- .remove = __devexit_p(ath79_spi_remove),
+ .remove = ath79_spi_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index 61fb0ec26f0..75c0c4f5fdf 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -907,7 +907,7 @@ static void atmel_spi_cleanup(struct spi_device *spi)
/*-------------------------------------------------------------------------*/
-static int __devinit atmel_spi_probe(struct platform_device *pdev)
+static int atmel_spi_probe(struct platform_device *pdev)
{
struct resource *regs;
int irq;
@@ -1003,7 +1003,7 @@ out_free:
return ret;
}
-static int __devexit atmel_spi_remove(struct platform_device *pdev)
+static int atmel_spi_remove(struct platform_device *pdev)
{
struct spi_master *master = platform_get_drvdata(pdev);
struct atmel_spi *as = spi_master_get_devdata(master);
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
index a9f4049c676..f44ab550853 100644
--- a/drivers/spi/spi-bcm63xx.c
+++ b/drivers/spi/spi-bcm63xx.c
@@ -36,7 +36,6 @@
#include <bcm63xx_dev_spi.h>
#define PFX KBUILD_MODNAME
-#define DRV_VER "0.1.2"
struct bcm63xx_spi {
struct completion done;
@@ -170,13 +169,6 @@ static int bcm63xx_spi_setup(struct spi_device *spi)
return -EINVAL;
}
- ret = bcm63xx_spi_check_transfer(spi, NULL);
- if (ret < 0) {
- dev_err(&spi->dev, "setup: unsupported mode bits %x\n",
- spi->mode & ~MODEBITS);
- return ret;
- }
-
dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n",
__func__, spi->mode & MODEBITS, spi->bits_per_word, 0);
@@ -337,7 +329,7 @@ static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id)
}
-static int __devinit bcm63xx_spi_probe(struct platform_device *pdev)
+static int bcm63xx_spi_probe(struct platform_device *pdev)
{
struct resource *r;
struct device *dev = &pdev->dev;
@@ -441,8 +433,8 @@ static int __devinit bcm63xx_spi_probe(struct platform_device *pdev)
goto out_clk_disable;
}
- dev_info(dev, "at 0x%08x (irq %d, FIFOs size %d) v%s\n",
- r->start, irq, bs->fifo_size, DRV_VER);
+ dev_info(dev, "at 0x%08x (irq %d, FIFOs size %d)\n",
+ r->start, irq, bs->fifo_size);
return 0;
@@ -457,7 +449,7 @@ out:
return ret;
}
-static int __devexit bcm63xx_spi_remove(struct platform_device *pdev)
+static int bcm63xx_spi_remove(struct platform_device *pdev)
{
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
struct bcm63xx_spi *bs = spi_master_get_devdata(master);
@@ -485,6 +477,8 @@ static int bcm63xx_spi_suspend(struct device *dev)
platform_get_drvdata(to_platform_device(dev));
struct bcm63xx_spi *bs = spi_master_get_devdata(master);
+ spi_master_suspend(master);
+
clk_disable(bs->clk);
return 0;
@@ -498,6 +492,8 @@ static int bcm63xx_spi_resume(struct device *dev)
clk_enable(bs->clk);
+ spi_master_resume(master);
+
return 0;
}
@@ -518,7 +514,7 @@ static struct platform_driver bcm63xx_spi_driver = {
.pm = BCM63XX_SPI_PM_OPS,
},
.probe = bcm63xx_spi_probe,
- .remove = __devexit_p(bcm63xx_spi_remove),
+ .remove = bcm63xx_spi_remove,
};
module_platform_driver(bcm63xx_spi_driver);
diff --git a/drivers/spi/spi-bfin-sport.c b/drivers/spi/spi-bfin-sport.c
index 6555ecd0730..ac7ffca7ba4 100644
--- a/drivers/spi/spi-bfin-sport.c
+++ b/drivers/spi/spi-bfin-sport.c
@@ -755,8 +755,7 @@ bfin_sport_spi_destroy_queue(struct bfin_sport_spi_master_data *drv_data)
return 0;
}
-static int __devinit
-bfin_sport_spi_probe(struct platform_device *pdev)
+static int bfin_sport_spi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct bfin5xx_spi_master *platform_info;
@@ -863,8 +862,7 @@ bfin_sport_spi_probe(struct platform_device *pdev)
}
/* stop hardware and remove the driver */
-static int __devexit
-bfin_sport_spi_remove(struct platform_device *pdev)
+static int bfin_sport_spi_remove(struct platform_device *pdev)
{
struct bfin_sport_spi_master_data *drv_data = platform_get_drvdata(pdev);
int status = 0;
@@ -935,7 +933,7 @@ static struct platform_driver bfin_sport_spi_driver = {
.owner = THIS_MODULE,
},
.probe = bfin_sport_spi_probe,
- .remove = __devexit_p(bfin_sport_spi_remove),
+ .remove = bfin_sport_spi_remove,
.suspend = bfin_sport_spi_suspend,
.resume = bfin_sport_spi_resume,
};
diff --git a/drivers/spi/spi-bfin5xx.c b/drivers/spi/spi-bfin5xx.c
index 9bb4d4af854..0429d833f75 100644
--- a/drivers/spi/spi-bfin5xx.c
+++ b/drivers/spi/spi-bfin5xx.c
@@ -1387,7 +1387,7 @@ out_error_get_res:
}
/* stop hardware and remove the driver */
-static int __devexit bfin_spi_remove(struct platform_device *pdev)
+static int bfin_spi_remove(struct platform_device *pdev)
{
struct bfin_spi_master_data *drv_data = platform_get_drvdata(pdev);
int status = 0;
@@ -1477,7 +1477,7 @@ static struct platform_driver bfin_spi_driver = {
},
.suspend = bfin_spi_suspend,
.resume = bfin_spi_resume,
- .remove = __devexit_p(bfin_spi_remove),
+ .remove = bfin_spi_remove,
};
static int __init bfin_spi_init(void)
diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c
index aef59b1a15f..8b3d8efafd3 100644
--- a/drivers/spi/spi-bitbang.c
+++ b/drivers/spi/spi-bitbang.c
@@ -260,11 +260,11 @@ static void bitbang_work(struct work_struct *work)
struct spi_bitbang *bitbang =
container_of(work, struct spi_bitbang, work);
unsigned long flags;
+ struct spi_message *m, *_m;
spin_lock_irqsave(&bitbang->lock, flags);
bitbang->busy = 1;
- while (!list_empty(&bitbang->queue)) {
- struct spi_message *m;
+ list_for_each_entry_safe(m, _m, &bitbang->queue, queue) {
struct spi_device *spi;
unsigned nsecs;
struct spi_transfer *t = NULL;
@@ -273,9 +273,7 @@ static void bitbang_work(struct work_struct *work)
int status;
int do_setup = -1;
- m = container_of(bitbang->queue.next, struct spi_message,
- queue);
- list_del_init(&m->queue);
+ list_del(&m->queue);
spin_unlock_irqrestore(&bitbang->lock, flags);
/* FIXME this is made-up ... the correct value is known to
@@ -346,17 +344,14 @@ static void bitbang_work(struct work_struct *work)
if (t->delay_usecs)
udelay(t->delay_usecs);
- if (!cs_change)
- continue;
- if (t->transfer_list.next == &m->transfers)
- break;
-
- /* sometimes a short mid-message deselect of the chip
- * may be needed to terminate a mode or command
- */
- ndelay(nsecs);
- bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
- ndelay(nsecs);
+ if (cs_change && !list_is_last(&t->transfer_list, &m->transfers)) {
+ /* sometimes a short mid-message deselect of the chip
+ * may be needed to terminate a mode or command
+ */
+ ndelay(nsecs);
+ bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
+ ndelay(nsecs);
+ }
}
m->status = status;
diff --git a/drivers/spi/spi-clps711x.c b/drivers/spi/spi-clps711x.c
new file mode 100644
index 00000000000..1366c4620d5
--- /dev/null
+++ b/drivers/spi/spi-clps711x.c
@@ -0,0 +1,296 @@
+/*
+ * CLPS711X SPI bus driver
+ *
+ * Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru>
+ *
+ * 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/io.h>
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/platform_data/spi-clps711x.h>
+
+#include <mach/hardware.h>
+
+#define DRIVER_NAME "spi-clps711x"
+
+struct spi_clps711x_data {
+ struct completion done;
+
+ struct clk *spi_clk;
+ u32 max_speed_hz;
+
+ u8 *tx_buf;
+ u8 *rx_buf;
+ int count;
+ int len;
+
+ int chipselect[0];
+};
+
+static int spi_clps711x_setup(struct spi_device *spi)
+{
+ struct spi_clps711x_data *hw = spi_master_get_devdata(spi->master);
+
+ if (spi->bits_per_word != 8) {
+ dev_err(&spi->dev, "Unsupported master bus width %i\n",
+ spi->bits_per_word);
+ return -EINVAL;
+ }
+
+ /* We are expect that SPI-device is not selected */
+ gpio_direction_output(hw->chipselect[spi->chip_select],
+ !(spi->mode & SPI_CS_HIGH));
+
+ return 0;
+}
+
+static void spi_clps711x_setup_mode(struct spi_device *spi)
+{
+ /* Setup edge for transfer */
+ if (spi->mode & SPI_CPHA)
+ clps_writew(clps_readw(SYSCON3) | SYSCON3_ADCCKNSEN, SYSCON3);
+ else
+ clps_writew(clps_readw(SYSCON3) & ~SYSCON3_ADCCKNSEN, SYSCON3);
+}
+
+static int spi_clps711x_setup_xfer(struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ u32 speed = xfer->speed_hz ? : spi->max_speed_hz;
+ u8 bpw = xfer->bits_per_word ? : spi->bits_per_word;
+ struct spi_clps711x_data *hw = spi_master_get_devdata(spi->master);
+
+ if (bpw != 8) {
+ dev_err(&spi->dev, "Unsupported master bus width %i\n", bpw);
+ return -EINVAL;
+ }
+
+ /* Setup SPI frequency divider */
+ if (!speed || (speed >= hw->max_speed_hz))
+ clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) |
+ SYSCON1_ADCKSEL(3), SYSCON1);
+ else if (speed >= (hw->max_speed_hz / 2))
+ clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) |
+ SYSCON1_ADCKSEL(2), SYSCON1);
+ else if (speed >= (hw->max_speed_hz / 8))
+ clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) |
+ SYSCON1_ADCKSEL(1), SYSCON1);
+ else
+ clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) |
+ SYSCON1_ADCKSEL(0), SYSCON1);
+
+ return 0;
+}
+
+static int spi_clps711x_transfer_one_message(struct spi_master *master,
+ struct spi_message *msg)
+{
+ struct spi_clps711x_data *hw = spi_master_get_devdata(master);
+ struct spi_transfer *xfer;
+ int status = 0, cs = hw->chipselect[msg->spi->chip_select];
+ u32 data;
+
+ spi_clps711x_setup_mode(msg->spi);
+
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ if (spi_clps711x_setup_xfer(msg->spi, xfer)) {
+ status = -EINVAL;
+ goto out_xfr;
+ }
+
+ gpio_set_value(cs, !!(msg->spi->mode & SPI_CS_HIGH));
+
+ INIT_COMPLETION(hw->done);
+
+ hw->count = 0;
+ hw->len = xfer->len;
+ hw->tx_buf = (u8 *)xfer->tx_buf;
+ hw->rx_buf = (u8 *)xfer->rx_buf;
+
+ /* Initiate transfer */
+ data = hw->tx_buf ? hw->tx_buf[hw->count] : 0;
+ clps_writel(data | SYNCIO_FRMLEN(8) | SYNCIO_TXFRMEN, SYNCIO);
+
+ wait_for_completion(&hw->done);
+
+ if (xfer->delay_usecs)
+ udelay(xfer->delay_usecs);
+
+ if (xfer->cs_change ||
+ list_is_last(&xfer->transfer_list, &msg->transfers))
+ gpio_set_value(cs, !(msg->spi->mode & SPI_CS_HIGH));
+
+ msg->actual_length += xfer->len;
+ }
+
+out_xfr:
+ msg->status = status;
+ spi_finalize_current_message(master);
+
+ return 0;
+}
+
+static irqreturn_t spi_clps711x_isr(int irq, void *dev_id)
+{
+ struct spi_clps711x_data *hw = (struct spi_clps711x_data *)dev_id;
+ u32 data;
+
+ /* Handle RX */
+ data = clps_readb(SYNCIO);
+ if (hw->rx_buf)
+ hw->rx_buf[hw->count] = (u8)data;
+
+ hw->count++;
+
+ /* Handle TX */
+ if (hw->count < hw->len) {
+ data = hw->tx_buf ? hw->tx_buf[hw->count] : 0;
+ clps_writel(data | SYNCIO_FRMLEN(8) | SYNCIO_TXFRMEN, SYNCIO);
+ } else
+ complete(&hw->done);
+
+ return IRQ_HANDLED;
+}
+
+static int spi_clps711x_probe(struct platform_device *pdev)
+{
+ int i, ret;
+ struct spi_master *master;
+ struct spi_clps711x_data *hw;
+ struct spi_clps711x_pdata *pdata = dev_get_platdata(&pdev->dev);
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data supplied\n");
+ return -EINVAL;
+ }
+
+ if (pdata->num_chipselect < 1) {
+ dev_err(&pdev->dev, "At least one CS must be defined\n");
+ return -EINVAL;
+ }
+
+ master = spi_alloc_master(&pdev->dev,
+ sizeof(struct spi_clps711x_data) +
+ sizeof(int) * pdata->num_chipselect);
+ if (!master) {
+ dev_err(&pdev->dev, "SPI allocating memory error\n");
+ return -ENOMEM;
+ }
+
+ master->bus_num = pdev->id;
+ master->mode_bits = SPI_CPHA | SPI_CS_HIGH;
+ master->num_chipselect = pdata->num_chipselect;
+ master->setup = spi_clps711x_setup;
+ master->transfer_one_message = spi_clps711x_transfer_one_message;
+
+ hw = spi_master_get_devdata(master);
+
+ for (i = 0; i < master->num_chipselect; i++) {
+ hw->chipselect[i] = pdata->chipselect[i];
+ if (!gpio_is_valid(hw->chipselect[i])) {
+ dev_err(&pdev->dev, "Invalid CS GPIO %i\n", i);
+ ret = -EINVAL;
+ goto err_out;
+ }
+ if (gpio_request(hw->chipselect[i], DRIVER_NAME)) {
+ dev_err(&pdev->dev, "Can't get CS GPIO %i\n", i);
+ ret = -EINVAL;
+ goto err_out;
+ }
+ }
+
+ hw->spi_clk = devm_clk_get(&pdev->dev, "spi");
+ if (IS_ERR(hw->spi_clk)) {
+ dev_err(&pdev->dev, "Can't get clocks\n");
+ ret = PTR_ERR(hw->spi_clk);
+ goto err_out;
+ }
+ hw->max_speed_hz = clk_get_rate(hw->spi_clk);
+
+ init_completion(&hw->done);
+ platform_set_drvdata(pdev, master);
+
+ /* Disable extended mode due hardware problems */
+ clps_writew(clps_readw(SYSCON3) & ~SYSCON3_ADCCON, SYSCON3);
+
+ /* Clear possible pending interrupt */
+ clps_readl(SYNCIO);
+
+ ret = devm_request_irq(&pdev->dev, IRQ_SSEOTI, spi_clps711x_isr, 0,
+ dev_name(&pdev->dev), hw);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't request IRQ\n");
+ clk_put(hw->spi_clk);
+ goto clk_out;
+ }
+
+ ret = spi_register_master(master);
+ if (!ret) {
+ dev_info(&pdev->dev,
+ "SPI bus driver initialized. Master clock %u Hz\n",
+ hw->max_speed_hz);
+ return 0;
+ }
+
+ dev_err(&pdev->dev, "Failed to register master\n");
+ devm_free_irq(&pdev->dev, IRQ_SSEOTI, hw);
+
+clk_out:
+ devm_clk_put(&pdev->dev, hw->spi_clk);
+
+err_out:
+ while (--i >= 0)
+ if (gpio_is_valid(hw->chipselect[i]))
+ gpio_free(hw->chipselect[i]);
+
+ platform_set_drvdata(pdev, NULL);
+ spi_master_put(master);
+ kfree(master);
+
+ return ret;
+}
+
+static int spi_clps711x_remove(struct platform_device *pdev)
+{
+ int i;
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct spi_clps711x_data *hw = spi_master_get_devdata(master);
+
+ devm_free_irq(&pdev->dev, IRQ_SSEOTI, hw);
+
+ for (i = 0; i < master->num_chipselect; i++)
+ if (gpio_is_valid(hw->chipselect[i]))
+ gpio_free(hw->chipselect[i]);
+
+ devm_clk_put(&pdev->dev, hw->spi_clk);
+ platform_set_drvdata(pdev, NULL);
+ spi_unregister_master(master);
+ kfree(master);
+
+ return 0;
+}
+
+static struct platform_driver clps711x_spi_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = spi_clps711x_probe,
+ .remove = spi_clps711x_remove,
+};
+module_platform_driver(clps711x_spi_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("CLPS711X SPI bus driver");
diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c
index 764bfee7592..58466b810da 100644
--- a/drivers/spi/spi-coldfire-qspi.c
+++ b/drivers/spi/spi-coldfire-qspi.c
@@ -401,7 +401,7 @@ static int mcfqspi_setup(struct spi_device *spi)
return 0;
}
-static int __devinit mcfqspi_probe(struct platform_device *pdev)
+static int mcfqspi_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct mcfqspi *mcfqspi;
@@ -515,7 +515,7 @@ fail0:
return status;
}
-static int __devexit mcfqspi_remove(struct platform_device *pdev)
+static int mcfqspi_remove(struct platform_device *pdev)
{
struct spi_master *master = platform_get_drvdata(pdev);
struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
@@ -594,7 +594,7 @@ static struct platform_driver mcfqspi_driver = {
.driver.owner = THIS_MODULE,
.driver.pm = &mcfqspi_pm,
.probe = mcfqspi_probe,
- .remove = __devexit_p(mcfqspi_remove),
+ .remove = mcfqspi_remove,
};
module_platform_driver(mcfqspi_driver);
diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c
index 147dfa87a64..13661e129d9 100644
--- a/drivers/spi/spi-davinci.c
+++ b/drivers/spi/spi-davinci.c
@@ -769,7 +769,7 @@ rx_dma_failed:
* It will invoke spi_bitbang_start to create work queue so that client driver
* can register transfer method to work queue.
*/
-static int __devinit davinci_spi_probe(struct platform_device *pdev)
+static int davinci_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct davinci_spi *dspi;
@@ -952,7 +952,7 @@ err:
* It will also call spi_bitbang_stop to destroy the work queue which was
* created by spi_bitbang_start.
*/
-static int __devexit davinci_spi_remove(struct platform_device *pdev)
+static int davinci_spi_remove(struct platform_device *pdev)
{
struct davinci_spi *dspi;
struct spi_master *master;
@@ -980,7 +980,7 @@ static struct platform_driver davinci_spi_driver = {
.owner = THIS_MODULE,
},
.probe = davinci_spi_probe,
- .remove = __devexit_p(davinci_spi_remove),
+ .remove = davinci_spi_remove,
};
module_platform_driver(davinci_spi_driver);
diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c
index db2f1ba06ea..4a6d5c9057a 100644
--- a/drivers/spi/spi-dw-mmio.c
+++ b/drivers/spi/spi-dw-mmio.c
@@ -26,7 +26,7 @@ struct dw_spi_mmio {
struct clk *clk;
};
-static int __devinit dw_spi_mmio_probe(struct platform_device *pdev)
+static int dw_spi_mmio_probe(struct platform_device *pdev)
{
struct dw_spi_mmio *dwsmmio;
struct dw_spi *dws;
@@ -106,7 +106,7 @@ err_end:
return ret;
}
-static int __devexit dw_spi_mmio_remove(struct platform_device *pdev)
+static int dw_spi_mmio_remove(struct platform_device *pdev)
{
struct dw_spi_mmio *dwsmmio = platform_get_drvdata(pdev);
struct resource *mem;
@@ -129,7 +129,7 @@ static int __devexit dw_spi_mmio_remove(struct platform_device *pdev)
static struct platform_driver dw_spi_mmio_driver = {
.probe = dw_spi_mmio_probe,
- .remove = __devexit_p(dw_spi_mmio_remove),
+ .remove = dw_spi_mmio_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c
index ff81abbb306..6055c8d9fdd 100644
--- a/drivers/spi/spi-dw-pci.c
+++ b/drivers/spi/spi-dw-pci.c
@@ -32,7 +32,7 @@ struct dw_spi_pci {
struct dw_spi dws;
};
-static int __devinit spi_pci_probe(struct pci_dev *pdev,
+static int spi_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct dw_spi_pci *dwpci;
@@ -105,7 +105,7 @@ err_disable:
return ret;
}
-static void __devexit spi_pci_remove(struct pci_dev *pdev)
+static void spi_pci_remove(struct pci_dev *pdev)
{
struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
@@ -159,7 +159,7 @@ static struct pci_driver dw_spi_driver = {
.name = DRIVER_NAME,
.id_table = pci_ids,
.probe = spi_pci_probe,
- .remove = __devexit_p(spi_pci_remove),
+ .remove = spi_pci_remove,
.suspend = spi_suspend,
.resume = spi_resume,
};
diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c
index d1a495f64e2..c1abc06899e 100644
--- a/drivers/spi/spi-dw.c
+++ b/drivers/spi/spi-dw.c
@@ -696,7 +696,7 @@ static void dw_spi_cleanup(struct spi_device *spi)
kfree(chip);
}
-static int __devinit init_queue(struct dw_spi *dws)
+static int init_queue(struct dw_spi *dws)
{
INIT_LIST_HEAD(&dws->queue);
spin_lock_init(&dws->lock);
@@ -795,7 +795,7 @@ static void spi_hw_init(struct dw_spi *dws)
}
}
-int __devinit dw_spi_add_host(struct dw_spi *dws)
+int dw_spi_add_host(struct dw_spi *dws)
{
struct spi_master *master;
int ret;
@@ -877,7 +877,7 @@ exit:
}
EXPORT_SYMBOL_GPL(dw_spi_add_host);
-void __devexit dw_spi_remove_host(struct dw_spi *dws)
+void dw_spi_remove_host(struct dw_spi *dws)
{
int status = 0;
diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c
index 3a219599612..acb1e1935c5 100644
--- a/drivers/spi/spi-ep93xx.c
+++ b/drivers/spi/spi-ep93xx.c
@@ -1023,7 +1023,7 @@ static void ep93xx_spi_release_dma(struct ep93xx_spi *espi)
free_page((unsigned long)espi->zeropage);
}
-static int __devinit ep93xx_spi_probe(struct platform_device *pdev)
+static int ep93xx_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct ep93xx_spi_info *info;
@@ -1138,7 +1138,7 @@ fail_release_master:
return error;
}
-static int __devexit ep93xx_spi_remove(struct platform_device *pdev)
+static int ep93xx_spi_remove(struct platform_device *pdev)
{
struct spi_master *master = platform_get_drvdata(pdev);
struct ep93xx_spi *espi = spi_master_get_devdata(master);
@@ -1180,7 +1180,7 @@ static struct platform_driver ep93xx_spi_driver = {
.owner = THIS_MODULE,
},
.probe = ep93xx_spi_probe,
- .remove = __devexit_p(ep93xx_spi_remove),
+ .remove = ep93xx_spi_remove,
};
module_platform_driver(ep93xx_spi_driver);
diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c
index 8f6aa735a24..6a6f62ec284 100644
--- a/drivers/spi/spi-falcon.c
+++ b/drivers/spi/spi-falcon.c
@@ -403,7 +403,7 @@ static int falcon_sflash_xfer_one(struct spi_master *master,
return 0;
}
-static int __devinit falcon_sflash_probe(struct platform_device *pdev)
+static int falcon_sflash_probe(struct platform_device *pdev)
{
struct falcon_sflash *priv;
struct spi_master *master;
@@ -438,7 +438,7 @@ static int __devinit falcon_sflash_probe(struct platform_device *pdev)
return ret;
}
-static int __devexit falcon_sflash_remove(struct platform_device *pdev)
+static int falcon_sflash_remove(struct platform_device *pdev)
{
struct falcon_sflash *priv = platform_get_drvdata(pdev);
@@ -455,7 +455,7 @@ MODULE_DEVICE_TABLE(of, falcon_sflash_match);
static struct platform_driver falcon_sflash_driver = {
.probe = falcon_sflash_probe,
- .remove = __devexit_p(falcon_sflash_remove),
+ .remove = falcon_sflash_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index 27bdc47b525..24610ca8955 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -587,7 +587,7 @@ static void fsl_espi_remove(struct mpc8xxx_spi *mspi)
iounmap(mspi->reg_base);
}
-static struct spi_master * __devinit fsl_espi_probe(struct device *dev,
+static struct spi_master * fsl_espi_probe(struct device *dev,
struct resource *mem, unsigned int irq)
{
struct fsl_spi_platform_data *pdata = dev->platform_data;
@@ -686,7 +686,7 @@ static int of_fsl_espi_get_chipselects(struct device *dev)
return 0;
}
-static int __devinit of_fsl_espi_probe(struct platform_device *ofdev)
+static int of_fsl_espi_probe(struct platform_device *ofdev)
{
struct device *dev = &ofdev->dev;
struct device_node *np = ofdev->dev.of_node;
@@ -725,7 +725,7 @@ err:
return ret;
}
-static int __devexit of_fsl_espi_remove(struct platform_device *dev)
+static int of_fsl_espi_remove(struct platform_device *dev)
{
return mpc8xxx_spi_remove(&dev->dev);
}
@@ -743,7 +743,7 @@ static struct platform_driver fsl_espi_driver = {
.of_match_table = of_fsl_espi_match,
},
.probe = of_fsl_espi_probe,
- .remove = __devexit_p(of_fsl_espi_remove),
+ .remove = of_fsl_espi_remove,
};
module_platform_driver(fsl_espi_driver);
diff --git a/drivers/spi/spi-fsl-lib.c b/drivers/spi/spi-fsl-lib.c
index 1503574b215..8ade675a04f 100644
--- a/drivers/spi/spi-fsl-lib.c
+++ b/drivers/spi/spi-fsl-lib.c
@@ -169,7 +169,7 @@ err:
return ret;
}
-int __devexit mpc8xxx_spi_remove(struct device *dev)
+int mpc8xxx_spi_remove(struct device *dev)
{
struct mpc8xxx_spi *mpc8xxx_spi;
struct spi_master *master;
@@ -189,7 +189,7 @@ int __devexit mpc8xxx_spi_remove(struct device *dev)
return 0;
}
-int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev)
+int of_mpc8xxx_spi_probe(struct platform_device *ofdev)
{
struct device *dev = &ofdev->dev;
struct device_node *np = ofdev->dev.of_node;
diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c
index 6a62934ca74..1a7f6359d99 100644
--- a/drivers/spi/spi-fsl-spi.c
+++ b/drivers/spi/spi-fsl-spi.c
@@ -843,7 +843,7 @@ static void fsl_spi_remove(struct mpc8xxx_spi *mspi)
fsl_spi_cpm_free(mspi);
}
-static struct spi_master * __devinit fsl_spi_probe(struct device *dev,
+static struct spi_master * fsl_spi_probe(struct device *dev,
struct resource *mem, unsigned int irq)
{
struct fsl_spi_platform_data *pdata = dev->platform_data;
@@ -1041,7 +1041,7 @@ static int of_fsl_spi_free_chipselects(struct device *dev)
return 0;
}
-static int __devinit of_fsl_spi_probe(struct platform_device *ofdev)
+static int of_fsl_spi_probe(struct platform_device *ofdev)
{
struct device *dev = &ofdev->dev;
struct device_node *np = ofdev->dev.of_node;
@@ -1081,7 +1081,7 @@ err:
return ret;
}
-static int __devexit of_fsl_spi_remove(struct platform_device *ofdev)
+static int of_fsl_spi_remove(struct platform_device *ofdev)
{
int ret;
@@ -1105,7 +1105,7 @@ static struct platform_driver of_fsl_spi_driver = {
.of_match_table = of_fsl_spi_match,
},
.probe = of_fsl_spi_probe,
- .remove = __devexit_p(of_fsl_spi_remove),
+ .remove = of_fsl_spi_remove,
};
#ifdef CONFIG_MPC832x_RDB
@@ -1116,7 +1116,7 @@ static struct platform_driver of_fsl_spi_driver = {
* tree can work with OpenFirmware driver. But for now we support old trees
* as well.
*/
-static int __devinit plat_mpc8xxx_spi_probe(struct platform_device *pdev)
+static int plat_mpc8xxx_spi_probe(struct platform_device *pdev)
{
struct resource *mem;
int irq;
@@ -1139,7 +1139,7 @@ static int __devinit plat_mpc8xxx_spi_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit plat_mpc8xxx_spi_remove(struct platform_device *pdev)
+static int plat_mpc8xxx_spi_remove(struct platform_device *pdev)
{
return mpc8xxx_spi_remove(&pdev->dev);
}
@@ -1147,7 +1147,7 @@ static int __devexit plat_mpc8xxx_spi_remove(struct platform_device *pdev)
MODULE_ALIAS("platform:mpc8xxx_spi");
static struct platform_driver mpc8xxx_spi_driver = {
.probe = plat_mpc8xxx_spi_probe,
- .remove = __devexit_p(plat_mpc8xxx_spi_remove),
+ .remove = plat_mpc8xxx_spi_remove,
.driver = {
.name = "mpc8xxx_spi",
.owner = THIS_MODULE,
diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c
index a2b50c516b3..c7cf0b7a069 100644
--- a/drivers/spi/spi-gpio.c
+++ b/drivers/spi/spi-gpio.c
@@ -287,7 +287,7 @@ static void spi_gpio_cleanup(struct spi_device *spi)
spi_bitbang_cleanup(spi);
}
-static int __devinit spi_gpio_alloc(unsigned pin, const char *label, bool is_in)
+static int spi_gpio_alloc(unsigned pin, const char *label, bool is_in)
{
int value;
@@ -301,9 +301,8 @@ static int __devinit spi_gpio_alloc(unsigned pin, const char *label, bool is_in)
return value;
}
-static int __devinit
-spi_gpio_request(struct spi_gpio_platform_data *pdata, const char *label,
- u16 *res_flags)
+static int spi_gpio_request(struct spi_gpio_platform_data *pdata,
+ const char *label, u16 *res_flags)
{
int value;
@@ -392,7 +391,7 @@ static inline int spi_gpio_probe_dt(struct platform_device *pdev)
}
#endif
-static int __devinit spi_gpio_probe(struct platform_device *pdev)
+static int spi_gpio_probe(struct platform_device *pdev)
{
int status;
struct spi_master *master;
@@ -485,7 +484,7 @@ gpio_free:
return status;
}
-static int __devexit spi_gpio_remove(struct platform_device *pdev)
+static int spi_gpio_remove(struct platform_device *pdev)
{
struct spi_gpio *spi_gpio;
struct spi_gpio_platform_data *pdata;
@@ -518,7 +517,7 @@ static struct platform_driver spi_gpio_driver = {
.of_match_table = of_match_ptr(spi_gpio_dt_ids),
},
.probe = spi_gpio_probe,
- .remove = __devexit_p(spi_gpio_remove),
+ .remove = spi_gpio_remove,
};
module_platform_driver(spi_gpio_driver);
diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index c9a0d8467de..904913290aa 100644
--- a/drivers/spi/spi-imx.c
+++ b/drivers/spi/spi-imx.c
@@ -750,7 +750,7 @@ static void spi_imx_cleanup(struct spi_device *spi)
{
}
-static int __devinit spi_imx_probe(struct platform_device *pdev)
+static int spi_imx_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id =
@@ -906,7 +906,7 @@ out_gpio_free:
return ret;
}
-static int __devexit spi_imx_remove(struct platform_device *pdev)
+static int spi_imx_remove(struct platform_device *pdev)
{
struct spi_master *master = platform_get_drvdata(pdev);
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -942,7 +942,7 @@ static struct platform_driver spi_imx_driver = {
},
.id_table = spi_imx_devtype,
.probe = spi_imx_probe,
- .remove = __devexit_p(spi_imx_remove),
+ .remove = spi_imx_remove,
};
module_platform_driver(spi_imx_driver);
diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c
index 0a1e39e94d0..cb3a3106bd4 100644
--- a/drivers/spi/spi-mpc512x-psc.c
+++ b/drivers/spi/spi-mpc512x-psc.c
@@ -406,7 +406,7 @@ static irqreturn_t mpc512x_psc_spi_isr(int irq, void *dev_id)
}
/* bus_num is used only for the case dev->platform_data == NULL */
-static int __devinit mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
+static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
u32 size, unsigned int irq,
s16 bus_num)
{
@@ -492,7 +492,7 @@ free_master:
return ret;
}
-static int __devexit mpc512x_psc_spi_do_remove(struct device *dev)
+static int mpc512x_psc_spi_do_remove(struct device *dev)
{
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
struct mpc512x_psc_spi *mps = spi_master_get_devdata(master);
@@ -508,7 +508,7 @@ static int __devexit mpc512x_psc_spi_do_remove(struct device *dev)
return 0;
}
-static int __devinit mpc512x_psc_spi_of_probe(struct platform_device *op)
+static int mpc512x_psc_spi_of_probe(struct platform_device *op)
{
const u32 *regaddr_p;
u64 regaddr64, size64;
@@ -539,7 +539,7 @@ static int __devinit mpc512x_psc_spi_of_probe(struct platform_device *op)
irq_of_parse_and_map(op->dev.of_node, 0), id);
}
-static int __devexit mpc512x_psc_spi_of_remove(struct platform_device *op)
+static int mpc512x_psc_spi_of_remove(struct platform_device *op)
{
return mpc512x_psc_spi_do_remove(&op->dev);
}
@@ -553,7 +553,7 @@ MODULE_DEVICE_TABLE(of, mpc512x_psc_spi_of_match);
static struct platform_driver mpc512x_psc_spi_of_driver = {
.probe = mpc512x_psc_spi_of_probe,
- .remove = __devexit_p(mpc512x_psc_spi_of_remove),
+ .remove = mpc512x_psc_spi_of_remove,
.driver = {
.name = "mpc512x-psc-spi",
.owner = THIS_MODULE,
diff --git a/drivers/spi/spi-mpc52xx-psc.c b/drivers/spi/spi-mpc52xx-psc.c
index bd47d262d53..291120b37db 100644
--- a/drivers/spi/spi-mpc52xx-psc.c
+++ b/drivers/spi/spi-mpc52xx-psc.c
@@ -363,7 +363,7 @@ static irqreturn_t mpc52xx_psc_spi_isr(int irq, void *dev_id)
}
/* bus_num is used only for the case dev->platform_data == NULL */
-static int __devinit mpc52xx_psc_spi_do_probe(struct device *dev, u32 regaddr,
+static int mpc52xx_psc_spi_do_probe(struct device *dev, u32 regaddr,
u32 size, unsigned int irq, s16 bus_num)
{
struct fsl_spi_platform_data *pdata = dev->platform_data;
@@ -450,7 +450,7 @@ free_master:
return ret;
}
-static int __devinit mpc52xx_psc_spi_of_probe(struct platform_device *op)
+static int mpc52xx_psc_spi_of_probe(struct platform_device *op)
{
const u32 *regaddr_p;
u64 regaddr64, size64;
@@ -479,7 +479,7 @@ static int __devinit mpc52xx_psc_spi_of_probe(struct platform_device *op)
irq_of_parse_and_map(op->dev.of_node, 0), id);
}
-static int __devexit mpc52xx_psc_spi_of_remove(struct platform_device *op)
+static int mpc52xx_psc_spi_of_remove(struct platform_device *op)
{
struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev));
struct mpc52xx_psc_spi *mps = spi_master_get_devdata(master);
@@ -505,7 +505,7 @@ MODULE_DEVICE_TABLE(of, mpc52xx_psc_spi_of_match);
static struct platform_driver mpc52xx_psc_spi_of_driver = {
.probe = mpc52xx_psc_spi_of_probe,
- .remove = __devexit_p(mpc52xx_psc_spi_of_remove),
+ .remove = mpc52xx_psc_spi_of_remove,
.driver = {
.name = "mpc52xx-psc-spi",
.owner = THIS_MODULE,
diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c
index 04541065021..29f77056eed 100644
--- a/drivers/spi/spi-mpc52xx.c
+++ b/drivers/spi/spi-mpc52xx.c
@@ -390,7 +390,7 @@ static int mpc52xx_spi_transfer(struct spi_device *spi, struct spi_message *m)
/*
* OF Platform Bus Binding
*/
-static int __devinit mpc52xx_spi_probe(struct platform_device *op)
+static int mpc52xx_spi_probe(struct platform_device *op)
{
struct spi_master *master;
struct mpc52xx_spi *ms;
@@ -527,7 +527,7 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op)
return rc;
}
-static int __devexit mpc52xx_spi_remove(struct platform_device *op)
+static int mpc52xx_spi_remove(struct platform_device *op)
{
struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev));
struct mpc52xx_spi *ms = spi_master_get_devdata(master);
@@ -547,7 +547,7 @@ static int __devexit mpc52xx_spi_remove(struct platform_device *op)
return 0;
}
-static const struct of_device_id mpc52xx_spi_match[] __devinitconst = {
+static const struct of_device_id mpc52xx_spi_match[] = {
{ .compatible = "fsl,mpc5200-spi", },
{}
};
@@ -560,6 +560,6 @@ static struct platform_driver mpc52xx_spi_of_driver = {
.of_match_table = mpc52xx_spi_match,
},
.probe = mpc52xx_spi_probe,
- .remove = __devexit_p(mpc52xx_spi_remove),
+ .remove = mpc52xx_spi_remove,
};
module_platform_driver(mpc52xx_spi_of_driver);
diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c
index 86dd04d6bc8..a3ede249d05 100644
--- a/drivers/spi/spi-mxs.c
+++ b/drivers/spi/spi-mxs.c
@@ -509,7 +509,7 @@ static const struct of_device_id mxs_spi_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, mxs_spi_dt_ids);
-static int __devinit mxs_spi_probe(struct platform_device *pdev)
+static int mxs_spi_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id =
of_match_device(mxs_spi_dt_ids, &pdev->dev);
@@ -636,7 +636,7 @@ out_master_free:
return ret;
}
-static int __devexit mxs_spi_remove(struct platform_device *pdev)
+static int mxs_spi_remove(struct platform_device *pdev)
{
struct spi_master *master;
struct mxs_spi *spi;
@@ -659,7 +659,7 @@ static int __devexit mxs_spi_remove(struct platform_device *pdev)
static struct platform_driver mxs_spi_driver = {
.probe = mxs_spi_probe,
- .remove = __devexit_p(mxs_spi_remove),
+ .remove = mxs_spi_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
diff --git a/drivers/spi/spi-nuc900.c b/drivers/spi/spi-nuc900.c
index a6eca6ffdab..b3f9ec83ef7 100644
--- a/drivers/spi/spi-nuc900.c
+++ b/drivers/spi/spi-nuc900.c
@@ -346,7 +346,7 @@ static void nuc900_init_spi(struct nuc900_spi *hw)
nuc900_enable_int(hw);
}
-static int __devinit nuc900_spi_probe(struct platform_device *pdev)
+static int nuc900_spi_probe(struct platform_device *pdev)
{
struct nuc900_spi *hw;
struct spi_master *master;
@@ -453,7 +453,7 @@ err_nomem:
return err;
}
-static int __devexit nuc900_spi_remove(struct platform_device *dev)
+static int nuc900_spi_remove(struct platform_device *dev)
{
struct nuc900_spi *hw = platform_get_drvdata(dev);
@@ -477,7 +477,7 @@ static int __devexit nuc900_spi_remove(struct platform_device *dev)
static struct platform_driver nuc900_spi_driver = {
.probe = nuc900_spi_probe,
- .remove = __devexit_p(nuc900_spi_remove),
+ .remove = nuc900_spi_remove,
.driver = {
.name = "nuc900-spi",
.owner = THIS_MODULE,
diff --git a/drivers/spi/spi-oc-tiny.c b/drivers/spi/spi-oc-tiny.c
index 9d9071b730b..432e66ec308 100644
--- a/drivers/spi/spi-oc-tiny.c
+++ b/drivers/spi/spi-oc-tiny.c
@@ -243,7 +243,7 @@ static irqreturn_t tiny_spi_irq(int irq, void *dev)
#ifdef CONFIG_OF
#include <linux/of_gpio.h>
-static int __devinit tiny_spi_of_probe(struct platform_device *pdev)
+static int tiny_spi_of_probe(struct platform_device *pdev)
{
struct tiny_spi *hw = platform_get_drvdata(pdev);
struct device_node *np = pdev->dev.of_node;
@@ -277,13 +277,13 @@ static int __devinit tiny_spi_of_probe(struct platform_device *pdev)
return 0;
}
#else /* !CONFIG_OF */
-static int __devinit tiny_spi_of_probe(struct platform_device *pdev)
+static int tiny_spi_of_probe(struct platform_device *pdev)
{
return 0;
}
#endif /* CONFIG_OF */
-static int __devinit tiny_spi_probe(struct platform_device *pdev)
+static int tiny_spi_probe(struct platform_device *pdev)
{
struct tiny_spi_platform_data *platp = pdev->dev.platform_data;
struct tiny_spi *hw;
@@ -373,7 +373,7 @@ exit:
return err;
}
-static int __devexit tiny_spi_remove(struct platform_device *pdev)
+static int tiny_spi_remove(struct platform_device *pdev)
{
struct tiny_spi *hw = platform_get_drvdata(pdev);
struct spi_master *master = hw->bitbang.master;
@@ -399,7 +399,7 @@ MODULE_DEVICE_TABLE(of, tiny_spi_match);
static struct platform_driver tiny_spi_driver = {
.probe = tiny_spi_probe,
- .remove = __devexit_p(tiny_spi_remove),
+ .remove = tiny_spi_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c
index ea8fb2efb0f..24daf964a40 100644
--- a/drivers/spi/spi-octeon.c
+++ b/drivers/spi/spi-octeon.c
@@ -266,7 +266,7 @@ static int octeon_spi_nop_transfer_hardware(struct spi_master *master)
return 0;
}
-static int __devinit octeon_spi_probe(struct platform_device *pdev)
+static int octeon_spi_probe(struct platform_device *pdev)
{
struct resource *res_mem;
@@ -326,7 +326,7 @@ fail:
return err;
}
-static int __devexit octeon_spi_remove(struct platform_device *pdev)
+static int octeon_spi_remove(struct platform_device *pdev)
{
struct octeon_spi *p = platform_get_drvdata(pdev);
u64 register_base = p->register_base;
@@ -352,7 +352,7 @@ static struct platform_driver octeon_spi_driver = {
.of_match_table = octeon_spi_match,
},
.probe = octeon_spi_probe,
- .remove = __devexit_p(octeon_spi_remove),
+ .remove = octeon_spi_remove,
};
module_platform_driver(octeon_spi_driver);
diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c
index dfb4b7f448c..3aef7fa7d5b 100644
--- a/drivers/spi/spi-omap-100k.c
+++ b/drivers/spi/spi-omap-100k.c
@@ -486,7 +486,7 @@ static int __init omap1_spi100k_reset(struct omap1_spi100k *spi100k)
return 0;
}
-static int __devinit omap1_spi100k_probe(struct platform_device *pdev)
+static int omap1_spi100k_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct omap1_spi100k *spi100k;
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index 3542fdc664b..b610f522ca4 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -39,7 +39,6 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
-#include <linux/err.h>
#include <linux/spi/spi.h>
@@ -130,6 +129,7 @@ struct omap2_mcspi {
struct omap2_mcspi_dma *dma_channels;
struct device *dev;
struct omap2_mcspi_regs ctx;
+ unsigned int pin_dir:1;
};
struct omap2_mcspi_cs {
@@ -323,19 +323,11 @@ static void omap2_mcspi_tx_dma(struct spi_device *spi,
struct omap2_mcspi *mcspi;
struct omap2_mcspi_dma *mcspi_dma;
unsigned int count;
- u8 * rx;
- const u8 * tx;
- void __iomem *chstat_reg;
- struct omap2_mcspi_cs *cs = spi->controller_state;
mcspi = spi_master_get_devdata(spi->master);
mcspi_dma = &mcspi->dma_channels[spi->chip_select];
count = xfer->len;
- rx = xfer->rx_buf;
- tx = xfer->tx_buf;
- chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
-
if (mcspi_dma->dma_tx) {
struct dma_async_tx_descriptor *tx;
struct scatterlist sg;
@@ -359,19 +351,6 @@ static void omap2_mcspi_tx_dma(struct spi_device *spi,
dma_async_issue_pending(mcspi_dma->dma_tx);
omap2_mcspi_set_dma_req(spi, 0, 1);
- wait_for_completion(&mcspi_dma->dma_tx_completion);
- dma_unmap_single(mcspi->dev, xfer->tx_dma, count,
- DMA_TO_DEVICE);
-
- /* for TX_ONLY mode, be sure all words have shifted out */
- if (rx == NULL) {
- if (mcspi_wait_for_reg_bit(chstat_reg,
- OMAP2_MCSPI_CHSTAT_TXS) < 0)
- dev_err(&spi->dev, "TXS timed out\n");
- else if (mcspi_wait_for_reg_bit(chstat_reg,
- OMAP2_MCSPI_CHSTAT_EOT) < 0)
- dev_err(&spi->dev, "EOT timed out\n");
- }
}
static unsigned
@@ -492,6 +471,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
struct dma_slave_config cfg;
enum dma_slave_buswidth width;
unsigned es;
+ void __iomem *chstat_reg;
mcspi = spi_master_get_devdata(spi->master);
mcspi_dma = &mcspi->dma_channels[spi->chip_select];
@@ -526,8 +506,24 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
omap2_mcspi_tx_dma(spi, xfer, cfg);
if (rx != NULL)
- return omap2_mcspi_rx_dma(spi, xfer, cfg, es);
-
+ count = omap2_mcspi_rx_dma(spi, xfer, cfg, es);
+
+ if (tx != NULL) {
+ chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
+ wait_for_completion(&mcspi_dma->dma_tx_completion);
+ dma_unmap_single(mcspi->dev, xfer->tx_dma, xfer->len,
+ DMA_TO_DEVICE);
+
+ /* for TX_ONLY mode, be sure all words have shifted out */
+ if (rx == NULL) {
+ if (mcspi_wait_for_reg_bit(chstat_reg,
+ OMAP2_MCSPI_CHSTAT_TXS) < 0)
+ dev_err(&spi->dev, "TXS timed out\n");
+ else if (mcspi_wait_for_reg_bit(chstat_reg,
+ OMAP2_MCSPI_CHSTAT_EOT) < 0)
+ dev_err(&spi->dev, "EOT timed out\n");
+ }
+ }
return count;
}
@@ -765,8 +761,15 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
/* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS
* REVISIT: this controller could support SPI_3WIRE mode.
*/
- l &= ~(OMAP2_MCSPI_CHCONF_IS|OMAP2_MCSPI_CHCONF_DPE1);
- l |= OMAP2_MCSPI_CHCONF_DPE0;
+ if (mcspi->pin_dir == MCSPI_PINDIR_D0_IN_D1_OUT) {
+ l &= ~OMAP2_MCSPI_CHCONF_IS;
+ l &= ~OMAP2_MCSPI_CHCONF_DPE1;
+ l |= OMAP2_MCSPI_CHCONF_DPE0;
+ } else {
+ l |= OMAP2_MCSPI_CHCONF_IS;
+ l |= OMAP2_MCSPI_CHCONF_DPE1;
+ l &= ~OMAP2_MCSPI_CHCONF_DPE0;
+ }
/* wordlength */
l &= ~OMAP2_MCSPI_CHCONF_WL_MASK;
@@ -1085,7 +1088,7 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master,
return 0;
}
-static int __devinit omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
+static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
{
struct spi_master *master = mcspi->master;
struct omap2_mcspi_regs *ctx = &mcspi->ctx;
@@ -1138,7 +1141,7 @@ static const struct of_device_id omap_mcspi_of_match[] = {
};
MODULE_DEVICE_TABLE(of, omap_mcspi_of_match);
-static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
+static int omap2_mcspi_probe(struct platform_device *pdev)
{
struct spi_master *master;
const struct omap2_mcspi_platform_config *pdata;
@@ -1167,6 +1170,11 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
master->cleanup = omap2_mcspi_cleanup;
master->dev.of_node = node;
+ dev_set_drvdata(&pdev->dev, master);
+
+ mcspi = spi_master_get_devdata(master);
+ mcspi->master = master;
+
match = of_match_device(omap_mcspi_of_match, &pdev->dev);
if (match) {
u32 num_cs = 1; /* default number of chipselect */
@@ -1175,19 +1183,17 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
of_property_read_u32(node, "ti,spi-num-cs", &num_cs);
master->num_chipselect = num_cs;
master->bus_num = bus_num++;
+ if (of_get_property(node, "ti,pindir-d0-out-d1-in", NULL))
+ mcspi->pin_dir = MCSPI_PINDIR_D0_OUT_D1_IN;
} else {
pdata = pdev->dev.platform_data;
master->num_chipselect = pdata->num_cs;
if (pdev->id != -1)
master->bus_num = pdev->id;
+ mcspi->pin_dir = pdata->pin_dir;
}
regs_offset = pdata->regs_offset;
- dev_set_drvdata(&pdev->dev, master);
-
- mcspi = spi_master_get_devdata(master);
- mcspi->master = master;
-
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (r == NULL) {
status = -ENODEV;
@@ -1272,7 +1278,7 @@ free_master:
return status;
}
-static int __devexit omap2_mcspi_remove(struct platform_device *pdev)
+static int omap2_mcspi_remove(struct platform_device *pdev)
{
struct spi_master *master;
struct omap2_mcspi *mcspi;
@@ -1341,7 +1347,7 @@ static struct platform_driver omap2_mcspi_driver = {
.of_match_table = omap_mcspi_of_match,
},
.probe = omap2_mcspi_probe,
- .remove = __devexit_p(omap2_mcspi_remove),
+ .remove = omap2_mcspi_remove,
};
module_platform_driver(omap2_mcspi_driver);
diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c
index b17c09cf0a0..b7e718254b1 100644
--- a/drivers/spi/spi-orion.c
+++ b/drivers/spi/spi-orion.c
@@ -32,8 +32,12 @@
#define ORION_SPI_DATA_IN_REG 0x0c
#define ORION_SPI_INT_CAUSE_REG 0x10
+#define ORION_SPI_MODE_CPOL (1 << 11)
+#define ORION_SPI_MODE_CPHA (1 << 12)
#define ORION_SPI_IF_8_16_BIT_MODE (1 << 5)
#define ORION_SPI_CLK_PRESCALE_MASK 0x1F
+#define ORION_SPI_MODE_MASK (ORION_SPI_MODE_CPOL | \
+ ORION_SPI_MODE_CPHA)
struct orion_spi {
struct spi_master *master;
@@ -123,6 +127,23 @@ static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed)
return 0;
}
+static void
+orion_spi_mode_set(struct spi_device *spi)
+{
+ u32 reg;
+ struct orion_spi *orion_spi;
+
+ orion_spi = spi_master_get_devdata(spi->master);
+
+ reg = readl(spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
+ reg &= ~ORION_SPI_MODE_MASK;
+ if (spi->mode & SPI_CPOL)
+ reg |= ORION_SPI_MODE_CPOL;
+ if (spi->mode & SPI_CPHA)
+ reg |= ORION_SPI_MODE_CPHA;
+ writel(reg, spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
+}
+
/*
* called only when no transfer is active on the bus
*/
@@ -142,6 +163,8 @@ orion_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
if ((t != NULL) && t->bits_per_word)
bits_per_word = t->bits_per_word;
+ orion_spi_mode_set(spi);
+
rc = orion_spi_baudrate_set(spi, speed);
if (rc)
return rc;
@@ -399,7 +422,7 @@ static int __init orion_spi_probe(struct platform_device *pdev)
}
/* we support only mode 0, and no options */
- master->mode_bits = 0;
+ master->mode_bits = SPI_CPHA | SPI_CPOL;
master->setup = orion_spi_setup;
master->transfer_one_message = orion_spi_transfer_one_message;
@@ -478,7 +501,7 @@ static int __exit orion_spi_remove(struct platform_device *pdev)
MODULE_ALIAS("platform:" DRIVER_NAME);
-static const struct of_device_id orion_spi_of_match_table[] __devinitdata = {
+static const struct of_device_id orion_spi_of_match_table[] = {
{ .compatible = "marvell,orion-spi", },
{}
};
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index a1db91a99b8..b0fe393c882 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -371,6 +371,7 @@ struct pl022 {
/* Two optional pin states - default & sleep */
struct pinctrl *pinctrl;
struct pinctrl_state *pins_default;
+ struct pinctrl_state *pins_idle;
struct pinctrl_state *pins_sleep;
struct spi_master *master;
struct pl022_ssp_controller *master_info;
@@ -1088,7 +1089,7 @@ err_alloc_rx_sg:
return -ENOMEM;
}
-static int __devinit pl022_dma_probe(struct pl022 *pl022)
+static int pl022_dma_probe(struct pl022 *pl022)
{
dma_cap_mask_t mask;
@@ -2057,8 +2058,7 @@ pl022_platform_data_dt_get(struct device *dev)
return pd;
}
-static int __devinit
-pl022_probe(struct amba_device *adev, const struct amba_id *id)
+static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
{
struct device *dev = &adev->dev;
struct pl022_ssp_controller *platform_info = adev->dev.platform_data;
@@ -2116,6 +2116,11 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
} else
dev_err(dev, "could not get default pinstate\n");
+ pl022->pins_idle = pinctrl_lookup_state(pl022->pinctrl,
+ PINCTRL_STATE_IDLE);
+ if (IS_ERR(pl022->pins_idle))
+ dev_dbg(dev, "could not get idle pinstate\n");
+
pl022->pins_sleep = pinctrl_lookup_state(pl022->pinctrl,
PINCTRL_STATE_SLEEP);
if (IS_ERR(pl022->pins_sleep))
@@ -2246,10 +2251,9 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
pm_runtime_set_autosuspend_delay(dev,
platform_info->autosuspend_delay);
pm_runtime_use_autosuspend(dev);
- pm_runtime_put_autosuspend(dev);
- } else {
- pm_runtime_put(dev);
}
+ pm_runtime_put(dev);
+
return 0;
err_spi_register:
@@ -2270,7 +2274,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
return status;
}
-static int __devexit
+static int
pl022_remove(struct amba_device *adev)
{
struct pl022 *pl022 = amba_get_drvdata(adev);
@@ -2303,35 +2307,47 @@ pl022_remove(struct amba_device *adev)
* the runtime counterparts to handle external resources like
* clocks, pins and regulators when going to sleep.
*/
-static void pl022_suspend_resources(struct pl022 *pl022)
+static void pl022_suspend_resources(struct pl022 *pl022, bool runtime)
{
int ret;
+ struct pinctrl_state *pins_state;
clk_disable(pl022->clk);
+ pins_state = runtime ? pl022->pins_idle : pl022->pins_sleep;
/* Optionally let pins go into sleep states */
- if (!IS_ERR(pl022->pins_sleep)) {
- ret = pinctrl_select_state(pl022->pinctrl,
- pl022->pins_sleep);
+ if (!IS_ERR(pins_state)) {
+ ret = pinctrl_select_state(pl022->pinctrl, pins_state);
if (ret)
- dev_err(&pl022->adev->dev,
- "could not set pins to sleep state\n");
+ dev_err(&pl022->adev->dev, "could not set %s pins\n",
+ runtime ? "idle" : "sleep");
}
}
-static void pl022_resume_resources(struct pl022 *pl022)
+static void pl022_resume_resources(struct pl022 *pl022, bool runtime)
{
int ret;
/* Optionaly enable pins to be muxed in and configured */
+ /* First go to the default state */
if (!IS_ERR(pl022->pins_default)) {
- ret = pinctrl_select_state(pl022->pinctrl,
- pl022->pins_default);
+ ret = pinctrl_select_state(pl022->pinctrl, pl022->pins_default);
if (ret)
dev_err(&pl022->adev->dev,
"could not set default pins\n");
}
+ if (!runtime) {
+ /* Then let's idle the pins until the next transfer happens */
+ if (!IS_ERR(pl022->pins_idle)) {
+ ret = pinctrl_select_state(pl022->pinctrl,
+ pl022->pins_idle);
+ if (ret)
+ dev_err(&pl022->adev->dev,
+ "could not set idle pins\n");
+ }
+ }
+
clk_enable(pl022->clk);
}
#endif
@@ -2347,7 +2363,9 @@ static int pl022_suspend(struct device *dev)
dev_warn(dev, "cannot suspend master\n");
return ret;
}
- pl022_suspend_resources(pl022);
+
+ pm_runtime_get_sync(dev);
+ pl022_suspend_resources(pl022, false);
dev_dbg(dev, "suspended\n");
return 0;
@@ -2358,7 +2376,8 @@ static int pl022_resume(struct device *dev)
struct pl022 *pl022 = dev_get_drvdata(dev);
int ret;
- pl022_resume_resources(pl022);
+ pl022_resume_resources(pl022, false);
+ pm_runtime_put(dev);
/* Start the queue running */
ret = spi_master_resume(pl022->master);
@@ -2376,7 +2395,7 @@ static int pl022_runtime_suspend(struct device *dev)
{
struct pl022 *pl022 = dev_get_drvdata(dev);
- pl022_suspend_resources(pl022);
+ pl022_suspend_resources(pl022, true);
return 0;
}
@@ -2384,7 +2403,7 @@ static int pl022_runtime_resume(struct device *dev)
{
struct pl022 *pl022 = dev_get_drvdata(dev);
- pl022_resume_resources(pl022);
+ pl022_resume_resources(pl022, true);
return 0;
}
#endif
@@ -2464,7 +2483,7 @@ static struct amba_driver pl022_driver = {
},
.id_table = pl022_ids,
.probe = pl022_probe,
- .remove = __devexit_p(pl022_remove),
+ .remove = pl022_remove,
};
static int __init pl022_init(void)
diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c
index 9f6ba34b172..cf95587eefd 100644
--- a/drivers/spi/spi-pxa2xx-pci.c
+++ b/drivers/spi/spi-pxa2xx-pci.c
@@ -51,7 +51,7 @@ void pxa_ssp_free(struct ssp_device *ssp)
}
EXPORT_SYMBOL_GPL(pxa_ssp_free);
-static int __devinit ce4100_spi_probe(struct pci_dev *dev,
+static int ce4100_spi_probe(struct pci_dev *dev,
const struct pci_device_id *ent)
{
int ret;
@@ -129,7 +129,7 @@ err_nomem:
return ret;
}
-static void __devexit ce4100_spi_remove(struct pci_dev *dev)
+static void ce4100_spi_remove(struct pci_dev *dev)
{
struct ce4100_info *spi_info;
struct ssp_device *ssp;
@@ -161,7 +161,7 @@ static struct pci_driver ce4100_spi_driver = {
.name = "ce4100_spi",
.id_table = ce4100_spi_devices,
.probe = ce4100_spi_probe,
- .remove = __devexit_p(ce4100_spi_remove),
+ .remove = ce4100_spi_remove,
};
module_pci_driver(ce4100_spi_driver);
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
index dc25bee8d33..5c8c4f5883c 100644
--- a/drivers/spi/spi-pxa2xx.c
+++ b/drivers/spi/spi-pxa2xx.c
@@ -1438,7 +1438,7 @@ static void cleanup(struct spi_device *spi)
kfree(chip);
}
-static int __devinit init_queue(struct driver_data *drv_data)
+static int init_queue(struct driver_data *drv_data)
{
INIT_LIST_HEAD(&drv_data->queue);
spin_lock_init(&drv_data->lock);
@@ -1526,7 +1526,7 @@ static int destroy_queue(struct driver_data *drv_data)
return 0;
}
-static int __devinit pxa2xx_spi_probe(struct platform_device *pdev)
+static int pxa2xx_spi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct pxa2xx_spi_master *platform_info;
diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c
index 30faf6d4ab9..902f2fb902d 100644
--- a/drivers/spi/spi-rspi.c
+++ b/drivers/spi/spi-rspi.c
@@ -661,7 +661,7 @@ static irqreturn_t rspi_irq(int irq, void *_sr)
return ret;
}
-static int __devinit rspi_request_dma(struct rspi_data *rspi,
+static int rspi_request_dma(struct rspi_data *rspi,
struct platform_device *pdev)
{
struct rspi_plat_data *rspi_pd = pdev->dev.platform_data;
@@ -709,7 +709,7 @@ static int __devinit rspi_request_dma(struct rspi_data *rspi,
return 0;
}
-static void __devexit rspi_release_dma(struct rspi_data *rspi)
+static void rspi_release_dma(struct rspi_data *rspi)
{
if (rspi->chan_tx)
dma_release_channel(rspi->chan_tx);
@@ -717,7 +717,7 @@ static void __devexit rspi_release_dma(struct rspi_data *rspi)
dma_release_channel(rspi->chan_rx);
}
-static int __devexit rspi_remove(struct platform_device *pdev)
+static int rspi_remove(struct platform_device *pdev)
{
struct rspi_data *rspi = dev_get_drvdata(&pdev->dev);
@@ -731,7 +731,7 @@ static int __devexit rspi_remove(struct platform_device *pdev)
return 0;
}
-static int __devinit rspi_probe(struct platform_device *pdev)
+static int rspi_probe(struct platform_device *pdev)
{
struct resource *res;
struct spi_master *master;
@@ -827,7 +827,7 @@ error1:
static struct platform_driver rspi_driver = {
.probe = rspi_probe,
- .remove = __devexit_p(rspi_remove),
+ .remove = rspi_remove,
.driver = {
.name = "rspi",
.owner = THIS_MODULE,
diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c
index a2a080b7f42..02d64603fcc 100644
--- a/drivers/spi/spi-s3c24xx.c
+++ b/drivers/spi/spi-s3c24xx.c
@@ -506,7 +506,7 @@ static void s3c24xx_spi_initialsetup(struct s3c24xx_spi *hw)
}
}
-static int __devinit s3c24xx_spi_probe(struct platform_device *pdev)
+static int s3c24xx_spi_probe(struct platform_device *pdev)
{
struct s3c2410_spi_info *pdata;
struct s3c24xx_spi *hw;
@@ -663,7 +663,7 @@ static int __devinit s3c24xx_spi_probe(struct platform_device *pdev)
return err;
}
-static int __devexit s3c24xx_spi_remove(struct platform_device *dev)
+static int s3c24xx_spi_remove(struct platform_device *dev)
{
struct s3c24xx_spi *hw = platform_get_drvdata(dev);
@@ -722,7 +722,7 @@ static const struct dev_pm_ops s3c24xx_spi_pmops = {
MODULE_ALIAS("platform:s3c2410-spi");
static struct platform_driver s3c24xx_spi_driver = {
.probe = s3c24xx_spi_probe,
- .remove = __devexit_p(s3c24xx_spi_remove),
+ .remove = s3c24xx_spi_remove,
.driver = {
.name = "s3c2410-spi",
.owner = THIS_MODULE,
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index 6e7a805d324..4dd7b7ce5c5 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -516,7 +516,7 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
/* Disable Clock */
if (sdd->port_conf->clk_from_cmu) {
- clk_disable(sdd->src_clk);
+ clk_disable_unprepare(sdd->src_clk);
} else {
val = readl(regs + S3C64XX_SPI_CLK_CFG);
val &= ~S3C64XX_SPI_ENCLK_ENABLE;
@@ -564,7 +564,7 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
/* There is half-multiplier before the SPI */
clk_set_rate(sdd->src_clk, sdd->cur_speed * 2);
/* Enable Clock */
- clk_enable(sdd->src_clk);
+ clk_prepare_enable(sdd->src_clk);
} else {
/* Configure Clock */
val = readl(regs + S3C64XX_SPI_CLK_CFG);
@@ -1056,7 +1056,7 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel)
flush_fifo(sdd);
}
-static int __devinit s3c64xx_spi_get_dmares(
+static int s3c64xx_spi_get_dmares(
struct s3c64xx_spi_driver_data *sdd, bool tx)
{
struct platform_device *pdev = sdd->pdev;
@@ -1112,7 +1112,7 @@ static int s3c64xx_spi_parse_dt_gpio(struct s3c64xx_spi_driver_data *sdd)
dev_err(dev, "invalid gpio[%d]: %d\n", idx, gpio);
goto free_gpio;
}
-
+ sdd->gpios[idx] = gpio;
ret = gpio_request(gpio, "spi-bus");
if (ret) {
dev_err(dev, "gpio [%d] request failed: %d\n",
@@ -1135,7 +1135,7 @@ static void s3c64xx_spi_dt_gpio_free(struct s3c64xx_spi_driver_data *sdd)
gpio_free(sdd->gpios[idx]);
}
-static struct __devinit s3c64xx_spi_info * s3c64xx_spi_parse_dt(
+static struct s3c64xx_spi_info * s3c64xx_spi_parse_dt(
struct device *dev)
{
struct s3c64xx_spi_info *sci;
@@ -1302,7 +1302,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
goto err3;
}
- if (clk_enable(sdd->clk)) {
+ if (clk_prepare_enable(sdd->clk)) {
dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n");
ret = -EBUSY;
goto err4;
@@ -1317,7 +1317,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
goto err5;
}
- if (clk_enable(sdd->src_clk)) {
+ if (clk_prepare_enable(sdd->src_clk)) {
dev_err(&pdev->dev, "Couldn't enable clock '%s'\n", clk_name);
ret = -EBUSY;
goto err6;
@@ -1361,11 +1361,11 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
err8:
free_irq(irq, sdd);
err7:
- clk_disable(sdd->src_clk);
+ clk_disable_unprepare(sdd->src_clk);
err6:
clk_put(sdd->src_clk);
err5:
- clk_disable(sdd->clk);
+ clk_disable_unprepare(sdd->clk);
err4:
clk_put(sdd->clk);
err3:
@@ -1393,10 +1393,10 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
free_irq(platform_get_irq(pdev, 0), sdd);
- clk_disable(sdd->src_clk);
+ clk_disable_unprepare(sdd->src_clk);
clk_put(sdd->src_clk);
- clk_disable(sdd->clk);
+ clk_disable_unprepare(sdd->clk);
clk_put(sdd->clk);
if (!sdd->cntrlr_info->cfg_gpio && pdev->dev.of_node)
@@ -1417,8 +1417,8 @@ static int s3c64xx_spi_suspend(struct device *dev)
spi_master_suspend(master);
/* Disable the clock */
- clk_disable(sdd->src_clk);
- clk_disable(sdd->clk);
+ clk_disable_unprepare(sdd->src_clk);
+ clk_disable_unprepare(sdd->clk);
if (!sdd->cntrlr_info->cfg_gpio && dev->of_node)
s3c64xx_spi_dt_gpio_free(sdd);
@@ -1440,8 +1440,8 @@ static int s3c64xx_spi_resume(struct device *dev)
sci->cfg_gpio();
/* Enable the clock */
- clk_enable(sdd->src_clk);
- clk_enable(sdd->clk);
+ clk_prepare_enable(sdd->src_clk);
+ clk_prepare_enable(sdd->clk);
s3c64xx_spi_hwinit(sdd, sdd->port_id);
@@ -1457,8 +1457,8 @@ static int s3c64xx_spi_runtime_suspend(struct device *dev)
struct spi_master *master = dev_get_drvdata(dev);
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
- clk_disable(sdd->clk);
- clk_disable(sdd->src_clk);
+ clk_disable_unprepare(sdd->clk);
+ clk_disable_unprepare(sdd->src_clk);
return 0;
}
@@ -1468,8 +1468,8 @@ static int s3c64xx_spi_runtime_resume(struct device *dev)
struct spi_master *master = dev_get_drvdata(dev);
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
- clk_enable(sdd->src_clk);
- clk_enable(sdd->clk);
+ clk_prepare_enable(sdd->src_clk);
+ clk_prepare_enable(sdd->clk);
return 0;
}
diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c
index 796c077ef43..32f7b55fce0 100644
--- a/drivers/spi/spi-sh-hspi.c
+++ b/drivers/spi/spi-sh-hspi.c
@@ -68,6 +68,16 @@ static u32 hspi_read(struct hspi_priv *hspi, int reg)
return ioread32(hspi->addr + reg);
}
+static void hspi_bit_set(struct hspi_priv *hspi, int reg, u32 mask, u32 set)
+{
+ u32 val = hspi_read(hspi, reg);
+
+ val &= ~mask;
+ val |= set & mask;
+
+ hspi_write(hspi, reg, val);
+}
+
/*
* transfer function
*/
@@ -105,6 +115,13 @@ static int hspi_unprepare_transfer(struct spi_master *master)
return 0;
}
+#define hspi_hw_cs_enable(hspi) hspi_hw_cs_ctrl(hspi, 0)
+#define hspi_hw_cs_disable(hspi) hspi_hw_cs_ctrl(hspi, 1)
+static void hspi_hw_cs_ctrl(struct hspi_priv *hspi, int hi)
+{
+ hspi_bit_set(hspi, SPSCR, (1 << 6), (hi) << 6);
+}
+
static void hspi_hw_setup(struct hspi_priv *hspi,
struct spi_message *msg,
struct spi_transfer *t)
@@ -155,7 +172,7 @@ static void hspi_hw_setup(struct hspi_priv *hspi,
hspi_write(hspi, SPCR, spcr);
hspi_write(hspi, SPSR, 0x0);
- hspi_write(hspi, SPSCR, 0x1); /* master mode */
+ hspi_write(hspi, SPSCR, 0x21); /* master mode / CS control */
}
static int hspi_transfer_one_message(struct spi_master *master,
@@ -166,12 +183,21 @@ static int hspi_transfer_one_message(struct spi_master *master,
u32 tx;
u32 rx;
int ret, i;
+ unsigned int cs_change;
+ const int nsecs = 50;
dev_dbg(hspi->dev, "%s\n", __func__);
+ cs_change = 1;
ret = 0;
list_for_each_entry(t, &msg->transfers, transfer_list) {
- hspi_hw_setup(hspi, msg, t);
+
+ if (cs_change) {
+ hspi_hw_setup(hspi, msg, t);
+ hspi_hw_cs_enable(hspi);
+ ndelay(nsecs);
+ }
+ cs_change = t->cs_change;
for (i = 0; i < t->len; i++) {
@@ -198,9 +224,22 @@ static int hspi_transfer_one_message(struct spi_master *master,
}
msg->actual_length += t->len;
+
+ if (t->delay_usecs)
+ udelay(t->delay_usecs);
+
+ if (cs_change) {
+ ndelay(nsecs);
+ hspi_hw_cs_disable(hspi);
+ ndelay(nsecs);
+ }
}
msg->status = ret;
+ if (!cs_change) {
+ ndelay(nsecs);
+ hspi_hw_cs_disable(hspi);
+ }
spi_finalize_current_message(master);
return ret;
@@ -229,7 +268,7 @@ static void hspi_cleanup(struct spi_device *spi)
dev_dbg(dev, "%s cleanup\n", spi->modalias);
}
-static int __devinit hspi_probe(struct platform_device *pdev)
+static int hspi_probe(struct platform_device *pdev)
{
struct resource *res;
struct spi_master *master;
@@ -300,7 +339,7 @@ static int __devinit hspi_probe(struct platform_device *pdev)
return ret;
}
-static int __devexit hspi_remove(struct platform_device *pdev)
+static int hspi_remove(struct platform_device *pdev)
{
struct hspi_priv *hspi = dev_get_drvdata(&pdev->dev);
@@ -314,7 +353,7 @@ static int __devexit hspi_remove(struct platform_device *pdev)
static struct platform_driver hspi_driver = {
.probe = hspi_probe,
- .remove = __devexit_p(hspi_remove),
+ .remove = hspi_remove,
.driver = {
.name = "sh-hspi",
.owner = THIS_MODULE,
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index 1f466bc66d9..96358d0eabb 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -597,7 +597,6 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
struct resource *r;
struct spi_master *master;
struct sh_msiof_spi_priv *p;
- char clk_name[16];
int i;
int ret;
@@ -614,10 +613,9 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
p->info = pdev->dev.platform_data;
init_completion(&p->done);
- snprintf(clk_name, sizeof(clk_name), "msiof%d", pdev->id);
- p->clk = clk_get(&pdev->dev, clk_name);
+ p->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(p->clk)) {
- dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+ dev_err(&pdev->dev, "cannot get clock\n");
ret = PTR_ERR(p->clk);
goto err1;
}
diff --git a/drivers/spi/spi-sh.c b/drivers/spi/spi-sh.c
index 79442c31bcd..3c3600a994b 100644
--- a/drivers/spi/spi-sh.c
+++ b/drivers/spi/spi-sh.c
@@ -432,7 +432,7 @@ static irqreturn_t spi_sh_irq(int irq, void *_ss)
return IRQ_HANDLED;
}
-static int __devexit spi_sh_remove(struct platform_device *pdev)
+static int spi_sh_remove(struct platform_device *pdev)
{
struct spi_sh_data *ss = dev_get_drvdata(&pdev->dev);
@@ -444,7 +444,7 @@ static int __devexit spi_sh_remove(struct platform_device *pdev)
return 0;
}
-static int __devinit spi_sh_probe(struct platform_device *pdev)
+static int spi_sh_probe(struct platform_device *pdev)
{
struct resource *res;
struct spi_master *master;
@@ -539,7 +539,7 @@ static int __devinit spi_sh_probe(struct platform_device *pdev)
static struct platform_driver spi_sh_driver = {
.probe = spi_sh_probe,
- .remove = __devexit_p(spi_sh_remove),
+ .remove = spi_sh_remove,
.driver = {
.name = "sh_spi",
.owner = THIS_MODULE,
diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c
index ecc3d9763d1..e0f43a512e8 100644
--- a/drivers/spi/spi-sirf.c
+++ b/drivers/spi/spi-sirf.c
@@ -479,7 +479,7 @@ static int spi_sirfsoc_setup(struct spi_device *spi)
return spi_sirfsoc_setup_transfer(spi, NULL);
}
-static int __devinit spi_sirfsoc_probe(struct platform_device *pdev)
+static int spi_sirfsoc_probe(struct platform_device *pdev)
{
struct sirfsoc_spi *sspi;
struct spi_master *master;
@@ -604,7 +604,7 @@ err_cs:
return ret;
}
-static int __devexit spi_sirfsoc_remove(struct platform_device *pdev)
+static int spi_sirfsoc_remove(struct platform_device *pdev)
{
struct spi_master *master;
struct sirfsoc_spi *sspi;
@@ -673,7 +673,7 @@ static struct platform_driver spi_sirfsoc_driver = {
.of_match_table = spi_sirfsoc_of_match,
},
.probe = spi_sirfsoc_probe,
- .remove = __devexit_p(spi_sirfsoc_remove),
+ .remove = spi_sirfsoc_remove,
};
module_platform_driver(spi_sirfsoc_driver);
diff --git a/drivers/spi/spi-stmp.c b/drivers/spi/spi-stmp.c
deleted file mode 100644
index 911e904b3c8..00000000000
--- a/drivers/spi/spi-stmp.c
+++ /dev/null
@@ -1,664 +0,0 @@
-/*
- * Freescale STMP378X SPI master driver
- *
- * Author: dmitry pervushin <dimka@embeddedalley.com>
- *
- * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
- * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
- */
-
-/*
- * The code contained herein is licensed under the GNU General Public
- * License. You may obtain a copy of the GNU General Public License
- * Version 2 or later at the following locations:
- *
- * http://www.opensource.org/licenses/gpl-license.html
- * http://www.gnu.org/copyleft/gpl.html
- */
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/spi/spi.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/dma-mapping.h>
-#include <linux/delay.h>
-
-#include <mach/platform.h>
-#include <mach/stmp3xxx.h>
-#include <mach/dma.h>
-#include <mach/regs-ssp.h>
-#include <mach/regs-apbh.h>
-
-
-/* 0 means DMA mode(recommended, default), !0 - PIO mode */
-static int pio;
-static int clock;
-
-/* default timeout for busy waits is 2 seconds */
-#define STMP_SPI_TIMEOUT (2 * HZ)
-
-struct stmp_spi {
- int id;
-
- void * __iomem regs; /* vaddr of the control registers */
-
- int irq, err_irq;
- u32 dma;
- struct stmp3xxx_dma_descriptor d;
-
- u32 speed_khz;
- u32 saved_timings;
- u32 divider;
-
- struct clk *clk;
- struct device *master_dev;
-
- struct work_struct work;
- struct workqueue_struct *workqueue;
-
- /* lock protects queue access */
- spinlock_t lock;
- struct list_head queue;
-
- struct completion done;
-};
-
-#define busy_wait(cond) \
- ({ \
- unsigned long end_jiffies = jiffies + STMP_SPI_TIMEOUT; \
- bool succeeded = false; \
- do { \
- if (cond) { \
- succeeded = true; \
- break; \
- } \
- cpu_relax(); \
- } while (time_before(jiffies, end_jiffies)); \
- succeeded; \
- })
-
-/**
- * stmp_spi_init_hw
- * Initialize the SSP port
- */
-static int stmp_spi_init_hw(struct stmp_spi *ss)
-{
- int err = 0;
- void *pins = ss->master_dev->platform_data;
-
- err = stmp3xxx_request_pin_group(pins, dev_name(ss->master_dev));
- if (err)
- goto out;
-
- ss->clk = clk_get(NULL, "ssp");
- if (IS_ERR(ss->clk)) {
- err = PTR_ERR(ss->clk);
- goto out_free_pins;
- }
- clk_enable(ss->clk);
-
- stmp3xxx_reset_block(ss->regs, false);
- stmp3xxx_dma_reset_channel(ss->dma);
-
- return 0;
-
-out_free_pins:
- stmp3xxx_release_pin_group(pins, dev_name(ss->master_dev));
-out:
- return err;
-}
-
-static void stmp_spi_release_hw(struct stmp_spi *ss)
-{
- void *pins = ss->master_dev->platform_data;
-
- if (ss->clk && !IS_ERR(ss->clk)) {
- clk_disable(ss->clk);
- clk_put(ss->clk);
- }
- stmp3xxx_release_pin_group(pins, dev_name(ss->master_dev));
-}
-
-static int stmp_spi_setup_transfer(struct spi_device *spi,
- struct spi_transfer *t)
-{
- u8 bits_per_word;
- u32 hz;
- struct stmp_spi *ss = spi_master_get_devdata(spi->master);
- u16 rate;
-
- bits_per_word = spi->bits_per_word;
- if (t && t->bits_per_word)
- bits_per_word = t->bits_per_word;
-
- /*
- * Calculate speed:
- * - by default, use maximum speed from ssp clk
- * - if device overrides it, use it
- * - if transfer specifies other speed, use transfer's one
- */
- hz = 1000 * ss->speed_khz / ss->divider;
- if (spi->max_speed_hz)
- hz = min(hz, spi->max_speed_hz);
- if (t && t->speed_hz)
- hz = min(hz, t->speed_hz);
-
- if (hz == 0) {
- dev_err(&spi->dev, "Cannot continue with zero clock\n");
- return -EINVAL;
- }
-
- if (bits_per_word != 8) {
- dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
- __func__, bits_per_word);
- return -EINVAL;
- }
-
- dev_dbg(&spi->dev, "Requested clk rate = %uHz, max = %uHz/%d = %uHz\n",
- hz, ss->speed_khz, ss->divider,
- ss->speed_khz * 1000 / ss->divider);
-
- if (ss->speed_khz * 1000 / ss->divider < hz) {
- dev_err(&spi->dev, "%s, unsupported clock rate %uHz\n",
- __func__, hz);
- return -EINVAL;
- }
-
- rate = 1000 * ss->speed_khz/ss->divider/hz;
-
- writel(BF(ss->divider, SSP_TIMING_CLOCK_DIVIDE) |
- BF(rate - 1, SSP_TIMING_CLOCK_RATE),
- HW_SSP_TIMING + ss->regs);
-
- writel(BF(1 /* mode SPI */, SSP_CTRL1_SSP_MODE) |
- BF(4 /* 8 bits */, SSP_CTRL1_WORD_LENGTH) |
- ((spi->mode & SPI_CPOL) ? BM_SSP_CTRL1_POLARITY : 0) |
- ((spi->mode & SPI_CPHA) ? BM_SSP_CTRL1_PHASE : 0) |
- (pio ? 0 : BM_SSP_CTRL1_DMA_ENABLE),
- ss->regs + HW_SSP_CTRL1);
-
- return 0;
-}
-
-static int stmp_spi_setup(struct spi_device *spi)
-{
- /* spi_setup() does basic checks,
- * stmp_spi_setup_transfer() does more later
- */
- if (spi->bits_per_word != 8) {
- dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
- __func__, spi->bits_per_word);
- return -EINVAL;
- }
- return 0;
-}
-
-static inline u32 stmp_spi_cs(unsigned cs)
-{
- return ((cs & 1) ? BM_SSP_CTRL0_WAIT_FOR_CMD : 0) |
- ((cs & 2) ? BM_SSP_CTRL0_WAIT_FOR_IRQ : 0);
-}
-
-static int stmp_spi_txrx_dma(struct stmp_spi *ss, int cs,
- unsigned char *buf, dma_addr_t dma_buf, int len,
- int first, int last, bool write)
-{
- u32 c0 = 0;
- dma_addr_t spi_buf_dma = dma_buf;
- int status = 0;
- enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
-
- c0 |= (first ? BM_SSP_CTRL0_LOCK_CS : 0);
- c0 |= (last ? BM_SSP_CTRL0_IGNORE_CRC : 0);
- c0 |= (write ? 0 : BM_SSP_CTRL0_READ);
- c0 |= BM_SSP_CTRL0_DATA_XFER;
-
- c0 |= stmp_spi_cs(cs);
-
- c0 |= BF(len, SSP_CTRL0_XFER_COUNT);
-
- if (!dma_buf)
- spi_buf_dma = dma_map_single(ss->master_dev, buf, len, dir);
-
- ss->d.command->cmd =
- BF(len, APBH_CHn_CMD_XFER_COUNT) |
- BF(1, APBH_CHn_CMD_CMDWORDS) |
- BM_APBH_CHn_CMD_WAIT4ENDCMD |
- BM_APBH_CHn_CMD_IRQONCMPLT |
- BF(write ? BV_APBH_CHn_CMD_COMMAND__DMA_READ :
- BV_APBH_CHn_CMD_COMMAND__DMA_WRITE,
- APBH_CHn_CMD_COMMAND);
- ss->d.command->pio_words[0] = c0;
- ss->d.command->buf_ptr = spi_buf_dma;
-
- stmp3xxx_dma_reset_channel(ss->dma);
- stmp3xxx_dma_clear_interrupt(ss->dma);
- stmp3xxx_dma_enable_interrupt(ss->dma);
- init_completion(&ss->done);
- stmp3xxx_dma_go(ss->dma, &ss->d, 1);
- wait_for_completion(&ss->done);
-
- if (!busy_wait(readl(ss->regs + HW_SSP_CTRL0) & BM_SSP_CTRL0_RUN))
- status = -ETIMEDOUT;
-
- if (!dma_buf)
- dma_unmap_single(ss->master_dev, spi_buf_dma, len, dir);
-
- return status;
-}
-
-static inline void stmp_spi_enable(struct stmp_spi *ss)
-{
- stmp3xxx_setl(BM_SSP_CTRL0_LOCK_CS, ss->regs + HW_SSP_CTRL0);
- stmp3xxx_clearl(BM_SSP_CTRL0_IGNORE_CRC, ss->regs + HW_SSP_CTRL0);
-}
-
-static inline void stmp_spi_disable(struct stmp_spi *ss)
-{
- stmp3xxx_clearl(BM_SSP_CTRL0_LOCK_CS, ss->regs + HW_SSP_CTRL0);
- stmp3xxx_setl(BM_SSP_CTRL0_IGNORE_CRC, ss->regs + HW_SSP_CTRL0);
-}
-
-static int stmp_spi_txrx_pio(struct stmp_spi *ss, int cs,
- unsigned char *buf, int len,
- bool first, bool last, bool write)
-{
- if (first)
- stmp_spi_enable(ss);
-
- stmp3xxx_setl(stmp_spi_cs(cs), ss->regs + HW_SSP_CTRL0);
-
- while (len--) {
- if (last && len <= 0)
- stmp_spi_disable(ss);
-
- stmp3xxx_clearl(BM_SSP_CTRL0_XFER_COUNT,
- ss->regs + HW_SSP_CTRL0);
- stmp3xxx_setl(1, ss->regs + HW_SSP_CTRL0);
-
- if (write)
- stmp3xxx_clearl(BM_SSP_CTRL0_READ,
- ss->regs + HW_SSP_CTRL0);
- else
- stmp3xxx_setl(BM_SSP_CTRL0_READ,
- ss->regs + HW_SSP_CTRL0);
-
- /* Run! */
- stmp3xxx_setl(BM_SSP_CTRL0_RUN, ss->regs + HW_SSP_CTRL0);
-
- if (!busy_wait(readl(ss->regs + HW_SSP_CTRL0) &
- BM_SSP_CTRL0_RUN))
- break;
-
- if (write)
- writel(*buf, ss->regs + HW_SSP_DATA);
-
- /* Set TRANSFER */
- stmp3xxx_setl(BM_SSP_CTRL0_DATA_XFER, ss->regs + HW_SSP_CTRL0);
-
- if (!write) {
- if (busy_wait((readl(ss->regs + HW_SSP_STATUS) &
- BM_SSP_STATUS_FIFO_EMPTY)))
- break;
- *buf = readl(ss->regs + HW_SSP_DATA) & 0xFF;
- }
-
- if (!busy_wait(readl(ss->regs + HW_SSP_CTRL0) &
- BM_SSP_CTRL0_RUN))
- break;
-
- /* advance to the next byte */
- buf++;
- }
-
- return len < 0 ? 0 : -ETIMEDOUT;
-}
-
-static int stmp_spi_handle_message(struct stmp_spi *ss, struct spi_message *m)
-{
- bool first, last;
- struct spi_transfer *t, *tmp_t;
- int status = 0;
- int cs;
-
- cs = m->spi->chip_select;
-
- list_for_each_entry_safe(t, tmp_t, &m->transfers, transfer_list) {
-
- first = (&t->transfer_list == m->transfers.next);
- last = (&t->transfer_list == m->transfers.prev);
-
- if (first || t->speed_hz || t->bits_per_word)
- stmp_spi_setup_transfer(m->spi, t);
-
- /* reject "not last" transfers which request to change cs */
- if (t->cs_change && !last) {
- dev_err(&m->spi->dev,
- "Message with t->cs_change has been skipped\n");
- continue;
- }
-
- if (t->tx_buf) {
- status = pio ?
- stmp_spi_txrx_pio(ss, cs, (void *)t->tx_buf,
- t->len, first, last, true) :
- stmp_spi_txrx_dma(ss, cs, (void *)t->tx_buf,
- t->tx_dma, t->len, first, last, true);
-#ifdef DEBUG
- if (t->len < 0x10)
- print_hex_dump_bytes("Tx ",
- DUMP_PREFIX_OFFSET,
- t->tx_buf, t->len);
- else
- pr_debug("Tx: %d bytes\n", t->len);
-#endif
- }
- if (t->rx_buf) {
- status = pio ?
- stmp_spi_txrx_pio(ss, cs, t->rx_buf,
- t->len, first, last, false) :
- stmp_spi_txrx_dma(ss, cs, t->rx_buf,
- t->rx_dma, t->len, first, last, false);
-#ifdef DEBUG
- if (t->len < 0x10)
- print_hex_dump_bytes("Rx ",
- DUMP_PREFIX_OFFSET,
- t->rx_buf, t->len);
- else
- pr_debug("Rx: %d bytes\n", t->len);
-#endif
- }
-
- if (t->delay_usecs)
- udelay(t->delay_usecs);
-
- if (status)
- break;
-
- }
- return status;
-}
-
-/**
- * stmp_spi_handle - handle messages from the queue
- */
-static void stmp_spi_handle(struct work_struct *w)
-{
- struct stmp_spi *ss = container_of(w, struct stmp_spi, work);
- unsigned long flags;
- struct spi_message *m;
-
- spin_lock_irqsave(&ss->lock, flags);
- while (!list_empty(&ss->queue)) {
- m = list_entry(ss->queue.next, struct spi_message, queue);
- list_del_init(&m->queue);
- spin_unlock_irqrestore(&ss->lock, flags);
-
- m->status = stmp_spi_handle_message(ss, m);
- m->complete(m->context);
-
- spin_lock_irqsave(&ss->lock, flags);
- }
- spin_unlock_irqrestore(&ss->lock, flags);
-
- return;
-}
-
-/**
- * stmp_spi_transfer - perform message transfer.
- * Called indirectly from spi_async, queues all the messages to
- * spi_handle_message.
- * @spi: spi device
- * @m: message to be queued
- */
-static int stmp_spi_transfer(struct spi_device *spi, struct spi_message *m)
-{
- struct stmp_spi *ss = spi_master_get_devdata(spi->master);
- unsigned long flags;
-
- m->status = -EINPROGRESS;
- spin_lock_irqsave(&ss->lock, flags);
- list_add_tail(&m->queue, &ss->queue);
- queue_work(ss->workqueue, &ss->work);
- spin_unlock_irqrestore(&ss->lock, flags);
- return 0;
-}
-
-static irqreturn_t stmp_spi_irq(int irq, void *dev_id)
-{
- struct stmp_spi *ss = dev_id;
-
- stmp3xxx_dma_clear_interrupt(ss->dma);
- complete(&ss->done);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t stmp_spi_irq_err(int irq, void *dev_id)
-{
- struct stmp_spi *ss = dev_id;
- u32 c1, st;
-
- c1 = readl(ss->regs + HW_SSP_CTRL1);
- st = readl(ss->regs + HW_SSP_STATUS);
- dev_err(ss->master_dev, "%s: status = 0x%08X, c1 = 0x%08X\n",
- __func__, st, c1);
- stmp3xxx_clearl(c1 & 0xCCCC0000, ss->regs + HW_SSP_CTRL1);
-
- return IRQ_HANDLED;
-}
-
-static int __devinit stmp_spi_probe(struct platform_device *dev)
-{
- int err = 0;
- struct spi_master *master;
- struct stmp_spi *ss;
- struct resource *r;
-
- master = spi_alloc_master(&dev->dev, sizeof(struct stmp_spi));
- if (master == NULL) {
- err = -ENOMEM;
- goto out0;
- }
- master->flags = SPI_MASTER_HALF_DUPLEX;
-
- ss = spi_master_get_devdata(master);
- platform_set_drvdata(dev, master);
-
- /* Get resources(memory, IRQ) associated with the device */
- r = platform_get_resource(dev, IORESOURCE_MEM, 0);
- if (r == NULL) {
- err = -ENODEV;
- goto out_put_master;
- }
- ss->regs = ioremap(r->start, resource_size(r));
- if (!ss->regs) {
- err = -EINVAL;
- goto out_put_master;
- }
-
- ss->master_dev = &dev->dev;
- ss->id = dev->id;
-
- INIT_WORK(&ss->work, stmp_spi_handle);
- INIT_LIST_HEAD(&ss->queue);
- spin_lock_init(&ss->lock);
-
- ss->workqueue = create_singlethread_workqueue(dev_name(&dev->dev));
- if (!ss->workqueue) {
- err = -ENXIO;
- goto out_put_master;
- }
- master->transfer = stmp_spi_transfer;
- master->setup = stmp_spi_setup;
-
- /* the spi->mode bits understood by this driver: */
- master->mode_bits = SPI_CPOL | SPI_CPHA;
-
- ss->irq = platform_get_irq(dev, 0);
- if (ss->irq < 0) {
- err = ss->irq;
- goto out_put_master;
- }
- ss->err_irq = platform_get_irq(dev, 1);
- if (ss->err_irq < 0) {
- err = ss->err_irq;
- goto out_put_master;
- }
-
- r = platform_get_resource(dev, IORESOURCE_DMA, 0);
- if (r == NULL) {
- err = -ENODEV;
- goto out_put_master;
- }
-
- ss->dma = r->start;
- err = stmp3xxx_dma_request(ss->dma, &dev->dev, dev_name(&dev->dev));
- if (err)
- goto out_put_master;
-
- err = stmp3xxx_dma_allocate_command(ss->dma, &ss->d);
- if (err)
- goto out_free_dma;
-
- master->bus_num = dev->id;
- master->num_chipselect = 1;
-
- /* SPI controller initializations */
- err = stmp_spi_init_hw(ss);
- if (err) {
- dev_dbg(&dev->dev, "cannot initialize hardware\n");
- goto out_free_dma_desc;
- }
-
- if (clock) {
- dev_info(&dev->dev, "clock rate forced to %d\n", clock);
- clk_set_rate(ss->clk, clock);
- }
- ss->speed_khz = clk_get_rate(ss->clk);
- ss->divider = 2;
- dev_info(&dev->dev, "max possible speed %d = %ld/%d kHz\n",
- ss->speed_khz, clk_get_rate(ss->clk), ss->divider);
-
- /* Register for SPI interrupt */
- err = request_irq(ss->irq, stmp_spi_irq, 0,
- dev_name(&dev->dev), ss);
- if (err) {
- dev_dbg(&dev->dev, "request_irq failed, %d\n", err);
- goto out_release_hw;
- }
-
- /* ..and shared interrupt for all SSP controllers */
- err = request_irq(ss->err_irq, stmp_spi_irq_err, IRQF_SHARED,
- dev_name(&dev->dev), ss);
- if (err) {
- dev_dbg(&dev->dev, "request_irq(error) failed, %d\n", err);
- goto out_free_irq;
- }
-
- err = spi_register_master(master);
- if (err) {
- dev_dbg(&dev->dev, "cannot register spi master, %d\n", err);
- goto out_free_irq_2;
- }
- dev_info(&dev->dev, "at (mapped) 0x%08X, irq=%d, bus %d, %s mode\n",
- (u32)ss->regs, ss->irq, master->bus_num,
- pio ? "PIO" : "DMA");
- return 0;
-
-out_free_irq_2:
- free_irq(ss->err_irq, ss);
-out_free_irq:
- free_irq(ss->irq, ss);
-out_free_dma_desc:
- stmp3xxx_dma_free_command(ss->dma, &ss->d);
-out_free_dma:
- stmp3xxx_dma_release(ss->dma);
-out_release_hw:
- stmp_spi_release_hw(ss);
-out_put_master:
- if (ss->workqueue)
- destroy_workqueue(ss->workqueue);
- if (ss->regs)
- iounmap(ss->regs);
- platform_set_drvdata(dev, NULL);
- spi_master_put(master);
-out0:
- return err;
-}
-
-static int __devexit stmp_spi_remove(struct platform_device *dev)
-{
- struct stmp_spi *ss;
- struct spi_master *master;
-
- master = spi_master_get(platform_get_drvdata(dev));
- ss = spi_master_get_devdata(master);
-
- spi_unregister_master(master);
-
- free_irq(ss->err_irq, ss);
- free_irq(ss->irq, ss);
- stmp3xxx_dma_free_command(ss->dma, &ss->d);
- stmp3xxx_dma_release(ss->dma);
- stmp_spi_release_hw(ss);
- destroy_workqueue(ss->workqueue);
- iounmap(ss->regs);
- spi_master_put(master);
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int stmp_spi_suspend(struct platform_device *pdev, pm_message_t pmsg)
-{
- struct stmp_spi *ss;
- struct spi_master *master;
-
- master = platform_get_drvdata(pdev);
- ss = spi_master_get_devdata(master);
-
- ss->saved_timings = readl(HW_SSP_TIMING + ss->regs);
- clk_disable(ss->clk);
-
- return 0;
-}
-
-static int stmp_spi_resume(struct platform_device *pdev)
-{
- struct stmp_spi *ss;
- struct spi_master *master;
-
- master = platform_get_drvdata(pdev);
- ss = spi_master_get_devdata(master);
-
- clk_enable(ss->clk);
- stmp3xxx_reset_block(ss->regs, false);
- writel(ss->saved_timings, ss->regs + HW_SSP_TIMING);
-
- return 0;
-}
-
-#else
-#define stmp_spi_suspend NULL
-#define stmp_spi_resume NULL
-#endif
-
-static struct platform_driver stmp_spi_driver = {
- .probe = stmp_spi_probe,
- .remove = __devexit_p(stmp_spi_remove),
- .driver = {
- .name = "stmp3xxx_ssp",
- .owner = THIS_MODULE,
- },
- .suspend = stmp_spi_suspend,
- .resume = stmp_spi_resume,
-};
-module_platform_driver(stmp_spi_driver);
-
-module_param(pio, int, S_IRUGO);
-module_param(clock, int, S_IRUGO);
-MODULE_AUTHOR("dmitry pervushin <dpervushin@embeddedalley.com>");
-MODULE_DESCRIPTION("STMP3xxx SPI/SSP driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c
new file mode 100644
index 00000000000..448a8cc71df
--- /dev/null
+++ b/drivers/spi/spi-tegra20-sflash.c
@@ -0,0 +1,665 @@
+/*
+ * SPI driver for Nvidia's Tegra20 Serial Flash Controller.
+ *
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi-tegra.h>
+#include <mach/clk.h>
+
+#define SPI_COMMAND 0x000
+#define SPI_GO BIT(30)
+#define SPI_M_S BIT(28)
+#define SPI_ACTIVE_SCLK_MASK (0x3 << 26)
+#define SPI_ACTIVE_SCLK_DRIVE_LOW (0 << 26)
+#define SPI_ACTIVE_SCLK_DRIVE_HIGH (1 << 26)
+#define SPI_ACTIVE_SCLK_PULL_LOW (2 << 26)
+#define SPI_ACTIVE_SCLK_PULL_HIGH (3 << 26)
+
+#define SPI_CK_SDA_FALLING (1 << 21)
+#define SPI_CK_SDA_RISING (0 << 21)
+#define SPI_CK_SDA_MASK (1 << 21)
+#define SPI_ACTIVE_SDA (0x3 << 18)
+#define SPI_ACTIVE_SDA_DRIVE_LOW (0 << 18)
+#define SPI_ACTIVE_SDA_DRIVE_HIGH (1 << 18)
+#define SPI_ACTIVE_SDA_PULL_LOW (2 << 18)
+#define SPI_ACTIVE_SDA_PULL_HIGH (3 << 18)
+
+#define SPI_CS_POL_INVERT BIT(16)
+#define SPI_TX_EN BIT(15)
+#define SPI_RX_EN BIT(14)
+#define SPI_CS_VAL_HIGH BIT(13)
+#define SPI_CS_VAL_LOW 0x0
+#define SPI_CS_SW BIT(12)
+#define SPI_CS_HW 0x0
+#define SPI_CS_DELAY_MASK (7 << 9)
+#define SPI_CS3_EN BIT(8)
+#define SPI_CS2_EN BIT(7)
+#define SPI_CS1_EN BIT(6)
+#define SPI_CS0_EN BIT(5)
+
+#define SPI_CS_MASK (SPI_CS3_EN | SPI_CS2_EN | \
+ SPI_CS1_EN | SPI_CS0_EN)
+#define SPI_BIT_LENGTH(x) (((x) & 0x1f) << 0)
+
+#define SPI_MODES (SPI_ACTIVE_SCLK_MASK | SPI_CK_SDA_MASK)
+
+#define SPI_STATUS 0x004
+#define SPI_BSY BIT(31)
+#define SPI_RDY BIT(30)
+#define SPI_TXF_FLUSH BIT(29)
+#define SPI_RXF_FLUSH BIT(28)
+#define SPI_RX_UNF BIT(27)
+#define SPI_TX_OVF BIT(26)
+#define SPI_RXF_EMPTY BIT(25)
+#define SPI_RXF_FULL BIT(24)
+#define SPI_TXF_EMPTY BIT(23)
+#define SPI_TXF_FULL BIT(22)
+#define SPI_BLK_CNT(count) (((count) & 0xffff) + 1)
+
+#define SPI_FIFO_ERROR (SPI_RX_UNF | SPI_TX_OVF)
+#define SPI_FIFO_EMPTY (SPI_TX_EMPTY | SPI_RX_EMPTY)
+
+#define SPI_RX_CMP 0x8
+#define SPI_DMA_CTL 0x0C
+#define SPI_DMA_EN BIT(31)
+#define SPI_IE_RXC BIT(27)
+#define SPI_IE_TXC BIT(26)
+#define SPI_PACKED BIT(20)
+#define SPI_RX_TRIG_MASK (0x3 << 18)
+#define SPI_RX_TRIG_1W (0x0 << 18)
+#define SPI_RX_TRIG_4W (0x1 << 18)
+#define SPI_TX_TRIG_MASK (0x3 << 16)
+#define SPI_TX_TRIG_1W (0x0 << 16)
+#define SPI_TX_TRIG_4W (0x1 << 16)
+#define SPI_DMA_BLK_COUNT(count) (((count) - 1) & 0xFFFF);
+
+#define SPI_TX_FIFO 0x10
+#define SPI_RX_FIFO 0x20
+
+#define DATA_DIR_TX (1 << 0)
+#define DATA_DIR_RX (1 << 1)
+
+#define MAX_CHIP_SELECT 4
+#define SPI_FIFO_DEPTH 4
+#define SPI_DMA_TIMEOUT (msecs_to_jiffies(1000))
+
+struct tegra_sflash_data {
+ struct device *dev;
+ struct spi_master *master;
+ spinlock_t lock;
+
+ struct clk *clk;
+ void __iomem *base;
+ unsigned irq;
+ u32 spi_max_frequency;
+ u32 cur_speed;
+
+ struct spi_device *cur_spi;
+ unsigned cur_pos;
+ unsigned cur_len;
+ unsigned bytes_per_word;
+ unsigned cur_direction;
+ unsigned curr_xfer_words;
+
+ unsigned cur_rx_pos;
+ unsigned cur_tx_pos;
+
+ u32 tx_status;
+ u32 rx_status;
+ u32 status_reg;
+
+ u32 def_command_reg;
+ u32 command_reg;
+ u32 dma_control_reg;
+
+ struct completion xfer_completion;
+ struct spi_transfer *curr_xfer;
+};
+
+static int tegra_sflash_runtime_suspend(struct device *dev);
+static int tegra_sflash_runtime_resume(struct device *dev);
+
+static inline unsigned long tegra_sflash_readl(struct tegra_sflash_data *tsd,
+ unsigned long reg)
+{
+ return readl(tsd->base + reg);
+}
+
+static inline void tegra_sflash_writel(struct tegra_sflash_data *tsd,
+ unsigned long val, unsigned long reg)
+{
+ writel(val, tsd->base + reg);
+}
+
+static void tegra_sflash_clear_status(struct tegra_sflash_data *tsd)
+{
+ /* Write 1 to clear status register */
+ tegra_sflash_writel(tsd, SPI_RDY | SPI_FIFO_ERROR, SPI_STATUS);
+}
+
+static unsigned tegra_sflash_calculate_curr_xfer_param(
+ struct spi_device *spi, struct tegra_sflash_data *tsd,
+ struct spi_transfer *t)
+{
+ unsigned remain_len = t->len - tsd->cur_pos;
+ unsigned max_word;
+
+ tsd->bytes_per_word = (t->bits_per_word - 1) / 8 + 1;
+ max_word = remain_len / tsd->bytes_per_word;
+ if (max_word > SPI_FIFO_DEPTH)
+ max_word = SPI_FIFO_DEPTH;
+ tsd->curr_xfer_words = max_word;
+ return max_word;
+}
+
+static unsigned tegra_sflash_fill_tx_fifo_from_client_txbuf(
+ struct tegra_sflash_data *tsd, struct spi_transfer *t)
+{
+ unsigned nbytes;
+ unsigned long status;
+ unsigned max_n_32bit = tsd->curr_xfer_words;
+ u8 *tx_buf = (u8 *)t->tx_buf + tsd->cur_tx_pos;
+
+ if (max_n_32bit > SPI_FIFO_DEPTH)
+ max_n_32bit = SPI_FIFO_DEPTH;
+ nbytes = max_n_32bit * tsd->bytes_per_word;
+
+ status = tegra_sflash_readl(tsd, SPI_STATUS);
+ while (!(status & SPI_TXF_FULL)) {
+ int i;
+ unsigned int x = 0;
+
+ for (i = 0; nbytes && (i < tsd->bytes_per_word);
+ i++, nbytes--)
+ x |= ((*tx_buf++) << i*8);
+ tegra_sflash_writel(tsd, x, SPI_TX_FIFO);
+ if (!nbytes)
+ break;
+
+ status = tegra_sflash_readl(tsd, SPI_STATUS);
+ }
+ tsd->cur_tx_pos += max_n_32bit * tsd->bytes_per_word;
+ return max_n_32bit;
+}
+
+static int tegra_sflash_read_rx_fifo_to_client_rxbuf(
+ struct tegra_sflash_data *tsd, struct spi_transfer *t)
+{
+ unsigned long status;
+ unsigned int read_words = 0;
+ u8 *rx_buf = (u8 *)t->rx_buf + tsd->cur_rx_pos;
+
+ status = tegra_sflash_readl(tsd, SPI_STATUS);
+ while (!(status & SPI_RXF_EMPTY)) {
+ int i;
+ unsigned long x;
+
+ x = tegra_sflash_readl(tsd, SPI_RX_FIFO);
+ for (i = 0; (i < tsd->bytes_per_word); i++)
+ *rx_buf++ = (x >> (i*8)) & 0xFF;
+ read_words++;
+ status = tegra_sflash_readl(tsd, SPI_STATUS);
+ }
+ tsd->cur_rx_pos += read_words * tsd->bytes_per_word;
+ return 0;
+}
+
+static int tegra_sflash_start_cpu_based_transfer(
+ struct tegra_sflash_data *tsd, struct spi_transfer *t)
+{
+ unsigned long val = 0;
+ unsigned cur_words;
+
+ if (tsd->cur_direction & DATA_DIR_TX)
+ val |= SPI_IE_TXC;
+
+ if (tsd->cur_direction & DATA_DIR_RX)
+ val |= SPI_IE_RXC;
+
+ tegra_sflash_writel(tsd, val, SPI_DMA_CTL);
+ tsd->dma_control_reg = val;
+
+ if (tsd->cur_direction & DATA_DIR_TX)
+ cur_words = tegra_sflash_fill_tx_fifo_from_client_txbuf(tsd, t);
+ else
+ cur_words = tsd->curr_xfer_words;
+ val |= SPI_DMA_BLK_COUNT(cur_words);
+ tegra_sflash_writel(tsd, val, SPI_DMA_CTL);
+ tsd->dma_control_reg = val;
+ val |= SPI_DMA_EN;
+ tegra_sflash_writel(tsd, val, SPI_DMA_CTL);
+ return 0;
+}
+
+static int tegra_sflash_start_transfer_one(struct spi_device *spi,
+ struct spi_transfer *t, bool is_first_of_msg,
+ bool is_single_xfer)
+{
+ struct tegra_sflash_data *tsd = spi_master_get_devdata(spi->master);
+ u32 speed;
+ unsigned long command;
+
+ speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz;
+ if (!speed)
+ speed = tsd->spi_max_frequency;
+ if (speed != tsd->cur_speed) {
+ clk_set_rate(tsd->clk, speed);
+ tsd->cur_speed = speed;
+ }
+
+ tsd->cur_spi = spi;
+ tsd->cur_pos = 0;
+ tsd->cur_rx_pos = 0;
+ tsd->cur_tx_pos = 0;
+ tsd->curr_xfer = t;
+ tegra_sflash_calculate_curr_xfer_param(spi, tsd, t);
+ if (is_first_of_msg) {
+ command = tsd->def_command_reg;
+ command |= SPI_BIT_LENGTH(t->bits_per_word - 1);
+ command |= SPI_CS_VAL_HIGH;
+
+ command &= ~SPI_MODES;
+ if (spi->mode & SPI_CPHA)
+ command |= SPI_CK_SDA_FALLING;
+
+ if (spi->mode & SPI_CPOL)
+ command |= SPI_ACTIVE_SCLK_DRIVE_HIGH;
+ else
+ command |= SPI_ACTIVE_SCLK_DRIVE_LOW;
+ command |= SPI_CS0_EN << spi->chip_select;
+ } else {
+ command = tsd->command_reg;
+ command &= ~SPI_BIT_LENGTH(~0);
+ command |= SPI_BIT_LENGTH(t->bits_per_word - 1);
+ command &= ~(SPI_RX_EN | SPI_TX_EN);
+ }
+
+ tsd->cur_direction = 0;
+ if (t->rx_buf) {
+ command |= SPI_RX_EN;
+ tsd->cur_direction |= DATA_DIR_RX;
+ }
+ if (t->tx_buf) {
+ command |= SPI_TX_EN;
+ tsd->cur_direction |= DATA_DIR_TX;
+ }
+ tegra_sflash_writel(tsd, command, SPI_COMMAND);
+ tsd->command_reg = command;
+
+ return tegra_sflash_start_cpu_based_transfer(tsd, t);
+}
+
+static int tegra_sflash_transfer_one_message(struct spi_master *master,
+ struct spi_message *msg)
+{
+ bool is_first_msg = true;
+ int single_xfer;
+ struct tegra_sflash_data *tsd = spi_master_get_devdata(master);
+ struct spi_transfer *xfer;
+ struct spi_device *spi = msg->spi;
+ int ret;
+
+ ret = pm_runtime_get_sync(tsd->dev);
+ if (ret < 0) {
+ dev_err(tsd->dev, "pm_runtime_get() failed, err = %d\n", ret);
+ return ret;
+ }
+
+ msg->status = 0;
+ msg->actual_length = 0;
+ single_xfer = list_is_singular(&msg->transfers);
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ INIT_COMPLETION(tsd->xfer_completion);
+ ret = tegra_sflash_start_transfer_one(spi, xfer,
+ is_first_msg, single_xfer);
+ if (ret < 0) {
+ dev_err(tsd->dev,
+ "spi can not start transfer, err %d\n", ret);
+ goto exit;
+ }
+ is_first_msg = false;
+ ret = wait_for_completion_timeout(&tsd->xfer_completion,
+ SPI_DMA_TIMEOUT);
+ if (WARN_ON(ret == 0)) {
+ dev_err(tsd->dev,
+ "spi trasfer timeout, err %d\n", ret);
+ ret = -EIO;
+ goto exit;
+ }
+
+ if (tsd->tx_status || tsd->rx_status) {
+ dev_err(tsd->dev, "Error in Transfer\n");
+ ret = -EIO;
+ goto exit;
+ }
+ msg->actual_length += xfer->len;
+ if (xfer->cs_change && xfer->delay_usecs) {
+ tegra_sflash_writel(tsd, tsd->def_command_reg,
+ SPI_COMMAND);
+ udelay(xfer->delay_usecs);
+ }
+ }
+ ret = 0;
+exit:
+ tegra_sflash_writel(tsd, tsd->def_command_reg, SPI_COMMAND);
+ msg->status = ret;
+ spi_finalize_current_message(master);
+ pm_runtime_put(tsd->dev);
+ return ret;
+}
+
+static irqreturn_t handle_cpu_based_xfer(struct tegra_sflash_data *tsd)
+{
+ struct spi_transfer *t = tsd->curr_xfer;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tsd->lock, flags);
+ if (tsd->tx_status || tsd->rx_status || (tsd->status_reg & SPI_BSY)) {
+ dev_err(tsd->dev,
+ "CpuXfer ERROR bit set 0x%x\n", tsd->status_reg);
+ dev_err(tsd->dev,
+ "CpuXfer 0x%08x:0x%08x\n", tsd->command_reg,
+ tsd->dma_control_reg);
+ tegra_periph_reset_assert(tsd->clk);
+ udelay(2);
+ tegra_periph_reset_deassert(tsd->clk);
+ complete(&tsd->xfer_completion);
+ goto exit;
+ }
+
+ if (tsd->cur_direction & DATA_DIR_RX)
+ tegra_sflash_read_rx_fifo_to_client_rxbuf(tsd, t);
+
+ if (tsd->cur_direction & DATA_DIR_TX)
+ tsd->cur_pos = tsd->cur_tx_pos;
+ else
+ tsd->cur_pos = tsd->cur_rx_pos;
+
+ if (tsd->cur_pos == t->len) {
+ complete(&tsd->xfer_completion);
+ goto exit;
+ }
+
+ tegra_sflash_calculate_curr_xfer_param(tsd->cur_spi, tsd, t);
+ tegra_sflash_start_cpu_based_transfer(tsd, t);
+exit:
+ spin_unlock_irqrestore(&tsd->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t tegra_sflash_isr(int irq, void *context_data)
+{
+ struct tegra_sflash_data *tsd = context_data;
+
+ tsd->status_reg = tegra_sflash_readl(tsd, SPI_STATUS);
+ if (tsd->cur_direction & DATA_DIR_TX)
+ tsd->tx_status = tsd->status_reg & SPI_TX_OVF;
+
+ if (tsd->cur_direction & DATA_DIR_RX)
+ tsd->rx_status = tsd->status_reg & SPI_RX_UNF;
+ tegra_sflash_clear_status(tsd);
+
+ return handle_cpu_based_xfer(tsd);
+}
+
+static struct tegra_spi_platform_data *tegra_sflash_parse_dt(
+ struct platform_device *pdev)
+{
+ struct tegra_spi_platform_data *pdata;
+ struct device_node *np = pdev->dev.of_node;
+ u32 max_freq;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&pdev->dev, "Memory alloc for pdata failed\n");
+ return NULL;
+ }
+
+ if (!of_property_read_u32(np, "spi-max-frequency", &max_freq))
+ pdata->spi_max_frequency = max_freq;
+
+ return pdata;
+}
+
+static struct of_device_id tegra_sflash_of_match[] = {
+ { .compatible = "nvidia,tegra20-sflash", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, tegra_sflash_of_match);
+
+static int tegra_sflash_probe(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct tegra_sflash_data *tsd;
+ struct resource *r;
+ struct tegra_spi_platform_data *pdata = pdev->dev.platform_data;
+ int ret;
+ const struct of_device_id *match;
+
+ match = of_match_device(of_match_ptr(tegra_sflash_of_match),
+ &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+
+ if (!pdata && pdev->dev.of_node)
+ pdata = tegra_sflash_parse_dt(pdev);
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data, exiting\n");
+ return -ENODEV;
+ }
+
+ if (!pdata->spi_max_frequency)
+ pdata->spi_max_frequency = 25000000; /* 25MHz */
+
+ master = spi_alloc_master(&pdev->dev, sizeof(*tsd));
+ if (!master) {
+ dev_err(&pdev->dev, "master allocation failed\n");
+ return -ENOMEM;
+ }
+
+ /* the spi->mode bits understood by this driver: */
+ master->mode_bits = SPI_CPOL | SPI_CPHA;
+ master->transfer_one_message = tegra_sflash_transfer_one_message;
+ master->num_chipselect = MAX_CHIP_SELECT;
+ master->bus_num = -1;
+
+ dev_set_drvdata(&pdev->dev, master);
+ tsd = spi_master_get_devdata(master);
+ tsd->master = master;
+ tsd->dev = &pdev->dev;
+ spin_lock_init(&tsd->lock);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "No IO memory resource\n");
+ ret = -ENODEV;
+ goto exit_free_master;
+ }
+ tsd->base = devm_request_and_ioremap(&pdev->dev, r);
+ if (!tsd->base) {
+ dev_err(&pdev->dev,
+ "Cannot request memregion/iomap dma address\n");
+ ret = -EADDRNOTAVAIL;
+ goto exit_free_master;
+ }
+
+ tsd->irq = platform_get_irq(pdev, 0);
+ ret = request_irq(tsd->irq, tegra_sflash_isr, 0,
+ dev_name(&pdev->dev), tsd);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n",
+ tsd->irq);
+ goto exit_free_master;
+ }
+
+ tsd->clk = devm_clk_get(&pdev->dev, "spi");
+ if (IS_ERR(tsd->clk)) {
+ dev_err(&pdev->dev, "can not get clock\n");
+ ret = PTR_ERR(tsd->clk);
+ goto exit_free_irq;
+ }
+
+ tsd->spi_max_frequency = pdata->spi_max_frequency;
+ init_completion(&tsd->xfer_completion);
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra_sflash_runtime_resume(&pdev->dev);
+ if (ret)
+ goto exit_pm_disable;
+ }
+
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret);
+ goto exit_pm_disable;
+ }
+
+ /* Reset controller */
+ tegra_periph_reset_assert(tsd->clk);
+ udelay(2);
+ tegra_periph_reset_deassert(tsd->clk);
+
+ tsd->def_command_reg = SPI_M_S | SPI_CS_SW;
+ tegra_sflash_writel(tsd, tsd->def_command_reg, SPI_COMMAND);
+ pm_runtime_put(&pdev->dev);
+
+ master->dev.of_node = pdev->dev.of_node;
+ ret = spi_register_master(master);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "can not register to master err %d\n", ret);
+ goto exit_pm_disable;
+ }
+ return ret;
+
+exit_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra_sflash_runtime_suspend(&pdev->dev);
+exit_free_irq:
+ free_irq(tsd->irq, tsd);
+exit_free_master:
+ spi_master_put(master);
+ return ret;
+}
+
+static int tegra_sflash_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = dev_get_drvdata(&pdev->dev);
+ struct tegra_sflash_data *tsd = spi_master_get_devdata(master);
+
+ free_irq(tsd->irq, tsd);
+ spi_unregister_master(master);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra_sflash_runtime_suspend(&pdev->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tegra_sflash_suspend(struct device *dev)
+{
+ struct spi_master *master = dev_get_drvdata(dev);
+
+ return spi_master_suspend(master);
+}
+
+static int tegra_sflash_resume(struct device *dev)
+{
+ struct spi_master *master = dev_get_drvdata(dev);
+ struct tegra_sflash_data *tsd = spi_master_get_devdata(master);
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "pm runtime failed, e = %d\n", ret);
+ return ret;
+ }
+ tegra_sflash_writel(tsd, tsd->command_reg, SPI_COMMAND);
+ pm_runtime_put(dev);
+
+ return spi_master_resume(master);
+}
+#endif
+
+static int tegra_sflash_runtime_suspend(struct device *dev)
+{
+ struct spi_master *master = dev_get_drvdata(dev);
+ struct tegra_sflash_data *tsd = spi_master_get_devdata(master);
+
+ /* Flush all write which are in PPSB queue by reading back */
+ tegra_sflash_readl(tsd, SPI_COMMAND);
+
+ clk_disable_unprepare(tsd->clk);
+ return 0;
+}
+
+static int tegra_sflash_runtime_resume(struct device *dev)
+{
+ struct spi_master *master = dev_get_drvdata(dev);
+ struct tegra_sflash_data *tsd = spi_master_get_devdata(master);
+ int ret;
+
+ ret = clk_prepare_enable(tsd->clk);
+ if (ret < 0) {
+ dev_err(tsd->dev, "clk_prepare failed: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops slink_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra_sflash_runtime_suspend,
+ tegra_sflash_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(tegra_sflash_suspend, tegra_sflash_resume)
+};
+static struct platform_driver tegra_sflash_driver = {
+ .driver = {
+ .name = "spi-tegra-sflash",
+ .owner = THIS_MODULE,
+ .pm = &slink_pm_ops,
+ .of_match_table = of_match_ptr(tegra_sflash_of_match),
+ },
+ .probe = tegra_sflash_probe,
+ .remove = tegra_sflash_remove,
+};
+module_platform_driver(tegra_sflash_driver);
+
+MODULE_ALIAS("platform:spi-tegra-sflash");
+MODULE_DESCRIPTION("NVIDIA Tegra20 Serial Flash Controller Driver");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c
new file mode 100644
index 00000000000..651167f2e0a
--- /dev/null
+++ b/drivers/spi/spi-tegra20-slink.c
@@ -0,0 +1,1358 @@
+/*
+ * SPI driver for Nvidia's Tegra20/Tegra30 SLINK Controller.
+ *
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi-tegra.h>
+#include <mach/clk.h>
+
+#define SLINK_COMMAND 0x000
+#define SLINK_BIT_LENGTH(x) (((x) & 0x1f) << 0)
+#define SLINK_WORD_SIZE(x) (((x) & 0x1f) << 5)
+#define SLINK_BOTH_EN (1 << 10)
+#define SLINK_CS_SW (1 << 11)
+#define SLINK_CS_VALUE (1 << 12)
+#define SLINK_CS_POLARITY (1 << 13)
+#define SLINK_IDLE_SDA_DRIVE_LOW (0 << 16)
+#define SLINK_IDLE_SDA_DRIVE_HIGH (1 << 16)
+#define SLINK_IDLE_SDA_PULL_LOW (2 << 16)
+#define SLINK_IDLE_SDA_PULL_HIGH (3 << 16)
+#define SLINK_IDLE_SDA_MASK (3 << 16)
+#define SLINK_CS_POLARITY1 (1 << 20)
+#define SLINK_CK_SDA (1 << 21)
+#define SLINK_CS_POLARITY2 (1 << 22)
+#define SLINK_CS_POLARITY3 (1 << 23)
+#define SLINK_IDLE_SCLK_DRIVE_LOW (0 << 24)
+#define SLINK_IDLE_SCLK_DRIVE_HIGH (1 << 24)
+#define SLINK_IDLE_SCLK_PULL_LOW (2 << 24)
+#define SLINK_IDLE_SCLK_PULL_HIGH (3 << 24)
+#define SLINK_IDLE_SCLK_MASK (3 << 24)
+#define SLINK_M_S (1 << 28)
+#define SLINK_WAIT (1 << 29)
+#define SLINK_GO (1 << 30)
+#define SLINK_ENB (1 << 31)
+
+#define SLINK_MODES (SLINK_IDLE_SCLK_MASK | SLINK_CK_SDA)
+
+#define SLINK_COMMAND2 0x004
+#define SLINK_LSBFE (1 << 0)
+#define SLINK_SSOE (1 << 1)
+#define SLINK_SPIE (1 << 4)
+#define SLINK_BIDIROE (1 << 6)
+#define SLINK_MODFEN (1 << 7)
+#define SLINK_INT_SIZE(x) (((x) & 0x1f) << 8)
+#define SLINK_CS_ACTIVE_BETWEEN (1 << 17)
+#define SLINK_SS_EN_CS(x) (((x) & 0x3) << 18)
+#define SLINK_SS_SETUP(x) (((x) & 0x3) << 20)
+#define SLINK_FIFO_REFILLS_0 (0 << 22)
+#define SLINK_FIFO_REFILLS_1 (1 << 22)
+#define SLINK_FIFO_REFILLS_2 (2 << 22)
+#define SLINK_FIFO_REFILLS_3 (3 << 22)
+#define SLINK_FIFO_REFILLS_MASK (3 << 22)
+#define SLINK_WAIT_PACK_INT(x) (((x) & 0x7) << 26)
+#define SLINK_SPC0 (1 << 29)
+#define SLINK_TXEN (1 << 30)
+#define SLINK_RXEN (1 << 31)
+
+#define SLINK_STATUS 0x008
+#define SLINK_COUNT(val) (((val) >> 0) & 0x1f)
+#define SLINK_WORD(val) (((val) >> 5) & 0x1f)
+#define SLINK_BLK_CNT(val) (((val) >> 0) & 0xffff)
+#define SLINK_MODF (1 << 16)
+#define SLINK_RX_UNF (1 << 18)
+#define SLINK_TX_OVF (1 << 19)
+#define SLINK_TX_FULL (1 << 20)
+#define SLINK_TX_EMPTY (1 << 21)
+#define SLINK_RX_FULL (1 << 22)
+#define SLINK_RX_EMPTY (1 << 23)
+#define SLINK_TX_UNF (1 << 24)
+#define SLINK_RX_OVF (1 << 25)
+#define SLINK_TX_FLUSH (1 << 26)
+#define SLINK_RX_FLUSH (1 << 27)
+#define SLINK_SCLK (1 << 28)
+#define SLINK_ERR (1 << 29)
+#define SLINK_RDY (1 << 30)
+#define SLINK_BSY (1 << 31)
+#define SLINK_FIFO_ERROR (SLINK_TX_OVF | SLINK_RX_UNF | \
+ SLINK_TX_UNF | SLINK_RX_OVF)
+
+#define SLINK_FIFO_EMPTY (SLINK_TX_EMPTY | SLINK_RX_EMPTY)
+
+#define SLINK_MAS_DATA 0x010
+#define SLINK_SLAVE_DATA 0x014
+
+#define SLINK_DMA_CTL 0x018
+#define SLINK_DMA_BLOCK_SIZE(x) (((x) & 0xffff) << 0)
+#define SLINK_TX_TRIG_1 (0 << 16)
+#define SLINK_TX_TRIG_4 (1 << 16)
+#define SLINK_TX_TRIG_8 (2 << 16)
+#define SLINK_TX_TRIG_16 (3 << 16)
+#define SLINK_TX_TRIG_MASK (3 << 16)
+#define SLINK_RX_TRIG_1 (0 << 18)
+#define SLINK_RX_TRIG_4 (1 << 18)
+#define SLINK_RX_TRIG_8 (2 << 18)
+#define SLINK_RX_TRIG_16 (3 << 18)
+#define SLINK_RX_TRIG_MASK (3 << 18)
+#define SLINK_PACKED (1 << 20)
+#define SLINK_PACK_SIZE_4 (0 << 21)
+#define SLINK_PACK_SIZE_8 (1 << 21)
+#define SLINK_PACK_SIZE_16 (2 << 21)
+#define SLINK_PACK_SIZE_32 (3 << 21)
+#define SLINK_PACK_SIZE_MASK (3 << 21)
+#define SLINK_IE_TXC (1 << 26)
+#define SLINK_IE_RXC (1 << 27)
+#define SLINK_DMA_EN (1 << 31)
+
+#define SLINK_STATUS2 0x01c
+#define SLINK_TX_FIFO_EMPTY_COUNT(val) (((val) & 0x3f) >> 0)
+#define SLINK_RX_FIFO_FULL_COUNT(val) (((val) & 0x3f0000) >> 16)
+#define SLINK_SS_HOLD_TIME(val) (((val) & 0xF) << 6)
+
+#define SLINK_TX_FIFO 0x100
+#define SLINK_RX_FIFO 0x180
+
+#define DATA_DIR_TX (1 << 0)
+#define DATA_DIR_RX (1 << 1)
+
+#define SLINK_DMA_TIMEOUT (msecs_to_jiffies(1000))
+
+#define DEFAULT_SPI_DMA_BUF_LEN (16*1024)
+#define TX_FIFO_EMPTY_COUNT_MAX SLINK_TX_FIFO_EMPTY_COUNT(0x20)
+#define RX_FIFO_FULL_COUNT_ZERO SLINK_RX_FIFO_FULL_COUNT(0)
+
+#define SLINK_STATUS2_RESET \
+ (TX_FIFO_EMPTY_COUNT_MAX | RX_FIFO_FULL_COUNT_ZERO << 16)
+
+#define MAX_CHIP_SELECT 4
+#define SLINK_FIFO_DEPTH 32
+
+struct tegra_slink_chip_data {
+ bool cs_hold_time;
+};
+
+struct tegra_slink_data {
+ struct device *dev;
+ struct spi_master *master;
+ const struct tegra_slink_chip_data *chip_data;
+ spinlock_t lock;
+
+ struct clk *clk;
+ void __iomem *base;
+ phys_addr_t phys;
+ unsigned irq;
+ int dma_req_sel;
+ u32 spi_max_frequency;
+ u32 cur_speed;
+
+ struct spi_device *cur_spi;
+ unsigned cur_pos;
+ unsigned cur_len;
+ unsigned words_per_32bit;
+ unsigned bytes_per_word;
+ unsigned curr_dma_words;
+ unsigned cur_direction;
+
+ unsigned cur_rx_pos;
+ unsigned cur_tx_pos;
+
+ unsigned dma_buf_size;
+ unsigned max_buf_size;
+ bool is_curr_dma_xfer;
+ bool is_hw_based_cs;
+
+ struct completion rx_dma_complete;
+ struct completion tx_dma_complete;
+
+ u32 tx_status;
+ u32 rx_status;
+ u32 status_reg;
+ bool is_packed;
+ unsigned long packed_size;
+
+ u32 command_reg;
+ u32 command2_reg;
+ u32 dma_control_reg;
+ u32 def_command_reg;
+ u32 def_command2_reg;
+
+ struct completion xfer_completion;
+ struct spi_transfer *curr_xfer;
+ struct dma_chan *rx_dma_chan;
+ u32 *rx_dma_buf;
+ dma_addr_t rx_dma_phys;
+ struct dma_async_tx_descriptor *rx_dma_desc;
+
+ struct dma_chan *tx_dma_chan;
+ u32 *tx_dma_buf;
+ dma_addr_t tx_dma_phys;
+ struct dma_async_tx_descriptor *tx_dma_desc;
+};
+
+static int tegra_slink_runtime_suspend(struct device *dev);
+static int tegra_slink_runtime_resume(struct device *dev);
+
+static inline unsigned long tegra_slink_readl(struct tegra_slink_data *tspi,
+ unsigned long reg)
+{
+ return readl(tspi->base + reg);
+}
+
+static inline void tegra_slink_writel(struct tegra_slink_data *tspi,
+ unsigned long val, unsigned long reg)
+{
+ writel(val, tspi->base + reg);
+
+ /* Read back register to make sure that register writes completed */
+ if (reg != SLINK_TX_FIFO)
+ readl(tspi->base + SLINK_MAS_DATA);
+}
+
+static void tegra_slink_clear_status(struct tegra_slink_data *tspi)
+{
+ unsigned long val;
+ unsigned long val_write = 0;
+
+ val = tegra_slink_readl(tspi, SLINK_STATUS);
+
+ /* Write 1 to clear status register */
+ val_write = SLINK_RDY | SLINK_FIFO_ERROR;
+ tegra_slink_writel(tspi, val_write, SLINK_STATUS);
+}
+
+static unsigned long tegra_slink_get_packed_size(struct tegra_slink_data *tspi,
+ struct spi_transfer *t)
+{
+ unsigned long val;
+
+ switch (tspi->bytes_per_word) {
+ case 0:
+ val = SLINK_PACK_SIZE_4;
+ break;
+ case 1:
+ val = SLINK_PACK_SIZE_8;
+ break;
+ case 2:
+ val = SLINK_PACK_SIZE_16;
+ break;
+ case 4:
+ val = SLINK_PACK_SIZE_32;
+ break;
+ default:
+ val = 0;
+ }
+ return val;
+}
+
+static unsigned tegra_slink_calculate_curr_xfer_param(
+ struct spi_device *spi, struct tegra_slink_data *tspi,
+ struct spi_transfer *t)
+{
+ unsigned remain_len = t->len - tspi->cur_pos;
+ unsigned max_word;
+ unsigned bits_per_word ;
+ unsigned max_len;
+ unsigned total_fifo_words;
+
+ bits_per_word = t->bits_per_word ? t->bits_per_word :
+ spi->bits_per_word;
+ tspi->bytes_per_word = (bits_per_word - 1) / 8 + 1;
+
+ if (bits_per_word == 8 || bits_per_word == 16) {
+ tspi->is_packed = 1;
+ tspi->words_per_32bit = 32/bits_per_word;
+ } else {
+ tspi->is_packed = 0;
+ tspi->words_per_32bit = 1;
+ }
+ tspi->packed_size = tegra_slink_get_packed_size(tspi, t);
+
+ if (tspi->is_packed) {
+ max_len = min(remain_len, tspi->max_buf_size);
+ tspi->curr_dma_words = max_len/tspi->bytes_per_word;
+ total_fifo_words = max_len/4;
+ } else {
+ max_word = (remain_len - 1) / tspi->bytes_per_word + 1;
+ max_word = min(max_word, tspi->max_buf_size/4);
+ tspi->curr_dma_words = max_word;
+ total_fifo_words = max_word;
+ }
+ return total_fifo_words;
+}
+
+static unsigned tegra_slink_fill_tx_fifo_from_client_txbuf(
+ struct tegra_slink_data *tspi, struct spi_transfer *t)
+{
+ unsigned nbytes;
+ unsigned tx_empty_count;
+ unsigned long fifo_status;
+ unsigned max_n_32bit;
+ unsigned i, count;
+ unsigned long x;
+ unsigned int written_words;
+ unsigned fifo_words_left;
+ u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_tx_pos;
+
+ fifo_status = tegra_slink_readl(tspi, SLINK_STATUS2);
+ tx_empty_count = SLINK_TX_FIFO_EMPTY_COUNT(fifo_status);
+
+ if (tspi->is_packed) {
+ fifo_words_left = tx_empty_count * tspi->words_per_32bit;
+ written_words = min(fifo_words_left, tspi->curr_dma_words);
+ nbytes = written_words * tspi->bytes_per_word;
+ max_n_32bit = DIV_ROUND_UP(nbytes, 4);
+ for (count = 0; count < max_n_32bit; count++) {
+ x = 0;
+ for (i = 0; (i < 4) && nbytes; i++, nbytes--)
+ x |= (*tx_buf++) << (i*8);
+ tegra_slink_writel(tspi, x, SLINK_TX_FIFO);
+ }
+ } else {
+ max_n_32bit = min(tspi->curr_dma_words, tx_empty_count);
+ written_words = max_n_32bit;
+ nbytes = written_words * tspi->bytes_per_word;
+ for (count = 0; count < max_n_32bit; count++) {
+ x = 0;
+ for (i = 0; nbytes && (i < tspi->bytes_per_word);
+ i++, nbytes--)
+ x |= ((*tx_buf++) << i*8);
+ tegra_slink_writel(tspi, x, SLINK_TX_FIFO);
+ }
+ }
+ tspi->cur_tx_pos += written_words * tspi->bytes_per_word;
+ return written_words;
+}
+
+static unsigned int tegra_slink_read_rx_fifo_to_client_rxbuf(
+ struct tegra_slink_data *tspi, struct spi_transfer *t)
+{
+ unsigned rx_full_count;
+ unsigned long fifo_status;
+ unsigned i, count;
+ unsigned long x;
+ unsigned int read_words = 0;
+ unsigned len;
+ u8 *rx_buf = (u8 *)t->rx_buf + tspi->cur_rx_pos;
+
+ fifo_status = tegra_slink_readl(tspi, SLINK_STATUS2);
+ rx_full_count = SLINK_RX_FIFO_FULL_COUNT(fifo_status);
+ if (tspi->is_packed) {
+ len = tspi->curr_dma_words * tspi->bytes_per_word;
+ for (count = 0; count < rx_full_count; count++) {
+ x = tegra_slink_readl(tspi, SLINK_RX_FIFO);
+ for (i = 0; len && (i < 4); i++, len--)
+ *rx_buf++ = (x >> i*8) & 0xFF;
+ }
+ tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word;
+ read_words += tspi->curr_dma_words;
+ } else {
+ unsigned int bits_per_word;
+
+ bits_per_word = t->bits_per_word ? t->bits_per_word :
+ tspi->cur_spi->bits_per_word;
+ for (count = 0; count < rx_full_count; count++) {
+ x = tegra_slink_readl(tspi, SLINK_RX_FIFO);
+ for (i = 0; (i < tspi->bytes_per_word); i++)
+ *rx_buf++ = (x >> (i*8)) & 0xFF;
+ }
+ tspi->cur_rx_pos += rx_full_count * tspi->bytes_per_word;
+ read_words += rx_full_count;
+ }
+ return read_words;
+}
+
+static void tegra_slink_copy_client_txbuf_to_spi_txbuf(
+ struct tegra_slink_data *tspi, struct spi_transfer *t)
+{
+ unsigned len;
+
+ /* Make the dma buffer to read by cpu */
+ dma_sync_single_for_cpu(tspi->dev, tspi->tx_dma_phys,
+ tspi->dma_buf_size, DMA_TO_DEVICE);
+
+ if (tspi->is_packed) {
+ len = tspi->curr_dma_words * tspi->bytes_per_word;
+ memcpy(tspi->tx_dma_buf, t->tx_buf + tspi->cur_pos, len);
+ } else {
+ unsigned int i;
+ unsigned int count;
+ u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_tx_pos;
+ unsigned consume = tspi->curr_dma_words * tspi->bytes_per_word;
+ unsigned int x;
+
+ for (count = 0; count < tspi->curr_dma_words; count++) {
+ x = 0;
+ for (i = 0; consume && (i < tspi->bytes_per_word);
+ i++, consume--)
+ x |= ((*tx_buf++) << i * 8);
+ tspi->tx_dma_buf[count] = x;
+ }
+ }
+ tspi->cur_tx_pos += tspi->curr_dma_words * tspi->bytes_per_word;
+
+ /* Make the dma buffer to read by dma */
+ dma_sync_single_for_device(tspi->dev, tspi->tx_dma_phys,
+ tspi->dma_buf_size, DMA_TO_DEVICE);
+}
+
+static void tegra_slink_copy_spi_rxbuf_to_client_rxbuf(
+ struct tegra_slink_data *tspi, struct spi_transfer *t)
+{
+ unsigned len;
+
+ /* Make the dma buffer to read by cpu */
+ dma_sync_single_for_cpu(tspi->dev, tspi->rx_dma_phys,
+ tspi->dma_buf_size, DMA_FROM_DEVICE);
+
+ if (tspi->is_packed) {
+ len = tspi->curr_dma_words * tspi->bytes_per_word;
+ memcpy(t->rx_buf + tspi->cur_rx_pos, tspi->rx_dma_buf, len);
+ } else {
+ unsigned int i;
+ unsigned int count;
+ unsigned char *rx_buf = t->rx_buf + tspi->cur_rx_pos;
+ unsigned int x;
+ unsigned int rx_mask, bits_per_word;
+
+ bits_per_word = t->bits_per_word ? t->bits_per_word :
+ tspi->cur_spi->bits_per_word;
+ rx_mask = (1 << bits_per_word) - 1;
+ for (count = 0; count < tspi->curr_dma_words; count++) {
+ x = tspi->rx_dma_buf[count];
+ x &= rx_mask;
+ for (i = 0; (i < tspi->bytes_per_word); i++)
+ *rx_buf++ = (x >> (i*8)) & 0xFF;
+ }
+ }
+ tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word;
+
+ /* Make the dma buffer to read by dma */
+ dma_sync_single_for_device(tspi->dev, tspi->rx_dma_phys,
+ tspi->dma_buf_size, DMA_FROM_DEVICE);
+}
+
+static void tegra_slink_dma_complete(void *args)
+{
+ struct completion *dma_complete = args;
+
+ complete(dma_complete);
+}
+
+static int tegra_slink_start_tx_dma(struct tegra_slink_data *tspi, int len)
+{
+ INIT_COMPLETION(tspi->tx_dma_complete);
+ tspi->tx_dma_desc = dmaengine_prep_slave_single(tspi->tx_dma_chan,
+ tspi->tx_dma_phys, len, DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!tspi->tx_dma_desc) {
+ dev_err(tspi->dev, "Not able to get desc for Tx\n");
+ return -EIO;
+ }
+
+ tspi->tx_dma_desc->callback = tegra_slink_dma_complete;
+ tspi->tx_dma_desc->callback_param = &tspi->tx_dma_complete;
+
+ dmaengine_submit(tspi->tx_dma_desc);
+ dma_async_issue_pending(tspi->tx_dma_chan);
+ return 0;
+}
+
+static int tegra_slink_start_rx_dma(struct tegra_slink_data *tspi, int len)
+{
+ INIT_COMPLETION(tspi->rx_dma_complete);
+ tspi->rx_dma_desc = dmaengine_prep_slave_single(tspi->rx_dma_chan,
+ tspi->rx_dma_phys, len, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!tspi->rx_dma_desc) {
+ dev_err(tspi->dev, "Not able to get desc for Rx\n");
+ return -EIO;
+ }
+
+ tspi->rx_dma_desc->callback = tegra_slink_dma_complete;
+ tspi->rx_dma_desc->callback_param = &tspi->rx_dma_complete;
+
+ dmaengine_submit(tspi->rx_dma_desc);
+ dma_async_issue_pending(tspi->rx_dma_chan);
+ return 0;
+}
+
+static int tegra_slink_start_dma_based_transfer(
+ struct tegra_slink_data *tspi, struct spi_transfer *t)
+{
+ unsigned long val;
+ unsigned long test_val;
+ unsigned int len;
+ int ret = 0;
+ unsigned long status;
+
+ /* Make sure that Rx and Tx fifo are empty */
+ status = tegra_slink_readl(tspi, SLINK_STATUS);
+ if ((status & SLINK_FIFO_EMPTY) != SLINK_FIFO_EMPTY) {
+ dev_err(tspi->dev,
+ "Rx/Tx fifo are not empty status 0x%08lx\n", status);
+ return -EIO;
+ }
+
+ val = SLINK_DMA_BLOCK_SIZE(tspi->curr_dma_words - 1);
+ val |= tspi->packed_size;
+ if (tspi->is_packed)
+ len = DIV_ROUND_UP(tspi->curr_dma_words * tspi->bytes_per_word,
+ 4) * 4;
+ else
+ len = tspi->curr_dma_words * 4;
+
+ /* Set attention level based on length of transfer */
+ if (len & 0xF)
+ val |= SLINK_TX_TRIG_1 | SLINK_RX_TRIG_1;
+ else if (((len) >> 4) & 0x1)
+ val |= SLINK_TX_TRIG_4 | SLINK_RX_TRIG_4;
+ else
+ val |= SLINK_TX_TRIG_8 | SLINK_RX_TRIG_8;
+
+ if (tspi->cur_direction & DATA_DIR_TX)
+ val |= SLINK_IE_TXC;
+
+ if (tspi->cur_direction & DATA_DIR_RX)
+ val |= SLINK_IE_RXC;
+
+ tegra_slink_writel(tspi, val, SLINK_DMA_CTL);
+ tspi->dma_control_reg = val;
+
+ if (tspi->cur_direction & DATA_DIR_TX) {
+ tegra_slink_copy_client_txbuf_to_spi_txbuf(tspi, t);
+ wmb();
+ ret = tegra_slink_start_tx_dma(tspi, len);
+ if (ret < 0) {
+ dev_err(tspi->dev,
+ "Starting tx dma failed, err %d\n", ret);
+ return ret;
+ }
+
+ /* Wait for tx fifo to be fill before starting slink */
+ test_val = tegra_slink_readl(tspi, SLINK_STATUS);
+ while (!(test_val & SLINK_TX_FULL))
+ test_val = tegra_slink_readl(tspi, SLINK_STATUS);
+ }
+
+ if (tspi->cur_direction & DATA_DIR_RX) {
+ /* Make the dma buffer to read by dma */
+ dma_sync_single_for_device(tspi->dev, tspi->rx_dma_phys,
+ tspi->dma_buf_size, DMA_FROM_DEVICE);
+
+ ret = tegra_slink_start_rx_dma(tspi, len);
+ if (ret < 0) {
+ dev_err(tspi->dev,
+ "Starting rx dma failed, err %d\n", ret);
+ if (tspi->cur_direction & DATA_DIR_TX)
+ dmaengine_terminate_all(tspi->tx_dma_chan);
+ return ret;
+ }
+ }
+ tspi->is_curr_dma_xfer = true;
+ if (tspi->is_packed) {
+ val |= SLINK_PACKED;
+ tegra_slink_writel(tspi, val, SLINK_DMA_CTL);
+ /* HW need small delay after settign Packed mode */
+ udelay(1);
+ }
+ tspi->dma_control_reg = val;
+
+ val |= SLINK_DMA_EN;
+ tegra_slink_writel(tspi, val, SLINK_DMA_CTL);
+ return ret;
+}
+
+static int tegra_slink_start_cpu_based_transfer(
+ struct tegra_slink_data *tspi, struct spi_transfer *t)
+{
+ unsigned long val;
+ unsigned cur_words;
+
+ val = tspi->packed_size;
+ if (tspi->cur_direction & DATA_DIR_TX)
+ val |= SLINK_IE_TXC;
+
+ if (tspi->cur_direction & DATA_DIR_RX)
+ val |= SLINK_IE_RXC;
+
+ tegra_slink_writel(tspi, val, SLINK_DMA_CTL);
+ tspi->dma_control_reg = val;
+
+ if (tspi->cur_direction & DATA_DIR_TX)
+ cur_words = tegra_slink_fill_tx_fifo_from_client_txbuf(tspi, t);
+ else
+ cur_words = tspi->curr_dma_words;
+ val |= SLINK_DMA_BLOCK_SIZE(cur_words - 1);
+ tegra_slink_writel(tspi, val, SLINK_DMA_CTL);
+ tspi->dma_control_reg = val;
+
+ tspi->is_curr_dma_xfer = false;
+ if (tspi->is_packed) {
+ val |= SLINK_PACKED;
+ tegra_slink_writel(tspi, val, SLINK_DMA_CTL);
+ udelay(1);
+ wmb();
+ }
+ tspi->dma_control_reg = val;
+ val |= SLINK_DMA_EN;
+ tegra_slink_writel(tspi, val, SLINK_DMA_CTL);
+ return 0;
+}
+
+static int tegra_slink_init_dma_param(struct tegra_slink_data *tspi,
+ bool dma_to_memory)
+{
+ struct dma_chan *dma_chan;
+ u32 *dma_buf;
+ dma_addr_t dma_phys;
+ int ret;
+ struct dma_slave_config dma_sconfig;
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_chan = dma_request_channel(mask, NULL, NULL);
+ if (!dma_chan) {
+ dev_err(tspi->dev,
+ "Dma channel is not available, will try later\n");
+ return -EPROBE_DEFER;
+ }
+
+ dma_buf = dma_alloc_coherent(tspi->dev, tspi->dma_buf_size,
+ &dma_phys, GFP_KERNEL);
+ if (!dma_buf) {
+ dev_err(tspi->dev, " Not able to allocate the dma buffer\n");
+ dma_release_channel(dma_chan);
+ return -ENOMEM;
+ }
+
+ dma_sconfig.slave_id = tspi->dma_req_sel;
+ if (dma_to_memory) {
+ dma_sconfig.src_addr = tspi->phys + SLINK_RX_FIFO;
+ dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dma_sconfig.src_maxburst = 0;
+ } else {
+ dma_sconfig.dst_addr = tspi->phys + SLINK_TX_FIFO;
+ dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ dma_sconfig.dst_maxburst = 0;
+ }
+
+ ret = dmaengine_slave_config(dma_chan, &dma_sconfig);
+ if (ret)
+ goto scrub;
+ if (dma_to_memory) {
+ tspi->rx_dma_chan = dma_chan;
+ tspi->rx_dma_buf = dma_buf;
+ tspi->rx_dma_phys = dma_phys;
+ } else {
+ tspi->tx_dma_chan = dma_chan;
+ tspi->tx_dma_buf = dma_buf;
+ tspi->tx_dma_phys = dma_phys;
+ }
+ return 0;
+
+scrub:
+ dma_free_coherent(tspi->dev, tspi->dma_buf_size, dma_buf, dma_phys);
+ dma_release_channel(dma_chan);
+ return ret;
+}
+
+static void tegra_slink_deinit_dma_param(struct tegra_slink_data *tspi,
+ bool dma_to_memory)
+{
+ u32 *dma_buf;
+ dma_addr_t dma_phys;
+ struct dma_chan *dma_chan;
+
+ if (dma_to_memory) {
+ dma_buf = tspi->rx_dma_buf;
+ dma_chan = tspi->rx_dma_chan;
+ dma_phys = tspi->rx_dma_phys;
+ tspi->rx_dma_chan = NULL;
+ tspi->rx_dma_buf = NULL;
+ } else {
+ dma_buf = tspi->tx_dma_buf;
+ dma_chan = tspi->tx_dma_chan;
+ dma_phys = tspi->tx_dma_phys;
+ tspi->tx_dma_buf = NULL;
+ tspi->tx_dma_chan = NULL;
+ }
+ if (!dma_chan)
+ return;
+
+ dma_free_coherent(tspi->dev, tspi->dma_buf_size, dma_buf, dma_phys);
+ dma_release_channel(dma_chan);
+}
+
+static int tegra_slink_start_transfer_one(struct spi_device *spi,
+ struct spi_transfer *t, bool is_first_of_msg,
+ bool is_single_xfer)
+{
+ struct tegra_slink_data *tspi = spi_master_get_devdata(spi->master);
+ u32 speed;
+ u8 bits_per_word;
+ unsigned total_fifo_words;
+ int ret;
+ struct tegra_spi_device_controller_data *cdata = spi->controller_data;
+ unsigned long command;
+ unsigned long command2;
+
+ bits_per_word = t->bits_per_word;
+ speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz;
+ if (!speed)
+ speed = tspi->spi_max_frequency;
+ if (speed != tspi->cur_speed) {
+ clk_set_rate(tspi->clk, speed * 4);
+ tspi->cur_speed = speed;
+ }
+
+ tspi->cur_spi = spi;
+ tspi->cur_pos = 0;
+ tspi->cur_rx_pos = 0;
+ tspi->cur_tx_pos = 0;
+ tspi->curr_xfer = t;
+ total_fifo_words = tegra_slink_calculate_curr_xfer_param(spi, tspi, t);
+
+ if (is_first_of_msg) {
+ tegra_slink_clear_status(tspi);
+
+ command = tspi->def_command_reg;
+ command |= SLINK_BIT_LENGTH(bits_per_word - 1);
+
+ command2 = tspi->def_command2_reg;
+ command2 |= SLINK_SS_EN_CS(spi->chip_select);
+
+ /* possibly use the hw based chip select */
+ tspi->is_hw_based_cs = false;
+ if (cdata && cdata->is_hw_based_cs && is_single_xfer &&
+ ((tspi->curr_dma_words * tspi->bytes_per_word) ==
+ (t->len - tspi->cur_pos))) {
+ int setup_count;
+ int sts2;
+
+ setup_count = cdata->cs_setup_clk_count >> 1;
+ setup_count = max(setup_count, 3);
+ command2 |= SLINK_SS_SETUP(setup_count);
+ if (tspi->chip_data->cs_hold_time) {
+ int hold_count;
+
+ hold_count = cdata->cs_hold_clk_count;
+ hold_count = max(hold_count, 0xF);
+ sts2 = tegra_slink_readl(tspi, SLINK_STATUS2);
+ sts2 &= ~SLINK_SS_HOLD_TIME(0xF);
+ sts2 |= SLINK_SS_HOLD_TIME(hold_count);
+ tegra_slink_writel(tspi, sts2, SLINK_STATUS2);
+ }
+ tspi->is_hw_based_cs = true;
+ }
+
+ if (tspi->is_hw_based_cs)
+ command &= ~SLINK_CS_SW;
+ else
+ command |= SLINK_CS_SW | SLINK_CS_VALUE;
+
+ command &= ~SLINK_MODES;
+ if (spi->mode & SPI_CPHA)
+ command |= SLINK_CK_SDA;
+
+ if (spi->mode & SPI_CPOL)
+ command |= SLINK_IDLE_SCLK_DRIVE_HIGH;
+ else
+ command |= SLINK_IDLE_SCLK_DRIVE_LOW;
+ } else {
+ command = tspi->command_reg;
+ command &= ~SLINK_BIT_LENGTH(~0);
+ command |= SLINK_BIT_LENGTH(bits_per_word - 1);
+
+ command2 = tspi->command2_reg;
+ command2 &= ~(SLINK_RXEN | SLINK_TXEN);
+ }
+
+ tegra_slink_writel(tspi, command, SLINK_COMMAND);
+ tspi->command_reg = command;
+
+ tspi->cur_direction = 0;
+ if (t->rx_buf) {
+ command2 |= SLINK_RXEN;
+ tspi->cur_direction |= DATA_DIR_RX;
+ }
+ if (t->tx_buf) {
+ command2 |= SLINK_TXEN;
+ tspi->cur_direction |= DATA_DIR_TX;
+ }
+ tegra_slink_writel(tspi, command2, SLINK_COMMAND2);
+ tspi->command2_reg = command2;
+
+ if (total_fifo_words > SLINK_FIFO_DEPTH)
+ ret = tegra_slink_start_dma_based_transfer(tspi, t);
+ else
+ ret = tegra_slink_start_cpu_based_transfer(tspi, t);
+ return ret;
+}
+
+static int tegra_slink_setup(struct spi_device *spi)
+{
+ struct tegra_slink_data *tspi = spi_master_get_devdata(spi->master);
+ unsigned long val;
+ unsigned long flags;
+ int ret;
+ unsigned int cs_pol_bit[MAX_CHIP_SELECT] = {
+ SLINK_CS_POLARITY,
+ SLINK_CS_POLARITY1,
+ SLINK_CS_POLARITY2,
+ SLINK_CS_POLARITY3,
+ };
+
+ dev_dbg(&spi->dev, "setup %d bpw, %scpol, %scpha, %dHz\n",
+ spi->bits_per_word,
+ spi->mode & SPI_CPOL ? "" : "~",
+ spi->mode & SPI_CPHA ? "" : "~",
+ spi->max_speed_hz);
+
+ BUG_ON(spi->chip_select >= MAX_CHIP_SELECT);
+
+ ret = pm_runtime_get_sync(tspi->dev);
+ if (ret < 0) {
+ dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret);
+ return ret;
+ }
+
+ spin_lock_irqsave(&tspi->lock, flags);
+ val = tspi->def_command_reg;
+ if (spi->mode & SPI_CS_HIGH)
+ val |= cs_pol_bit[spi->chip_select];
+ else
+ val &= ~cs_pol_bit[spi->chip_select];
+ tspi->def_command_reg = val;
+ tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND);
+ spin_unlock_irqrestore(&tspi->lock, flags);
+
+ pm_runtime_put(tspi->dev);
+ return 0;
+}
+
+static int tegra_slink_prepare_transfer(struct spi_master *master)
+{
+ struct tegra_slink_data *tspi = spi_master_get_devdata(master);
+
+ return pm_runtime_get_sync(tspi->dev);
+}
+
+static int tegra_slink_unprepare_transfer(struct spi_master *master)
+{
+ struct tegra_slink_data *tspi = spi_master_get_devdata(master);
+
+ pm_runtime_put(tspi->dev);
+ return 0;
+}
+
+static int tegra_slink_transfer_one_message(struct spi_master *master,
+ struct spi_message *msg)
+{
+ bool is_first_msg = true;
+ int single_xfer;
+ struct tegra_slink_data *tspi = spi_master_get_devdata(master);
+ struct spi_transfer *xfer;
+ struct spi_device *spi = msg->spi;
+ int ret;
+
+ msg->status = 0;
+ msg->actual_length = 0;
+ single_xfer = list_is_singular(&msg->transfers);
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ INIT_COMPLETION(tspi->xfer_completion);
+ ret = tegra_slink_start_transfer_one(spi, xfer,
+ is_first_msg, single_xfer);
+ if (ret < 0) {
+ dev_err(tspi->dev,
+ "spi can not start transfer, err %d\n", ret);
+ goto exit;
+ }
+ is_first_msg = false;
+ ret = wait_for_completion_timeout(&tspi->xfer_completion,
+ SLINK_DMA_TIMEOUT);
+ if (WARN_ON(ret == 0)) {
+ dev_err(tspi->dev,
+ "spi trasfer timeout, err %d\n", ret);
+ ret = -EIO;
+ goto exit;
+ }
+
+ if (tspi->tx_status || tspi->rx_status) {
+ dev_err(tspi->dev, "Error in Transfer\n");
+ ret = -EIO;
+ goto exit;
+ }
+ msg->actual_length += xfer->len;
+ if (xfer->cs_change && xfer->delay_usecs) {
+ tegra_slink_writel(tspi, tspi->def_command_reg,
+ SLINK_COMMAND);
+ udelay(xfer->delay_usecs);
+ }
+ }
+ ret = 0;
+exit:
+ tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND);
+ tegra_slink_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2);
+ msg->status = ret;
+ spi_finalize_current_message(master);
+ return ret;
+}
+
+static irqreturn_t handle_cpu_based_xfer(struct tegra_slink_data *tspi)
+{
+ struct spi_transfer *t = tspi->curr_xfer;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tspi->lock, flags);
+ if (tspi->tx_status || tspi->rx_status ||
+ (tspi->status_reg & SLINK_BSY)) {
+ dev_err(tspi->dev,
+ "CpuXfer ERROR bit set 0x%x\n", tspi->status_reg);
+ dev_err(tspi->dev,
+ "CpuXfer 0x%08x:0x%08x:0x%08x\n", tspi->command_reg,
+ tspi->command2_reg, tspi->dma_control_reg);
+ tegra_periph_reset_assert(tspi->clk);
+ udelay(2);
+ tegra_periph_reset_deassert(tspi->clk);
+ complete(&tspi->xfer_completion);
+ goto exit;
+ }
+
+ if (tspi->cur_direction & DATA_DIR_RX)
+ tegra_slink_read_rx_fifo_to_client_rxbuf(tspi, t);
+
+ if (tspi->cur_direction & DATA_DIR_TX)
+ tspi->cur_pos = tspi->cur_tx_pos;
+ else
+ tspi->cur_pos = tspi->cur_rx_pos;
+
+ if (tspi->cur_pos == t->len) {
+ complete(&tspi->xfer_completion);
+ goto exit;
+ }
+
+ tegra_slink_calculate_curr_xfer_param(tspi->cur_spi, tspi, t);
+ tegra_slink_start_cpu_based_transfer(tspi, t);
+exit:
+ spin_unlock_irqrestore(&tspi->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t handle_dma_based_xfer(struct tegra_slink_data *tspi)
+{
+ struct spi_transfer *t = tspi->curr_xfer;
+ long wait_status;
+ int err = 0;
+ unsigned total_fifo_words;
+ unsigned long flags;
+
+ /* Abort dmas if any error */
+ if (tspi->cur_direction & DATA_DIR_TX) {
+ if (tspi->tx_status) {
+ dmaengine_terminate_all(tspi->tx_dma_chan);
+ err += 1;
+ } else {
+ wait_status = wait_for_completion_interruptible_timeout(
+ &tspi->tx_dma_complete, SLINK_DMA_TIMEOUT);
+ if (wait_status <= 0) {
+ dmaengine_terminate_all(tspi->tx_dma_chan);
+ dev_err(tspi->dev, "TxDma Xfer failed\n");
+ err += 1;
+ }
+ }
+ }
+
+ if (tspi->cur_direction & DATA_DIR_RX) {
+ if (tspi->rx_status) {
+ dmaengine_terminate_all(tspi->rx_dma_chan);
+ err += 2;
+ } else {
+ wait_status = wait_for_completion_interruptible_timeout(
+ &tspi->rx_dma_complete, SLINK_DMA_TIMEOUT);
+ if (wait_status <= 0) {
+ dmaengine_terminate_all(tspi->rx_dma_chan);
+ dev_err(tspi->dev, "RxDma Xfer failed\n");
+ err += 2;
+ }
+ }
+ }
+
+ spin_lock_irqsave(&tspi->lock, flags);
+ if (err) {
+ dev_err(tspi->dev,
+ "DmaXfer: ERROR bit set 0x%x\n", tspi->status_reg);
+ dev_err(tspi->dev,
+ "DmaXfer 0x%08x:0x%08x:0x%08x\n", tspi->command_reg,
+ tspi->command2_reg, tspi->dma_control_reg);
+ tegra_periph_reset_assert(tspi->clk);
+ udelay(2);
+ tegra_periph_reset_deassert(tspi->clk);
+ complete(&tspi->xfer_completion);
+ spin_unlock_irqrestore(&tspi->lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ if (tspi->cur_direction & DATA_DIR_RX)
+ tegra_slink_copy_spi_rxbuf_to_client_rxbuf(tspi, t);
+
+ if (tspi->cur_direction & DATA_DIR_TX)
+ tspi->cur_pos = tspi->cur_tx_pos;
+ else
+ tspi->cur_pos = tspi->cur_rx_pos;
+
+ if (tspi->cur_pos == t->len) {
+ complete(&tspi->xfer_completion);
+ goto exit;
+ }
+
+ /* Continue transfer in current message */
+ total_fifo_words = tegra_slink_calculate_curr_xfer_param(tspi->cur_spi,
+ tspi, t);
+ if (total_fifo_words > SLINK_FIFO_DEPTH)
+ err = tegra_slink_start_dma_based_transfer(tspi, t);
+ else
+ err = tegra_slink_start_cpu_based_transfer(tspi, t);
+
+exit:
+ spin_unlock_irqrestore(&tspi->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t tegra_slink_isr_thread(int irq, void *context_data)
+{
+ struct tegra_slink_data *tspi = context_data;
+
+ if (!tspi->is_curr_dma_xfer)
+ return handle_cpu_based_xfer(tspi);
+ return handle_dma_based_xfer(tspi);
+}
+
+static irqreturn_t tegra_slink_isr(int irq, void *context_data)
+{
+ struct tegra_slink_data *tspi = context_data;
+
+ tspi->status_reg = tegra_slink_readl(tspi, SLINK_STATUS);
+ if (tspi->cur_direction & DATA_DIR_TX)
+ tspi->tx_status = tspi->status_reg &
+ (SLINK_TX_OVF | SLINK_TX_UNF);
+
+ if (tspi->cur_direction & DATA_DIR_RX)
+ tspi->rx_status = tspi->status_reg &
+ (SLINK_RX_OVF | SLINK_RX_UNF);
+ tegra_slink_clear_status(tspi);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static struct tegra_spi_platform_data *tegra_slink_parse_dt(
+ struct platform_device *pdev)
+{
+ struct tegra_spi_platform_data *pdata;
+ const unsigned int *prop;
+ struct device_node *np = pdev->dev.of_node;
+ u32 of_dma[2];
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&pdev->dev, "Memory alloc for pdata failed\n");
+ return NULL;
+ }
+
+ if (of_property_read_u32_array(np, "nvidia,dma-request-selector",
+ of_dma, 2) >= 0)
+ pdata->dma_req_sel = of_dma[1];
+
+ prop = of_get_property(np, "spi-max-frequency", NULL);
+ if (prop)
+ pdata->spi_max_frequency = be32_to_cpup(prop);
+
+ return pdata;
+}
+
+const struct tegra_slink_chip_data tegra30_spi_cdata = {
+ .cs_hold_time = true,
+};
+
+const struct tegra_slink_chip_data tegra20_spi_cdata = {
+ .cs_hold_time = false,
+};
+
+static struct of_device_id tegra_slink_of_match[] = {
+ { .compatible = "nvidia,tegra30-slink", .data = &tegra30_spi_cdata, },
+ { .compatible = "nvidia,tegra20-slink", .data = &tegra20_spi_cdata, },
+ {}
+};
+MODULE_DEVICE_TABLE(of, tegra_slink_of_match);
+
+static int tegra_slink_probe(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct tegra_slink_data *tspi;
+ struct resource *r;
+ struct tegra_spi_platform_data *pdata = pdev->dev.platform_data;
+ int ret, spi_irq;
+ const struct tegra_slink_chip_data *cdata = NULL;
+ const struct of_device_id *match;
+
+ match = of_match_device(of_match_ptr(tegra_slink_of_match), &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+ cdata = match->data;
+ if (!pdata && pdev->dev.of_node)
+ pdata = tegra_slink_parse_dt(pdev);
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data, exiting\n");
+ return -ENODEV;
+ }
+
+ if (!pdata->spi_max_frequency)
+ pdata->spi_max_frequency = 25000000; /* 25MHz */
+
+ master = spi_alloc_master(&pdev->dev, sizeof(*tspi));
+ if (!master) {
+ dev_err(&pdev->dev, "master allocation failed\n");
+ return -ENOMEM;
+ }
+
+ /* the spi->mode bits understood by this driver: */
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
+ master->setup = tegra_slink_setup;
+ master->prepare_transfer_hardware = tegra_slink_prepare_transfer;
+ master->transfer_one_message = tegra_slink_transfer_one_message;
+ master->unprepare_transfer_hardware = tegra_slink_unprepare_transfer;
+ master->num_chipselect = MAX_CHIP_SELECT;
+ master->bus_num = -1;
+
+ dev_set_drvdata(&pdev->dev, master);
+ tspi = spi_master_get_devdata(master);
+ tspi->master = master;
+ tspi->dma_req_sel = pdata->dma_req_sel;
+ tspi->dev = &pdev->dev;
+ tspi->chip_data = cdata;
+ spin_lock_init(&tspi->lock);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "No IO memory resource\n");
+ ret = -ENODEV;
+ goto exit_free_master;
+ }
+ tspi->phys = r->start;
+ tspi->base = devm_request_and_ioremap(&pdev->dev, r);
+ if (!tspi->base) {
+ dev_err(&pdev->dev,
+ "Cannot request memregion/iomap dma address\n");
+ ret = -EADDRNOTAVAIL;
+ goto exit_free_master;
+ }
+
+ spi_irq = platform_get_irq(pdev, 0);
+ tspi->irq = spi_irq;
+ ret = request_threaded_irq(tspi->irq, tegra_slink_isr,
+ tegra_slink_isr_thread, IRQF_ONESHOT,
+ dev_name(&pdev->dev), tspi);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n",
+ tspi->irq);
+ goto exit_free_master;
+ }
+
+ tspi->clk = devm_clk_get(&pdev->dev, "slink");
+ if (IS_ERR(tspi->clk)) {
+ dev_err(&pdev->dev, "can not get clock\n");
+ ret = PTR_ERR(tspi->clk);
+ goto exit_free_irq;
+ }
+
+ tspi->max_buf_size = SLINK_FIFO_DEPTH << 2;
+ tspi->dma_buf_size = DEFAULT_SPI_DMA_BUF_LEN;
+ tspi->spi_max_frequency = pdata->spi_max_frequency;
+
+ if (pdata->dma_req_sel) {
+ ret = tegra_slink_init_dma_param(tspi, true);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "RxDma Init failed, err %d\n", ret);
+ goto exit_free_irq;
+ }
+
+ ret = tegra_slink_init_dma_param(tspi, false);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "TxDma Init failed, err %d\n", ret);
+ goto exit_rx_dma_free;
+ }
+ tspi->max_buf_size = tspi->dma_buf_size;
+ init_completion(&tspi->tx_dma_complete);
+ init_completion(&tspi->rx_dma_complete);
+ }
+
+ init_completion(&tspi->xfer_completion);
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra_slink_runtime_resume(&pdev->dev);
+ if (ret)
+ goto exit_pm_disable;
+ }
+
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret);
+ goto exit_pm_disable;
+ }
+ tspi->def_command_reg = SLINK_M_S;
+ tspi->def_command2_reg = SLINK_CS_ACTIVE_BETWEEN;
+ tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND);
+ tegra_slink_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2);
+ pm_runtime_put(&pdev->dev);
+
+ master->dev.of_node = pdev->dev.of_node;
+ ret = spi_register_master(master);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "can not register to master err %d\n", ret);
+ goto exit_pm_disable;
+ }
+ return ret;
+
+exit_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra_slink_runtime_suspend(&pdev->dev);
+ tegra_slink_deinit_dma_param(tspi, false);
+exit_rx_dma_free:
+ tegra_slink_deinit_dma_param(tspi, true);
+exit_free_irq:
+ free_irq(spi_irq, tspi);
+exit_free_master:
+ spi_master_put(master);
+ return ret;
+}
+
+static int tegra_slink_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = dev_get_drvdata(&pdev->dev);
+ struct tegra_slink_data *tspi = spi_master_get_devdata(master);
+
+ free_irq(tspi->irq, tspi);
+ spi_unregister_master(master);
+
+ if (tspi->tx_dma_chan)
+ tegra_slink_deinit_dma_param(tspi, false);
+
+ if (tspi->rx_dma_chan)
+ tegra_slink_deinit_dma_param(tspi, true);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra_slink_runtime_suspend(&pdev->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tegra_slink_suspend(struct device *dev)
+{
+ struct spi_master *master = dev_get_drvdata(dev);
+
+ return spi_master_suspend(master);
+}
+
+static int tegra_slink_resume(struct device *dev)
+{
+ struct spi_master *master = dev_get_drvdata(dev);
+ struct tegra_slink_data *tspi = spi_master_get_devdata(master);
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "pm runtime failed, e = %d\n", ret);
+ return ret;
+ }
+ tegra_slink_writel(tspi, tspi->command_reg, SLINK_COMMAND);
+ tegra_slink_writel(tspi, tspi->command2_reg, SLINK_COMMAND2);
+ pm_runtime_put(dev);
+
+ return spi_master_resume(master);
+}
+#endif
+
+static int tegra_slink_runtime_suspend(struct device *dev)
+{
+ struct spi_master *master = dev_get_drvdata(dev);
+ struct tegra_slink_data *tspi = spi_master_get_devdata(master);
+
+ /* Flush all write which are in PPSB queue by reading back */
+ tegra_slink_readl(tspi, SLINK_MAS_DATA);
+
+ clk_disable_unprepare(tspi->clk);
+ return 0;
+}
+
+static int tegra_slink_runtime_resume(struct device *dev)
+{
+ struct spi_master *master = dev_get_drvdata(dev);
+ struct tegra_slink_data *tspi = spi_master_get_devdata(master);
+ int ret;
+
+ ret = clk_prepare_enable(tspi->clk);
+ if (ret < 0) {
+ dev_err(tspi->dev, "clk_prepare failed: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops slink_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra_slink_runtime_suspend,
+ tegra_slink_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(tegra_slink_suspend, tegra_slink_resume)
+};
+static struct platform_driver tegra_slink_driver = {
+ .driver = {
+ .name = "spi-tegra-slink",
+ .owner = THIS_MODULE,
+ .pm = &slink_pm_ops,
+ .of_match_table = of_match_ptr(tegra_slink_of_match),
+ },
+ .probe = tegra_slink_probe,
+ .remove = tegra_slink_remove,
+};
+module_platform_driver(tegra_slink_driver);
+
+MODULE_ALIAS("platform:spi-tegra-slink");
+MODULE_DESCRIPTION("NVIDIA Tegra20/Tegra30 SLINK Controller Driver");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/spi-ti-ssp.c b/drivers/spi/spi-ti-ssp.c
index 3f6f6e81c65..46992cab65f 100644
--- a/drivers/spi/spi-ti-ssp.c
+++ b/drivers/spi/spi-ti-ssp.c
@@ -289,7 +289,7 @@ error_unlock:
return error;
}
-static int __devinit ti_ssp_spi_probe(struct platform_device *pdev)
+static int ti_ssp_spi_probe(struct platform_device *pdev)
{
const struct ti_ssp_spi_data *pdata;
struct ti_ssp_spi *hw;
@@ -357,7 +357,7 @@ error_wq:
return error;
}
-static int __devexit ti_ssp_spi_remove(struct platform_device *pdev)
+static int ti_ssp_spi_remove(struct platform_device *pdev)
{
struct ti_ssp_spi *hw = platform_get_drvdata(pdev);
int error;
@@ -378,7 +378,7 @@ static int __devexit ti_ssp_spi_remove(struct platform_device *pdev)
static struct platform_driver ti_ssp_spi_driver = {
.probe = ti_ssp_spi_probe,
- .remove = __devexit_p(ti_ssp_spi_remove),
+ .remove = ti_ssp_spi_remove,
.driver = {
.name = "ti-ssp-spi",
.owner = THIS_MODULE,
diff --git a/drivers/spi/spi-tle62x0.c b/drivers/spi/spi-tle62x0.c
index 24421024dea..6b0874d782e 100644
--- a/drivers/spi/spi-tle62x0.c
+++ b/drivers/spi/spi-tle62x0.c
@@ -240,7 +240,7 @@ static int to_gpio_num(struct device_attribute *attr)
return -1;
}
-static int __devinit tle62x0_probe(struct spi_device *spi)
+static int tle62x0_probe(struct spi_device *spi)
{
struct tle62x0_state *st;
struct tle62x0_pdata *pdata;
@@ -294,7 +294,7 @@ static int __devinit tle62x0_probe(struct spi_device *spi)
return ret;
}
-static int __devexit tle62x0_remove(struct spi_device *spi)
+static int tle62x0_remove(struct spi_device *spi)
{
struct tle62x0_state *st = spi_get_drvdata(spi);
int ptr;
@@ -313,7 +313,7 @@ static struct spi_driver tle62x0_driver = {
.owner = THIS_MODULE,
},
.probe = tle62x0_probe,
- .remove = __devexit_p(tle62x0_remove),
+ .remove = tle62x0_remove,
};
module_spi_driver(tle62x0_driver);
diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c
index 135f7406f4b..f756481b0fe 100644
--- a/drivers/spi/spi-topcliff-pch.c
+++ b/drivers/spi/spi-topcliff-pch.c
@@ -1401,7 +1401,7 @@ static void pch_alloc_dma_buf(struct pch_spi_board_data *board_dat,
PCH_BUF_SIZE, &dma->rx_buf_dma, GFP_KERNEL);
}
-static int __devinit pch_spi_pd_probe(struct platform_device *plat_dev)
+static int pch_spi_pd_probe(struct platform_device *plat_dev)
{
int ret;
struct spi_master *master;
@@ -1498,7 +1498,7 @@ err_pci_iomap:
return ret;
}
-static int __devexit pch_spi_pd_remove(struct platform_device *plat_dev)
+static int pch_spi_pd_remove(struct platform_device *plat_dev)
{
struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev);
struct pch_spi_data *data = platform_get_drvdata(plat_dev);
@@ -1619,12 +1619,12 @@ static struct platform_driver pch_spi_pd_driver = {
.owner = THIS_MODULE,
},
.probe = pch_spi_pd_probe,
- .remove = __devexit_p(pch_spi_pd_remove),
+ .remove = pch_spi_pd_remove,
.suspend = pch_spi_pd_suspend,
.resume = pch_spi_pd_resume
};
-static int __devinit pch_spi_probe(struct pci_dev *pdev,
+static int pch_spi_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct pch_spi_board_data *board_dat;
@@ -1705,7 +1705,7 @@ err_no_mem:
return retval;
}
-static void __devexit pch_spi_remove(struct pci_dev *pdev)
+static void pch_spi_remove(struct pci_dev *pdev)
{
int i;
struct pch_pd_dev_save *pd_dev_save = pci_get_drvdata(pdev);
@@ -1776,7 +1776,7 @@ static struct pci_driver pch_spi_pcidev_driver = {
.name = "pch_spi",
.id_table = pch_spi_pcidev_id,
.probe = pch_spi_probe,
- .remove = __devexit_p(pch_spi_remove),
+ .remove = pch_spi_remove,
.suspend = pch_spi_suspend,
.resume = pch_spi_resume,
};
diff --git a/drivers/spi/spi-xcomm.c b/drivers/spi/spi-xcomm.c
index 266a847e299..4d3ec8b9f47 100644
--- a/drivers/spi/spi-xcomm.c
+++ b/drivers/spi/spi-xcomm.c
@@ -217,7 +217,7 @@ static int spi_xcomm_setup(struct spi_device *spi)
return 0;
}
-static int __devinit spi_xcomm_probe(struct i2c_client *i2c,
+static int spi_xcomm_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct spi_xcomm *spi_xcomm;
@@ -246,7 +246,7 @@ static int __devinit spi_xcomm_probe(struct i2c_client *i2c,
return ret;
}
-static int __devexit spi_xcomm_remove(struct i2c_client *i2c)
+static int spi_xcomm_remove(struct i2c_client *i2c)
{
struct spi_master *master = i2c_get_clientdata(i2c);
@@ -267,7 +267,7 @@ static struct i2c_driver spi_xcomm_driver = {
},
.id_table = spi_xcomm_ids,
.probe = spi_xcomm_probe,
- .remove = __devexit_p(spi_xcomm_remove),
+ .remove = spi_xcomm_remove,
};
module_i2c_driver(spi_xcomm_driver);
diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c
index 4c5a663b9fa..e1d76960742 100644
--- a/drivers/spi/spi-xilinx.c
+++ b/drivers/spi/spi-xilinx.c
@@ -462,7 +462,7 @@ void xilinx_spi_deinit(struct spi_master *master)
}
EXPORT_SYMBOL(xilinx_spi_deinit);
-static int __devinit xilinx_spi_probe(struct platform_device *dev)
+static int xilinx_spi_probe(struct platform_device *dev)
{
struct xspi_platform_data *pdata;
struct resource *r;
@@ -518,7 +518,7 @@ static int __devinit xilinx_spi_probe(struct platform_device *dev)
return 0;
}
-static int __devexit xilinx_spi_remove(struct platform_device *dev)
+static int xilinx_spi_remove(struct platform_device *dev)
{
xilinx_spi_deinit(platform_get_drvdata(dev));
platform_set_drvdata(dev, 0);
@@ -531,7 +531,7 @@ MODULE_ALIAS("platform:" XILINX_SPI_NAME);
static struct platform_driver xilinx_spi_driver = {
.probe = xilinx_spi_probe,
- .remove = __devexit_p(xilinx_spi_remove),
+ .remove = xilinx_spi_remove,
.driver = {
.name = XILINX_SPI_NAME,
.owner = THIS_MODULE,
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 718cc1f4923..ab095acdb2a 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -30,6 +30,7 @@
#include <linux/slab.h>
#include <linux/mod_devicetable.h>
#include <linux/spi/spi.h>
+#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/export.h>
#include <linux/sched.h>
@@ -333,6 +334,7 @@ struct spi_device *spi_alloc_device(struct spi_master *master)
spi->dev.parent = &master->dev;
spi->dev.bus = &spi_bus_type;
spi->dev.release = spidev_release;
+ spi->cs_gpio = -EINVAL;
device_initialize(&spi->dev);
return spi;
}
@@ -350,15 +352,16 @@ EXPORT_SYMBOL_GPL(spi_alloc_device);
int spi_add_device(struct spi_device *spi)
{
static DEFINE_MUTEX(spi_add_lock);
- struct device *dev = spi->master->dev.parent;
+ struct spi_master *master = spi->master;
+ struct device *dev = master->dev.parent;
struct device *d;
int status;
/* Chipselects are numbered 0..max; validate. */
- if (spi->chip_select >= spi->master->num_chipselect) {
+ if (spi->chip_select >= master->num_chipselect) {
dev_err(dev, "cs%d >= max %d\n",
spi->chip_select,
- spi->master->num_chipselect);
+ master->num_chipselect);
return -EINVAL;
}
@@ -382,6 +385,9 @@ int spi_add_device(struct spi_device *spi)
goto done;
}
+ if (master->cs_gpios)
+ spi->cs_gpio = master->cs_gpios[spi->chip_select];
+
/* Drivers may modify this initial i/o setup, but will
* normally rely on the device being setup. Devices
* using SPI_CS_HIGH can't coexist well otherwise...
@@ -492,8 +498,7 @@ static void spi_match_master_to_boardinfo(struct spi_master *master,
* The board info passed can safely be __initdata ... but be careful of
* any embedded pointers (platform_data, etc), they're copied as-is.
*/
-int __devinit
-spi_register_board_info(struct spi_board_info const *info, unsigned n)
+int spi_register_board_info(struct spi_board_info const *info, unsigned n)
{
struct boardinfo *bi;
int i;
@@ -806,7 +811,7 @@ err_init_queue:
/*-------------------------------------------------------------------------*/
-#if defined(CONFIG_OF) && !defined(CONFIG_SPARC)
+#if defined(CONFIG_OF)
/**
* of_register_spi_devices() - Register child devices onto the SPI bus
* @master: Pointer to spi_master device
@@ -861,6 +866,8 @@ static void of_register_spi_devices(struct spi_master *master)
spi->mode |= SPI_CPOL;
if (of_find_property(nc, "spi-cs-high", NULL))
spi->mode |= SPI_CS_HIGH;
+ if (of_find_property(nc, "spi-3wire", NULL))
+ spi->mode |= SPI_3WIRE;
/* Device speed */
prop = of_get_property(nc, "spi-max-frequency", &len);
@@ -1046,6 +1053,44 @@ struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
}
EXPORT_SYMBOL_GPL(spi_alloc_master);
+#ifdef CONFIG_OF
+static int of_spi_register_master(struct spi_master *master)
+{
+ u16 nb;
+ int i, *cs;
+ struct device_node *np = master->dev.of_node;
+
+ if (!np)
+ return 0;
+
+ nb = of_gpio_named_count(np, "cs-gpios");
+ master->num_chipselect = max(nb, master->num_chipselect);
+
+ if (nb < 1)
+ return 0;
+
+ cs = devm_kzalloc(&master->dev,
+ sizeof(int) * master->num_chipselect,
+ GFP_KERNEL);
+ master->cs_gpios = cs;
+
+ if (!master->cs_gpios)
+ return -ENOMEM;
+
+ memset(cs, -EINVAL, master->num_chipselect);
+
+ for (i = 0; i < nb; i++)
+ cs[i] = of_get_named_gpio(np, "cs-gpios", i);
+
+ return 0;
+}
+#else
+static int of_spi_register_master(struct spi_master *master)
+{
+ return 0;
+}
+#endif
+
/**
* spi_register_master - register SPI master controller
* @master: initialized master, originally from spi_alloc_master()
@@ -1077,6 +1122,10 @@ int spi_register_master(struct spi_master *master)
if (!dev)
return -ENODEV;
+ status = of_spi_register_master(master);
+ if (status)
+ return status;
+
/* even if it's just one always-selected device, there must
* be at least one chipselect
*/
@@ -1257,7 +1306,7 @@ EXPORT_SYMBOL_GPL(spi_busnum_to_master);
int spi_setup(struct spi_device *spi)
{
unsigned bad_bits;
- int status;
+ int status = 0;
/* help drivers fail *cleanly* when they need options
* that aren't supported with their current master
@@ -1272,7 +1321,8 @@ int spi_setup(struct spi_device *spi)
if (!spi->bits_per_word)
spi->bits_per_word = 8;
- status = spi->master->setup(spi);
+ if (spi->master->setup)
+ status = spi->master->setup(spi);
dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s"
"%u bits/w, %u Hz max --> %d\n",
@@ -1291,6 +1341,7 @@ EXPORT_SYMBOL_GPL(spi_setup);
static int __spi_async(struct spi_device *spi, struct spi_message *message)
{
struct spi_master *master = spi->master;
+ struct spi_transfer *xfer;
/* Half-duplex links include original MicroWire, and ones with
* only one data pin like SPI_3WIRE (switches direction) or where
@@ -1299,7 +1350,6 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
*/
if ((master->flags & SPI_MASTER_HALF_DUPLEX)
|| (spi->mode & SPI_3WIRE)) {
- struct spi_transfer *xfer;
unsigned flags = master->flags;
list_for_each_entry(xfer, &message->transfers, transfer_list) {
@@ -1312,6 +1362,15 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
}
}
+ /**
+ * Set transfer bits_per_word as spi device default if it is not
+ * set for this transfer.
+ */
+ list_for_each_entry(xfer, &message->transfers, transfer_list) {
+ if (!xfer->bits_per_word)
+ xfer->bits_per_word = spi->bits_per_word;
+ }
+
message->spi = spi;
message->status = -EINPROGRESS;
return master->transfer(spi, message);
@@ -1588,12 +1647,18 @@ int spi_write_then_read(struct spi_device *spi,
struct spi_transfer x[2];
u8 *local_buf;
- /* Use preallocated DMA-safe buffer. We can't avoid copying here,
- * (as a pure convenience thing), but we can keep heap costs
- * out of the hot path ...
+ /* Use preallocated DMA-safe buffer if we can. We can't avoid
+ * copying here, (as a pure convenience thing), but we can
+ * keep heap costs out of the hot path unless someone else is
+ * using the pre-allocated buffer or the transfer is too large.
*/
- if ((n_tx + n_rx) > SPI_BUFSIZ)
- return -EINVAL;
+ if ((n_tx + n_rx) > SPI_BUFSIZ || !mutex_trylock(&lock)) {
+ local_buf = kmalloc(max((unsigned)SPI_BUFSIZ, n_tx + n_rx), GFP_KERNEL);
+ if (!local_buf)
+ return -ENOMEM;
+ } else {
+ local_buf = buf;
+ }
spi_message_init(&message);
memset(x, 0, sizeof x);
@@ -1606,14 +1671,6 @@ int spi_write_then_read(struct spi_device *spi,
spi_message_add_tail(&x[1], &message);
}
- /* ... unless someone else is using the pre-allocated buffer */
- if (!mutex_trylock(&lock)) {
- local_buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
- if (!local_buf)
- return -ENOMEM;
- } else
- local_buf = buf;
-
memcpy(local_buf, txbuf, n_tx);
x[0].tx_buf = local_buf;
x[1].rx_buf = local_buf + n_tx;
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 830adbed1d7..2e0655dbe07 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -31,6 +31,8 @@
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
@@ -571,7 +573,7 @@ static struct class *spidev_class;
/*-------------------------------------------------------------------------*/
-static int __devinit spidev_probe(struct spi_device *spi)
+static int spidev_probe(struct spi_device *spi)
{
struct spidev_data *spidev;
int status;
@@ -620,7 +622,7 @@ static int __devinit spidev_probe(struct spi_device *spi)
return status;
}
-static int __devexit spidev_remove(struct spi_device *spi)
+static int spidev_remove(struct spi_device *spi)
{
struct spidev_data *spidev = spi_get_drvdata(spi);
@@ -642,13 +644,21 @@ static int __devexit spidev_remove(struct spi_device *spi)
return 0;
}
+static const struct of_device_id spidev_dt_ids[] = {
+ { .compatible = "rohm,dh2228fv" },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, spidev_dt_ids);
+
static struct spi_driver spidev_spi_driver = {
.driver = {
.name = "spidev",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(spidev_dt_ids),
},
.probe = spidev_probe,
- .remove = __devexit_p(spidev_remove),
+ .remove = spidev_remove,
/* NOTE: suspend/resume methods are not necessary here.
* We don't do anything except pass the requests to/from
diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig
index 42cdaa9a4d8..ff3c8a21f10 100644
--- a/drivers/ssb/Kconfig
+++ b/drivers/ssb/Kconfig
@@ -160,4 +160,13 @@ config SSB_DRIVER_GIGE
If unsure, say N
+config SSB_DRIVER_GPIO
+ bool "SSB GPIO driver"
+ depends on SSB
+ select GPIOLIB
+ help
+ Driver to provide access to the GPIO pins on the bus.
+
+ If unsure, say N
+
endmenu
diff --git a/drivers/ssb/Makefile b/drivers/ssb/Makefile
index 656e58b9261..9159ba77c38 100644
--- a/drivers/ssb/Makefile
+++ b/drivers/ssb/Makefile
@@ -15,6 +15,7 @@ ssb-$(CONFIG_SSB_DRIVER_MIPS) += driver_mipscore.o
ssb-$(CONFIG_SSB_DRIVER_EXTIF) += driver_extif.o
ssb-$(CONFIG_SSB_DRIVER_PCICORE) += driver_pcicore.o
ssb-$(CONFIG_SSB_DRIVER_GIGE) += driver_gige.o
+ssb-$(CONFIG_SSB_DRIVER_GPIO) += driver_gpio.o
# b43 pci-ssb-bridge driver
# Not strictly a part of SSB, but kept here for convenience
diff --git a/drivers/ssb/driver_chipcommon.c b/drivers/ssb/driver_chipcommon.c
index 95c33a05f43..71098a7b5fe 100644
--- a/drivers/ssb/driver_chipcommon.c
+++ b/drivers/ssb/driver_chipcommon.c
@@ -349,6 +349,9 @@ void ssb_chipcommon_init(struct ssb_chipcommon *cc)
{
if (!cc->dev)
return; /* We don't have a ChipCommon */
+
+ spin_lock_init(&cc->gpio_lock);
+
if (cc->dev->id.revision >= 11)
cc->status = chipco_read32(cc, SSB_CHIPCO_CHIPSTAT);
ssb_dprintk(KERN_INFO PFX "chipcommon status is 0x%x\n", cc->status);
@@ -505,28 +508,93 @@ u32 ssb_chipco_gpio_in(struct ssb_chipcommon *cc, u32 mask)
u32 ssb_chipco_gpio_out(struct ssb_chipcommon *cc, u32 mask, u32 value)
{
- return chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUT, mask, value);
+ unsigned long flags;
+ u32 res = 0;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUT, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
}
u32 ssb_chipco_gpio_outen(struct ssb_chipcommon *cc, u32 mask, u32 value)
{
- return chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUTEN, mask, value);
+ unsigned long flags;
+ u32 res = 0;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUTEN, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
}
u32 ssb_chipco_gpio_control(struct ssb_chipcommon *cc, u32 mask, u32 value)
{
- return chipco_write32_masked(cc, SSB_CHIPCO_GPIOCTL, mask, value);
+ unsigned long flags;
+ u32 res = 0;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOCTL, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
}
EXPORT_SYMBOL(ssb_chipco_gpio_control);
u32 ssb_chipco_gpio_intmask(struct ssb_chipcommon *cc, u32 mask, u32 value)
{
- return chipco_write32_masked(cc, SSB_CHIPCO_GPIOIRQ, mask, value);
+ unsigned long flags;
+ u32 res = 0;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOIRQ, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
}
u32 ssb_chipco_gpio_polarity(struct ssb_chipcommon *cc, u32 mask, u32 value)
{
- return chipco_write32_masked(cc, SSB_CHIPCO_GPIOPOL, mask, value);
+ unsigned long flags;
+ u32 res = 0;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOPOL, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
+}
+
+u32 ssb_chipco_gpio_pullup(struct ssb_chipcommon *cc, u32 mask, u32 value)
+{
+ unsigned long flags;
+ u32 res = 0;
+
+ if (cc->dev->id.revision < 20)
+ return 0xffffffff;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOPULLUP, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
+}
+
+u32 ssb_chipco_gpio_pulldown(struct ssb_chipcommon *cc, u32 mask, u32 value)
+{
+ unsigned long flags;
+ u32 res = 0;
+
+ if (cc->dev->id.revision < 20)
+ return 0xffffffff;
+
+ spin_lock_irqsave(&cc->gpio_lock, flags);
+ res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOPULLDOWN, mask, value);
+ spin_unlock_irqrestore(&cc->gpio_lock, flags);
+
+ return res;
}
#ifdef CONFIG_SSB_SERIAL
diff --git a/drivers/ssb/driver_extif.c b/drivers/ssb/driver_extif.c
index 553227a3062..59385fdab5b 100644
--- a/drivers/ssb/driver_extif.c
+++ b/drivers/ssb/driver_extif.c
@@ -138,6 +138,13 @@ u32 ssb_extif_watchdog_timer_set(struct ssb_extif *extif, u32 ticks)
return ticks;
}
+void ssb_extif_init(struct ssb_extif *extif)
+{
+ if (!extif->dev)
+ return; /* We don't have a Extif core */
+ spin_lock_init(&extif->gpio_lock);
+}
+
u32 ssb_extif_gpio_in(struct ssb_extif *extif, u32 mask)
{
return extif_read32(extif, SSB_EXTIF_GPIO_IN) & mask;
@@ -145,22 +152,50 @@ u32 ssb_extif_gpio_in(struct ssb_extif *extif, u32 mask)
u32 ssb_extif_gpio_out(struct ssb_extif *extif, u32 mask, u32 value)
{
- return extif_write32_masked(extif, SSB_EXTIF_GPIO_OUT(0),
+ unsigned long flags;
+ u32 res = 0;
+
+ spin_lock_irqsave(&extif->gpio_lock, flags);
+ res = extif_write32_masked(extif, SSB_EXTIF_GPIO_OUT(0),
mask, value);
+ spin_unlock_irqrestore(&extif->gpio_lock, flags);
+
+ return res;
}
u32 ssb_extif_gpio_outen(struct ssb_extif *extif, u32 mask, u32 value)
{
- return extif_write32_masked(extif, SSB_EXTIF_GPIO_OUTEN(0),
+ unsigned long flags;
+ u32 res = 0;
+
+ spin_lock_irqsave(&extif->gpio_lock, flags);
+ res = extif_write32_masked(extif, SSB_EXTIF_GPIO_OUTEN(0),
mask, value);
+ spin_unlock_irqrestore(&extif->gpio_lock, flags);
+
+ return res;
}
u32 ssb_extif_gpio_polarity(struct ssb_extif *extif, u32 mask, u32 value)
{
- return extif_write32_masked(extif, SSB_EXTIF_GPIO_INTPOL, mask, value);
+ unsigned long flags;
+ u32 res = 0;
+
+ spin_lock_irqsave(&extif->gpio_lock, flags);
+ res = extif_write32_masked(extif, SSB_EXTIF_GPIO_INTPOL, mask, value);
+ spin_unlock_irqrestore(&extif->gpio_lock, flags);
+
+ return res;
}
u32 ssb_extif_gpio_intmask(struct ssb_extif *extif, u32 mask, u32 value)
{
- return extif_write32_masked(extif, SSB_EXTIF_GPIO_INTMASK, mask, value);
+ unsigned long flags;
+ u32 res = 0;
+
+ spin_lock_irqsave(&extif->gpio_lock, flags);
+ res = extif_write32_masked(extif, SSB_EXTIF_GPIO_INTMASK, mask, value);
+ spin_unlock_irqrestore(&extif->gpio_lock, flags);
+
+ return res;
}
diff --git a/drivers/ssb/driver_gpio.c b/drivers/ssb/driver_gpio.c
new file mode 100644
index 00000000000..97ac0a38e3d
--- /dev/null
+++ b/drivers/ssb/driver_gpio.c
@@ -0,0 +1,176 @@
+/*
+ * Sonics Silicon Backplane
+ * GPIO driver
+ *
+ * Copyright 2011, Broadcom Corporation
+ * Copyright 2012, Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/gpio.h>
+#include <linux/export.h>
+#include <linux/ssb/ssb.h>
+
+#include "ssb_private.h"
+
+static struct ssb_bus *ssb_gpio_get_bus(struct gpio_chip *chip)
+{
+ return container_of(chip, struct ssb_bus, gpio);
+}
+
+static int ssb_gpio_chipco_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ return !!ssb_chipco_gpio_in(&bus->chipco, 1 << gpio);
+}
+
+static void ssb_gpio_chipco_set_value(struct gpio_chip *chip, unsigned gpio,
+ int value)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_chipco_gpio_out(&bus->chipco, 1 << gpio, value ? 1 << gpio : 0);
+}
+
+static int ssb_gpio_chipco_direction_input(struct gpio_chip *chip,
+ unsigned gpio)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_chipco_gpio_outen(&bus->chipco, 1 << gpio, 0);
+ return 0;
+}
+
+static int ssb_gpio_chipco_direction_output(struct gpio_chip *chip,
+ unsigned gpio, int value)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_chipco_gpio_outen(&bus->chipco, 1 << gpio, 1 << gpio);
+ ssb_chipco_gpio_out(&bus->chipco, 1 << gpio, value ? 1 << gpio : 0);
+ return 0;
+}
+
+static int ssb_gpio_chipco_request(struct gpio_chip *chip, unsigned gpio)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_chipco_gpio_control(&bus->chipco, 1 << gpio, 0);
+ /* clear pulldown */
+ ssb_chipco_gpio_pulldown(&bus->chipco, 1 << gpio, 0);
+ /* Set pullup */
+ ssb_chipco_gpio_pullup(&bus->chipco, 1 << gpio, 1 << gpio);
+
+ return 0;
+}
+
+static void ssb_gpio_chipco_free(struct gpio_chip *chip, unsigned gpio)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ /* clear pullup */
+ ssb_chipco_gpio_pullup(&bus->chipco, 1 << gpio, 0);
+}
+
+static int ssb_gpio_chipco_init(struct ssb_bus *bus)
+{
+ struct gpio_chip *chip = &bus->gpio;
+
+ chip->label = "ssb_chipco_gpio";
+ chip->owner = THIS_MODULE;
+ chip->request = ssb_gpio_chipco_request;
+ chip->free = ssb_gpio_chipco_free;
+ chip->get = ssb_gpio_chipco_get_value;
+ chip->set = ssb_gpio_chipco_set_value;
+ chip->direction_input = ssb_gpio_chipco_direction_input;
+ chip->direction_output = ssb_gpio_chipco_direction_output;
+ chip->ngpio = 16;
+ /* There is just one SoC in one device and its GPIO addresses should be
+ * deterministic to address them more easily. The other buses could get
+ * a random base number. */
+ if (bus->bustype == SSB_BUSTYPE_SSB)
+ chip->base = 0;
+ else
+ chip->base = -1;
+
+ return gpiochip_add(chip);
+}
+
+#ifdef CONFIG_SSB_DRIVER_EXTIF
+
+static int ssb_gpio_extif_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ return !!ssb_extif_gpio_in(&bus->extif, 1 << gpio);
+}
+
+static void ssb_gpio_extif_set_value(struct gpio_chip *chip, unsigned gpio,
+ int value)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_extif_gpio_out(&bus->extif, 1 << gpio, value ? 1 << gpio : 0);
+}
+
+static int ssb_gpio_extif_direction_input(struct gpio_chip *chip,
+ unsigned gpio)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_extif_gpio_outen(&bus->extif, 1 << gpio, 0);
+ return 0;
+}
+
+static int ssb_gpio_extif_direction_output(struct gpio_chip *chip,
+ unsigned gpio, int value)
+{
+ struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+ ssb_extif_gpio_outen(&bus->extif, 1 << gpio, 1 << gpio);
+ ssb_extif_gpio_out(&bus->extif, 1 << gpio, value ? 1 << gpio : 0);
+ return 0;
+}
+
+static int ssb_gpio_extif_init(struct ssb_bus *bus)
+{
+ struct gpio_chip *chip = &bus->gpio;
+
+ chip->label = "ssb_extif_gpio";
+ chip->owner = THIS_MODULE;
+ chip->get = ssb_gpio_extif_get_value;
+ chip->set = ssb_gpio_extif_set_value;
+ chip->direction_input = ssb_gpio_extif_direction_input;
+ chip->direction_output = ssb_gpio_extif_direction_output;
+ chip->ngpio = 5;
+ /* There is just one SoC in one device and its GPIO addresses should be
+ * deterministic to address them more easily. The other buses could get
+ * a random base number. */
+ if (bus->bustype == SSB_BUSTYPE_SSB)
+ chip->base = 0;
+ else
+ chip->base = -1;
+
+ return gpiochip_add(chip);
+}
+
+#else
+static int ssb_gpio_extif_init(struct ssb_bus *bus)
+{
+ return -ENOTSUPP;
+}
+#endif
+
+int ssb_gpio_init(struct ssb_bus *bus)
+{
+ if (ssb_chipco_available(&bus->chipco))
+ return ssb_gpio_chipco_init(bus);
+ else if (ssb_extif_available(&bus->extif))
+ return ssb_gpio_extif_init(bus);
+ else
+ SSB_WARN_ON(1);
+
+ return -1;
+}
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c
index 6e0daaa0e04..c82c5c95fe8 100644
--- a/drivers/ssb/main.c
+++ b/drivers/ssb/main.c
@@ -804,7 +804,14 @@ static int __devinit ssb_bus_register(struct ssb_bus *bus,
if (err)
goto err_pcmcia_exit;
ssb_chipcommon_init(&bus->chipco);
+ ssb_extif_init(&bus->extif);
ssb_mipscore_init(&bus->mipscore);
+ err = ssb_gpio_init(bus);
+ if (err == -ENOTSUPP)
+ ssb_dprintk(KERN_DEBUG PFX "GPIO driver not activated\n");
+ else if (err)
+ ssb_dprintk(KERN_ERR PFX
+ "Error registering GPIO driver: %i\n", err);
err = ssb_fetch_invariants(bus, get_invariants);
if (err) {
ssb_bus_may_powerdown(bus);
diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h
index 8942db1d855..6c10b66c796 100644
--- a/drivers/ssb/ssb_private.h
+++ b/drivers/ssb/ssb_private.h
@@ -242,4 +242,21 @@ static inline int ssb_watchdog_register(struct ssb_bus *bus)
}
#endif /* CONFIG_SSB_EMBEDDED */
+#ifdef CONFIG_SSB_DRIVER_EXTIF
+extern void ssb_extif_init(struct ssb_extif *extif);
+#else
+static inline void ssb_extif_init(struct ssb_extif *extif)
+{
+}
+#endif
+
+#ifdef CONFIG_SSB_DRIVER_GPIO
+extern int ssb_gpio_init(struct ssb_bus *bus);
+#else /* CONFIG_SSB_DRIVER_GPIO */
+static inline int ssb_gpio_init(struct ssb_bus *bus)
+{
+ return -ENOTSUPP;
+}
+#endif /* CONFIG_SSB_DRIVER_GPIO */
+
#endif /* LINUX_SSB_PRIVATE_H_ */
diff --git a/drivers/staging/comedi/drivers/usbdux.c b/drivers/staging/comedi/drivers/usbdux.c
index 78f3a2e013c..17b45ebb055 100644
--- a/drivers/staging/comedi/drivers/usbdux.c
+++ b/drivers/staging/comedi/drivers/usbdux.c
@@ -73,7 +73,7 @@ sampling rate. If you sample two channels you get 4kHz and so on.
* And loads of cleaning up, in particular streamlining the
* bulk transfers.
* 1.1: moved EP4 transfers to EP1 to make space for a PWM output on EP4
- * 1.2: added PWM suport via EP4
+ * 1.2: added PWM support via EP4
* 2.0: PWM seems to be stable and is not interfering with the other functions
* 2.1: changed PWM API
* 2.2: added firmware kernel request to fix an udev problem
diff --git a/drivers/staging/media/dt3155v4l/dt3155v4l.c b/drivers/staging/media/dt3155v4l/dt3155v4l.c
index 238910373f5..479c643da2f 100644
--- a/drivers/staging/media/dt3155v4l/dt3155v4l.c
+++ b/drivers/staging/media/dt3155v4l/dt3155v4l.c
@@ -396,7 +396,9 @@ dt3155_open(struct file *filp)
pd->q->drv_priv = pd;
pd->curr_buf = NULL;
pd->field_count = 0;
- vb2_queue_init(pd->q); /* cannot fail */
+ ret = vb2_queue_init(pd->q);
+ if (ret < 0)
+ return ret;
INIT_LIST_HEAD(&pd->dmaq);
spin_lock_init(&pd->lock);
/* disable all irqs, clear all irq flags */
diff --git a/drivers/staging/media/go7007/go7007-fw.c b/drivers/staging/media/go7007/go7007-fw.c
index c9a6409edfe..f99c05b454b 100644
--- a/drivers/staging/media/go7007/go7007-fw.c
+++ b/drivers/staging/media/go7007/go7007-fw.c
@@ -382,8 +382,8 @@ static int gen_mjpeghdr_to_package(struct go7007 *go, __le16 *code, int space)
buf = kzalloc(4096, GFP_KERNEL);
if (buf == NULL) {
- printk(KERN_ERR "go7007: unable to allocate 4096 bytes for "
- "firmware construction\n");
+ dev_err(go->dev,
+ "unable to allocate 4096 bytes for firmware construction\n");
return -1;
}
@@ -652,8 +652,8 @@ static int gen_mpeg1hdr_to_package(struct go7007 *go,
buf = kzalloc(5120, GFP_KERNEL);
if (buf == NULL) {
- printk(KERN_ERR "go7007: unable to allocate 5120 bytes for "
- "firmware construction\n");
+ dev_err(go->dev,
+ "unable to allocate 5120 bytes for firmware construction\n");
return -1;
}
framelen[0] = mpeg1_frame_header(go, buf, 0, 1, PFRAME);
@@ -839,8 +839,8 @@ static int gen_mpeg4hdr_to_package(struct go7007 *go,
buf = kzalloc(5120, GFP_KERNEL);
if (buf == NULL) {
- printk(KERN_ERR "go7007: unable to allocate 5120 bytes for "
- "firmware construction\n");
+ dev_err(go->dev,
+ "unable to allocate 5120 bytes for firmware construction\n");
return -1;
}
framelen[0] = mpeg4_frame_header(go, buf, 0, PFRAME);
@@ -1545,9 +1545,8 @@ static int do_special(struct go7007 *go, u16 type, __le16 *code, int space,
case SPECIAL_MODET:
return modet_to_package(go, code, space);
}
- printk(KERN_ERR
- "go7007: firmware file contains unsupported feature %04x\n",
- type);
+ dev_err(go->dev,
+ "firmware file contains unsupported feature %04x\n", type);
return -1;
}
@@ -1577,15 +1576,16 @@ int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen)
return -1;
}
if (request_firmware(&fw_entry, go->board_info->firmware, go->dev)) {
- printk(KERN_ERR
- "go7007: unable to load firmware from file \"%s\"\n",
+ dev_err(go->dev,
+ "unable to load firmware from file \"%s\"\n",
go->board_info->firmware);
return -1;
}
code = kzalloc(codespace * 2, GFP_KERNEL);
if (code == NULL) {
- printk(KERN_ERR "go7007: unable to allocate %d bytes for "
- "firmware construction\n", codespace * 2);
+ dev_err(go->dev,
+ "unable to allocate %d bytes for firmware construction\n",
+ codespace * 2);
goto fw_failed;
}
src = (__le16 *)fw_entry->data;
@@ -1594,9 +1594,9 @@ int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen)
chunk_flags = __le16_to_cpu(src[0]);
chunk_len = __le16_to_cpu(src[1]);
if (chunk_len + 2 > srclen) {
- printk(KERN_ERR "go7007: firmware file \"%s\" "
- "appears to be corrupted\n",
- go->board_info->firmware);
+ dev_err(go->dev,
+ "firmware file \"%s\" appears to be corrupted\n",
+ go->board_info->firmware);
goto fw_failed;
}
if (chunk_flags & mode_flag) {
@@ -1604,17 +1604,15 @@ int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen)
ret = do_special(go, __le16_to_cpu(src[2]),
&code[i], codespace - i, framelen);
if (ret < 0) {
- printk(KERN_ERR "go7007: insufficient "
- "memory for firmware "
- "construction\n");
+ dev_err(go->dev,
+ "insufficient memory for firmware construction\n");
goto fw_failed;
}
i += ret;
} else {
if (codespace - i < chunk_len) {
- printk(KERN_ERR "go7007: insufficient "
- "memory for firmware "
- "construction\n");
+ dev_err(go->dev,
+ "insufficient memory for firmware construction\n");
goto fw_failed;
}
memcpy(&code[i], &src[2], chunk_len * 2);
diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c
index 980371b0274..a78133b67de 100644
--- a/drivers/staging/media/go7007/go7007-v4l2.c
+++ b/drivers/staging/media/go7007/go7007-v4l2.c
@@ -812,7 +812,7 @@ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
return retval;
mutex_lock(&gofh->lock);
- if (buf->index < 0 || buf->index >= gofh->buf_count)
+ if (buf->index >= gofh->buf_count)
goto unlock_and_return;
gobuf = &gofh->bufs[buf->index];
diff --git a/drivers/staging/media/go7007/s2250-board.c b/drivers/staging/media/go7007/s2250-board.c
index 014d38410c9..b3974100c6c 100644
--- a/drivers/staging/media/go7007/s2250-board.c
+++ b/drivers/staging/media/go7007/s2250-board.c
@@ -688,15 +688,4 @@ static struct i2c_driver s2250_driver = {
.id_table = s2250_id,
};
-static __init int init_s2250(void)
-{
- return i2c_add_driver(&s2250_driver);
-}
-
-static __exit void exit_s2250(void)
-{
- i2c_del_driver(&s2250_driver);
-}
-
-module_init(init_s2250);
-module_exit(exit_s2250);
+module_i2c_driver(s2250_driver);
diff --git a/drivers/staging/media/go7007/wis-ov7640.c b/drivers/staging/media/go7007/wis-ov7640.c
index 6bc9470fecb..9f01657f884 100644
--- a/drivers/staging/media/go7007/wis-ov7640.c
+++ b/drivers/staging/media/go7007/wis-ov7640.c
@@ -29,8 +29,7 @@ struct wis_ov7640 {
int hue;
};
-static u8 initial_registers[] =
-{
+static u8 initial_registers[] = {
0x12, 0x80,
0x12, 0x54,
0x14, 0x24,
@@ -60,12 +59,12 @@ static int wis_ov7640_probe(struct i2c_client *client,
client->flags = I2C_CLIENT_SCCB;
- printk(KERN_DEBUG
+ dev_dbg(&client->dev,
"wis-ov7640: initializing OV7640 at address %d on %s\n",
client->addr, adapter->name);
if (write_regs(client, initial_registers) < 0) {
- printk(KERN_ERR "wis-ov7640: error initializing OV7640\n");
+ dev_err(&client->dev, "wis-ov7640: error initializing OV7640\n");
return -ENODEV;
}
@@ -92,17 +91,6 @@ static struct i2c_driver wis_ov7640_driver = {
.id_table = wis_ov7640_id,
};
-static int __init wis_ov7640_init(void)
-{
- return i2c_add_driver(&wis_ov7640_driver);
-}
-
-static void __exit wis_ov7640_cleanup(void)
-{
- i2c_del_driver(&wis_ov7640_driver);
-}
-
-module_init(wis_ov7640_init);
-module_exit(wis_ov7640_cleanup);
+module_i2c_driver(wis_ov7640_driver);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/media/go7007/wis-saa7113.c b/drivers/staging/media/go7007/wis-saa7113.c
index 05e0e108386..8810c1e6e1e 100644
--- a/drivers/staging/media/go7007/wis-saa7113.c
+++ b/drivers/staging/media/go7007/wis-saa7113.c
@@ -32,8 +32,7 @@ struct wis_saa7113 {
int hue;
};
-static u8 initial_registers[] =
-{
+static u8 initial_registers[] = {
0x01, 0x08,
0x02, 0xc0,
0x03, 0x33,
@@ -282,12 +281,12 @@ static int wis_saa7113_probe(struct i2c_client *client,
dec->hue = 0;
i2c_set_clientdata(client, dec);
- printk(KERN_DEBUG
+ dev_dbg(&client->dev,
"wis-saa7113: initializing SAA7113 at address %d on %s\n",
client->addr, adapter->name);
if (write_regs(client, initial_registers) < 0) {
- printk(KERN_ERR
+ dev_err(&client->dev,
"wis-saa7113: error initializing SAA7113\n");
kfree(dec);
return -ENODEV;
@@ -320,17 +319,6 @@ static struct i2c_driver wis_saa7113_driver = {
.id_table = wis_saa7113_id,
};
-static int __init wis_saa7113_init(void)
-{
- return i2c_add_driver(&wis_saa7113_driver);
-}
-
-static void __exit wis_saa7113_cleanup(void)
-{
- i2c_del_driver(&wis_saa7113_driver);
-}
-
-module_init(wis_saa7113_init);
-module_exit(wis_saa7113_cleanup);
+module_i2c_driver(wis_saa7113_driver);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/media/go7007/wis-saa7115.c b/drivers/staging/media/go7007/wis-saa7115.c
index 46cff59e28b..fa86acd3fdf 100644
--- a/drivers/staging/media/go7007/wis-saa7115.c
+++ b/drivers/staging/media/go7007/wis-saa7115.c
@@ -32,8 +32,7 @@ struct wis_saa7115 {
int hue;
};
-static u8 initial_registers[] =
-{
+static u8 initial_registers[] = {
0x01, 0x08,
0x02, 0xc0,
0x03, 0x20,
@@ -415,12 +414,12 @@ static int wis_saa7115_probe(struct i2c_client *client,
dec->hue = 0;
i2c_set_clientdata(client, dec);
- printk(KERN_DEBUG
+ dev_dbg(&client->dev,
"wis-saa7115: initializing SAA7115 at address %d on %s\n",
client->addr, adapter->name);
if (write_regs(client, initial_registers) < 0) {
- printk(KERN_ERR
+ dev_err(&client->dev,
"wis-saa7115: error initializing SAA7115\n");
kfree(dec);
return -ENODEV;
@@ -453,17 +452,6 @@ static struct i2c_driver wis_saa7115_driver = {
.id_table = wis_saa7115_id,
};
-static int __init wis_saa7115_init(void)
-{
- return i2c_add_driver(&wis_saa7115_driver);
-}
-
-static void __exit wis_saa7115_cleanup(void)
-{
- i2c_del_driver(&wis_saa7115_driver);
-}
-
-module_init(wis_saa7115_init);
-module_exit(wis_saa7115_cleanup);
+module_i2c_driver(wis_saa7115_driver);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/media/go7007/wis-sony-tuner.c b/drivers/staging/media/go7007/wis-sony-tuner.c
index 8f1b7d4f6a2..1291ab79d2a 100644
--- a/drivers/staging/media/go7007/wis-sony-tuner.c
+++ b/drivers/staging/media/go7007/wis-sony-tuner.c
@@ -704,17 +704,6 @@ static struct i2c_driver wis_sony_tuner_driver = {
.id_table = wis_sony_tuner_id,
};
-static int __init wis_sony_tuner_init(void)
-{
- return i2c_add_driver(&wis_sony_tuner_driver);
-}
-
-static void __exit wis_sony_tuner_cleanup(void)
-{
- i2c_del_driver(&wis_sony_tuner_driver);
-}
-
-module_init(wis_sony_tuner_init);
-module_exit(wis_sony_tuner_cleanup);
+module_i2c_driver(wis_sony_tuner_driver);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/media/go7007/wis-tw2804.c b/drivers/staging/media/go7007/wis-tw2804.c
index 9134f03e3cf..d6410ee01be 100644
--- a/drivers/staging/media/go7007/wis-tw2804.c
+++ b/drivers/staging/media/go7007/wis-tw2804.c
@@ -341,17 +341,6 @@ static struct i2c_driver wis_tw2804_driver = {
.id_table = wis_tw2804_id,
};
-static int __init wis_tw2804_init(void)
-{
- return i2c_add_driver(&wis_tw2804_driver);
-}
-
-static void __exit wis_tw2804_cleanup(void)
-{
- i2c_del_driver(&wis_tw2804_driver);
-}
-
-module_init(wis_tw2804_init);
-module_exit(wis_tw2804_cleanup);
+module_i2c_driver(wis_tw2804_driver);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/media/go7007/wis-tw9903.c b/drivers/staging/media/go7007/wis-tw9903.c
index 9230f4a8052..94071def3bb 100644
--- a/drivers/staging/media/go7007/wis-tw9903.c
+++ b/drivers/staging/media/go7007/wis-tw9903.c
@@ -325,17 +325,6 @@ static struct i2c_driver wis_tw9903_driver = {
.id_table = wis_tw9903_id,
};
-static int __init wis_tw9903_init(void)
-{
- return i2c_add_driver(&wis_tw9903_driver);
-}
-
-static void __exit wis_tw9903_cleanup(void)
-{
- i2c_del_driver(&wis_tw9903_driver);
-}
-
-module_init(wis_tw9903_init);
-module_exit(wis_tw9903_cleanup);
+module_i2c_driver(wis_tw9903_driver);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/media/go7007/wis-uda1342.c b/drivers/staging/media/go7007/wis-uda1342.c
index 0127be2f3be..05ac798f35f 100644
--- a/drivers/staging/media/go7007/wis-uda1342.c
+++ b/drivers/staging/media/go7007/wis-uda1342.c
@@ -98,17 +98,6 @@ static struct i2c_driver wis_uda1342_driver = {
.id_table = wis_uda1342_id,
};
-static int __init wis_uda1342_init(void)
-{
- return i2c_add_driver(&wis_uda1342_driver);
-}
-
-static void __exit wis_uda1342_cleanup(void)
-{
- i2c_del_driver(&wis_uda1342_driver);
-}
-
-module_init(wis_uda1342_init);
-module_exit(wis_uda1342_cleanup);
+module_i2c_driver(wis_uda1342_driver);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/media/lirc/lirc_serial.c b/drivers/staging/media/lirc/lirc_serial.c
index 71e3bf2937f..b5d0088f310 100644
--- a/drivers/staging/media/lirc/lirc_serial.c
+++ b/drivers/staging/media/lirc/lirc_serial.c
@@ -1239,6 +1239,10 @@ static int __init lirc_serial_init_module(void)
}
}
+ /* make sure sense is either -1, 0, or 1 */
+ if (sense != -1)
+ sense = !!sense;
+
result = lirc_serial_init();
if (result)
return result;
@@ -1298,7 +1302,7 @@ MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
module_param(share_irq, bool, S_IRUGO);
MODULE_PARM_DESC(share_irq, "Share interrupts (0 = off, 1 = on)");
-module_param(sense, bool, S_IRUGO);
+module_param(sense, int, S_IRUGO);
MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit"
" (0 = active high, 1 = active low )");
diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/staging/omapdrm/omap_drv.c
index d4823fd6776..84943e5ba1d 100644
--- a/drivers/staging/omapdrm/omap_drv.c
+++ b/drivers/staging/omapdrm/omap_drv.c
@@ -565,6 +565,14 @@ static int dev_load(struct drm_device *dev, unsigned long flags)
dev->dev_private = priv;
+ ret = omapdss_compat_init();
+ if (ret) {
+ dev_err(dev->dev, "coult not init omapdss\n");
+ dev->dev_private = NULL;
+ kfree(priv);
+ return ret;
+ }
+
priv->wq = alloc_ordered_workqueue("omapdrm", 0);
INIT_LIST_HEAD(&priv->obj_list);
@@ -576,6 +584,7 @@ static int dev_load(struct drm_device *dev, unsigned long flags)
dev_err(dev->dev, "omap_modeset_init failed: ret=%d\n", ret);
dev->dev_private = NULL;
kfree(priv);
+ omapdss_compat_uninit();
return ret;
}
@@ -610,6 +619,8 @@ static int dev_unload(struct drm_device *dev)
flush_workqueue(priv->wq);
destroy_workqueue(priv->wq);
+ omapdss_compat_uninit();
+
kfree(dev->dev_private);
dev->dev_private = NULL;
diff --git a/drivers/staging/tidspbridge/rmgr/node.c b/drivers/staging/tidspbridge/rmgr/node.c
index 294e9b40f51..737f4a9d86a 100644
--- a/drivers/staging/tidspbridge/rmgr/node.c
+++ b/drivers/staging/tidspbridge/rmgr/node.c
@@ -736,7 +736,7 @@ DBAPI node_alloc_msg_buf(struct node_object *hnode, u32 usize,
case 4:
break;
default:
- /* alignment value not suportted */
+ /* alignment value not supportted */
status = -EPERM;
break;
}
diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c
index ff6fd4fb624..0f03b7919d7 100644
--- a/drivers/target/iscsi/iscsi_target_configfs.c
+++ b/drivers/target/iscsi/iscsi_target_configfs.c
@@ -235,7 +235,7 @@ static struct se_tpg_np *lio_target_call_addnptotpg(
* iSER/SCTP (TODO, software emulation with osc-iwarp)
* iSER/IB (TODO, hardware available)
*
- * can be enabled with atributes under
+ * can be enabled with attributes under
* sys/kernel/config/iscsi/$IQN/$TPG/np/$IP:$PORT/
*
*/
diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c
index 8aacf611b86..8e6298cc883 100644
--- a/drivers/target/iscsi/iscsi_target_erl0.c
+++ b/drivers/target/iscsi/iscsi_target_erl0.c
@@ -410,11 +410,11 @@ static int iscsit_dataout_pre_datapduinorder_yes(
/*
* For DataSequenceInOrder=Yes: If the offset is greater than the global
* DataPDUInOrder=Yes offset counter in struct iscsi_cmd a protcol error has
- * occured and fail the connection.
+ * occurred and fail the connection.
*
* For DataSequenceInOrder=No: If the offset is greater than the per
* sequence DataPDUInOrder=Yes offset counter in struct iscsi_seq a protocol
- * error has occured and fail the connection.
+ * error has occurred and fail the connection.
*/
if (conn->sess->sess_ops->DataSequenceInOrder) {
if (be32_to_cpu(hdr->offset) != cmd->write_data_done) {
@@ -801,7 +801,7 @@ void iscsit_start_time2retain_handler(struct iscsi_session *sess)
{
int tpg_active;
/*
- * Only start Time2Retain timer when the assoicated TPG is still in
+ * Only start Time2Retain timer when the associated TPG is still in
* an ACTIVE (eg: not disabled or shutdown) state.
*/
spin_lock(&ISCSI_TPG_S(sess)->tpg_state_lock);
diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c
index 90b740048f2..1bf7432bfcb 100644
--- a/drivers/target/iscsi/iscsi_target_parameters.c
+++ b/drivers/target/iscsi/iscsi_target_parameters.c
@@ -1432,6 +1432,7 @@ static struct iscsi_param *iscsi_check_key(
break;
case PHASE_OPERATIONAL:
pr_debug("Operational phase.\n");
+ break;
default:
pr_debug("Unknown phase.\n");
}
diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c
index 1a91195ab61..69e0cfd9887 100644
--- a/drivers/target/iscsi/iscsi_target_util.c
+++ b/drivers/target/iscsi/iscsi_target_util.c
@@ -684,7 +684,7 @@ void iscsit_release_cmd(struct iscsi_cmd *cmd)
void iscsit_free_cmd(struct iscsi_cmd *cmd)
{
/*
- * Determine if a struct se_cmd is assoicated with
+ * Determine if a struct se_cmd is associated with
* this struct iscsi_cmd.
*/
switch (cmd->iscsi_opcode) {
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 8636fae1f7e..c2c77d1ac49 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -98,7 +98,7 @@ config EXYNOS_THERMAL
depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5)
depends on CPU_THERMAL
help
- If you say yes here you get support for TMU (Thermal Managment
+ If you say yes here you get support for TMU (Thermal Management
Unit) on SAMSUNG EXYNOS series of SoC.
config DB8500_THERMAL
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index d8e05eeab23..0ecf22b6a38 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -357,6 +357,7 @@ config TRACE_SINK
config PPC_EPAPR_HV_BYTECHAN
tristate "ePAPR hypervisor byte channel driver"
depends on PPC
+ select EPAPR_PARAVIRT
help
This driver creates /dev entries for each ePAPR hypervisor byte
channel, thereby allowing applications to communicate with byte
diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c
index be1a9a1e749..cd69b48f6df 100644
--- a/drivers/tty/hvc/hvc_opal.c
+++ b/drivers/tty/hvc/hvc_opal.c
@@ -178,7 +178,7 @@ static int hvc_opal_probe(struct platform_device *dev)
proto = HV_PROTOCOL_HVSI;
ops = &hvc_opal_hvsi_ops;
} else {
- pr_err("hvc_opal: Unkown protocol for %s\n",
+ pr_err("hvc_opal: Unknown protocol for %s\n",
dev->dev.of_node->full_name);
return -ENXIO;
}
diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c
index ed6f5f1f5a5..0c629807610 100644
--- a/drivers/tty/hvc/hvc_vio.c
+++ b/drivers/tty/hvc/hvc_vio.c
@@ -313,7 +313,7 @@ static int hvc_vio_probe(struct vio_dev *vdev,
proto = HV_PROTOCOL_HVSI;
ops = &hvterm_hvsi_ops;
} else {
- pr_err("hvc_vio: Unkown protocol for %s\n", vdev->dev.of_node->full_name);
+ pr_err("hvc_vio: Unknown protocol for %s\n", vdev->dev.of_node->full_name);
return -ENXIO;
}
diff --git a/drivers/tty/ipwireless/setup_protocol.h b/drivers/tty/ipwireless/setup_protocol.h
index 9d6bcc77c73..002c34e7252 100644
--- a/drivers/tty/ipwireless/setup_protocol.h
+++ b/drivers/tty/ipwireless/setup_protocol.h
@@ -59,7 +59,7 @@ struct tl_setup_config_done_msg {
unsigned char sig_no; /* TL_SETUP_SIGNO_CONFIG_DONE_MSG */
} __attribute__ ((__packed__));
-/* Asyncronous messages */
+/* Asynchronous messages */
struct tl_setup_open_msg {
unsigned char sig_no; /* TL_SETUP_SIGNO_OPEN_MSG */
unsigned char port_no;
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index f3d283f2e3a..c31133a6ea8 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -271,7 +271,7 @@ config SERIAL_8250_DW
present in the Synopsys DesignWare APB UART.
config SERIAL_8250_EM
- tristate "Support for Emma Mobile intergrated serial port"
+ tristate "Support for Emma Mobile integrated serial port"
depends on SERIAL_8250 && ARM && HAVE_CLK
help
Selecting this option will add support for the integrated serial
diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c
index e6a008f4939..2e2b2c1cb72 100644
--- a/drivers/tty/serial/bfin_uart.c
+++ b/drivers/tty/serial/bfin_uart.c
@@ -815,7 +815,7 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios,
lcr = WLS(5);
break;
default:
- printk(KERN_ERR "%s: word lengh not supported\n",
+ printk(KERN_ERR "%s: word length not supported\n",
__func__);
}
diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c
index 6197a69adb4..72b6334bcf1 100644
--- a/drivers/tty/serial/icom.c
+++ b/drivers/tty/serial/icom.c
@@ -505,7 +505,7 @@ static void load_code(struct icom_port *icom_port)
/* Stop processor */
stop_processor(icom_port);
- dev_err(&icom_port->adapter->pci_dev->dev,"Port not opertional\n");
+ dev_err(&icom_port->adapter->pci_dev->dev,"Port not operational\n");
}
if (new_page != NULL)
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 88dde95b679..d938b2b99e3 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -222,7 +222,7 @@ static int usb_probe_device(struct device *dev)
/* TODO: Add real matching code */
/* The device should always appear to be in use
- * unless the driver suports autosuspend.
+ * unless the driver supports autosuspend.
*/
if (!udriver->supports_autosuspend)
error = usb_autoresume_device(udev);
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index 59dcea2f695..f4a21f6f081 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -51,7 +51,7 @@
* full speed USB controllers, including the at91rm9200 (arm920T, with MMU),
* at91sam926x (arm926ejs, with MMU), and several no-mmu versions.
*
- * This driver expects the board has been wired with two GPIOs suppporting
+ * This driver expects the board has been wired with two GPIOs supporting
* a VBUS sensing IRQ, and a D+ pullup. (They may be omitted, but the
* testing hasn't covered such cases.)
*
diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c
index ea5f2586fbd..6c3586a4c95 100644
--- a/drivers/usb/storage/realtek_cr.c
+++ b/drivers/usb/storage/realtek_cr.c
@@ -883,7 +883,7 @@ static void rts51x_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
} else {
US_DEBUGP("%s: NOT working scsi, not SS\n", __func__);
chip->proto_handler_backup(srb, us);
- /* Check wether card is plugged in */
+ /* Check whether card is plugged in */
if (srb->cmnd[0] == TEST_UNIT_READY) {
if (srb->result == SAM_STAT_GOOD) {
SET_LUN_READY(chip, srb->device->lun);
diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c
index 79e7e4d45eb..d670130ee68 100644
--- a/drivers/vhost/tcm_vhost.c
+++ b/drivers/vhost/tcm_vhost.c
@@ -230,7 +230,7 @@ static struct se_node_acl *tcm_vhost_alloc_fabric_acl(
nacl = kzalloc(sizeof(struct tcm_vhost_nacl), GFP_KERNEL);
if (!nacl) {
- pr_err("Unable to alocate struct tcm_vhost_nacl\n");
+ pr_err("Unable to allocate struct tcm_vhost_nacl\n");
return NULL;
}
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index d08d7998a4a..9c31277b3a8 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2045,7 +2045,7 @@ config FB_S3C_DEBUG_REGWRITE
bool "Debug register writes"
depends on FB_S3C
---help---
- Show all register writes via printk(KERN_DEBUG)
+ Show all register writes via pr_debug()
config FB_S3C2410
tristate "S3C2410 LCD framebuffer support"
@@ -2442,4 +2442,19 @@ config FB_SH_MOBILE_MERAM
Up to 4 memory channels can be configured, allowing 4 RGB or
2 YCbCr framebuffers to be configured.
+config FB_SSD1307
+ tristate "Solomon SSD1307 framebuffer support"
+ depends on FB && I2C
+ depends on OF
+ depends on GENERIC_GPIO
+ select FB_SYS_FOPS
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ select FB_DEFERRED_IO
+ select PWM
+ help
+ This driver implements support for the Solomon SSD1307
+ OLED controller over I2C.
+
endmenu
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 23e948ebfab..768a137a1ba 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -161,6 +161,7 @@ obj-$(CONFIG_FB_BFIN_7393) += bfin_adv7393fb.o
obj-$(CONFIG_FB_MX3) += mx3fb.o
obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o
obj-$(CONFIG_FB_MXS) += mxsfb.o
+obj-$(CONFIG_FB_SSD1307) += ssd1307fb.o
# the test framebuffer is last
obj-$(CONFIG_FB_VIRTUAL) += vfb.o
diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c
index 6d159662904..b05afd03729 100644
--- a/drivers/video/console/newport_con.c
+++ b/drivers/video/console/newport_con.c
@@ -327,9 +327,16 @@ out_unmap:
static void newport_init(struct vc_data *vc, int init)
{
- vc->vc_cols = newport_xsize / 8;
- vc->vc_rows = newport_ysize / 16;
+ int cols, rows;
+
+ cols = newport_xsize / 8;
+ rows = newport_ysize / 16;
vc->vc_can_do_color = 1;
+ if (init) {
+ vc->vc_cols = cols;
+ vc->vc_rows = rows;
+ } else
+ vc_resize(vc, cols, rows);
}
static void newport_deinit(struct vc_data *c)
diff --git a/drivers/video/console/softcursor.c b/drivers/video/console/softcursor.c
index 25f835bf3d7..46dd8f5d2e9 100644
--- a/drivers/video/console/softcursor.c
+++ b/drivers/video/console/softcursor.c
@@ -35,8 +35,7 @@ int soft_cursor(struct fb_info *info, struct fb_cursor *cursor)
dsize = s_pitch * cursor->image.height;
if (dsize + sizeof(struct fb_image) != ops->cursor_size) {
- if (ops->cursor_src != NULL)
- kfree(ops->cursor_src);
+ kfree(ops->cursor_src);
ops->cursor_size = dsize + sizeof(struct fb_image);
ops->cursor_src = kmalloc(ops->cursor_size, GFP_ATOMIC);
diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c
index 80665f66ac1..46534e00fe0 100644
--- a/drivers/video/da8xx-fb.c
+++ b/drivers/video/da8xx-fb.c
@@ -213,62 +213,51 @@ static struct fb_fix_screeninfo da8xx_fb_fix __devinitdata = {
.accel = FB_ACCEL_NONE
};
-struct da8xx_panel {
- const char name[25]; /* Full name <vendor>_<model> */
- unsigned short width;
- unsigned short height;
- int hfp; /* Horizontal front porch */
- int hbp; /* Horizontal back porch */
- int hsw; /* Horizontal Sync Pulse Width */
- int vfp; /* Vertical front porch */
- int vbp; /* Vertical back porch */
- int vsw; /* Vertical Sync Pulse Width */
- unsigned int pxl_clk; /* Pixel clock */
- unsigned char invert_pxl_clk; /* Invert Pixel clock */
-};
-
-static struct da8xx_panel known_lcd_panels[] = {
+static struct fb_videomode known_lcd_panels[] = {
/* Sharp LCD035Q3DG01 */
[0] = {
- .name = "Sharp_LCD035Q3DG01",
- .width = 320,
- .height = 240,
- .hfp = 8,
- .hbp = 6,
- .hsw = 0,
- .vfp = 2,
- .vbp = 2,
- .vsw = 0,
- .pxl_clk = 4608000,
- .invert_pxl_clk = 1,
+ .name = "Sharp_LCD035Q3DG01",
+ .xres = 320,
+ .yres = 240,
+ .pixclock = 4608000,
+ .left_margin = 6,
+ .right_margin = 8,
+ .upper_margin = 2,
+ .lower_margin = 2,
+ .hsync_len = 0,
+ .vsync_len = 0,
+ .sync = FB_SYNC_CLK_INVERT |
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
},
/* Sharp LK043T1DG01 */
[1] = {
- .name = "Sharp_LK043T1DG01",
- .width = 480,
- .height = 272,
- .hfp = 2,
- .hbp = 2,
- .hsw = 41,
- .vfp = 2,
- .vbp = 2,
- .vsw = 10,
- .pxl_clk = 7833600,
- .invert_pxl_clk = 0,
+ .name = "Sharp_LK043T1DG01",
+ .xres = 480,
+ .yres = 272,
+ .pixclock = 7833600,
+ .left_margin = 2,
+ .right_margin = 2,
+ .upper_margin = 2,
+ .lower_margin = 2,
+ .hsync_len = 41,
+ .vsync_len = 10,
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .flag = 0,
},
[2] = {
/* Hitachi SP10Q010 */
- .name = "SP10Q010",
- .width = 320,
- .height = 240,
- .hfp = 10,
- .hbp = 10,
- .hsw = 10,
- .vfp = 10,
- .vbp = 10,
- .vsw = 10,
- .pxl_clk = 7833600,
- .invert_pxl_clk = 0,
+ .name = "SP10Q010",
+ .xres = 320,
+ .yres = 240,
+ .pixclock = 7833600,
+ .left_margin = 10,
+ .right_margin = 10,
+ .upper_margin = 10,
+ .lower_margin = 10,
+ .hsync_len = 10,
+ .vsync_len = 10,
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .flag = 0,
},
};
@@ -399,10 +388,9 @@ static int lcd_cfg_dma(int burst_size, int fifo_th)
reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_8);
break;
case 16:
+ default:
reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_16);
break;
- default:
- return -EINVAL;
}
reg |= (fifo_th << 8);
@@ -447,7 +435,8 @@ static void lcd_cfg_vertical_sync(int back_porch, int pulse_width,
lcdc_write(reg, LCD_RASTER_TIMING_1_REG);
}
-static int lcd_cfg_display(const struct lcd_ctrl_config *cfg)
+static int lcd_cfg_display(const struct lcd_ctrl_config *cfg,
+ struct fb_videomode *panel)
{
u32 reg;
u32 reg_int;
@@ -456,7 +445,7 @@ static int lcd_cfg_display(const struct lcd_ctrl_config *cfg)
LCD_MONO_8BIT_MODE |
LCD_MONOCHROME_MODE);
- switch (cfg->p_disp_panel->panel_shade) {
+ switch (cfg->panel_shade) {
case MONOCHROME:
reg |= LCD_MONOCHROME_MODE;
if (cfg->mono_8bit_mode)
@@ -469,7 +458,9 @@ static int lcd_cfg_display(const struct lcd_ctrl_config *cfg)
break;
case COLOR_PASSIVE:
- if (cfg->stn_565_mode)
+ /* AC bias applicable only for Pasive panels */
+ lcd_cfg_ac_bias(cfg->ac_bias, cfg->ac_bias_intrpt);
+ if (cfg->bpp == 12 && cfg->stn_565_mode)
reg |= LCD_STN_565_ENABLE;
break;
@@ -490,22 +481,19 @@ static int lcd_cfg_display(const struct lcd_ctrl_config *cfg)
reg = lcdc_read(LCD_RASTER_TIMING_2_REG);
- if (cfg->sync_ctrl)
- reg |= LCD_SYNC_CTRL;
- else
- reg &= ~LCD_SYNC_CTRL;
+ reg |= LCD_SYNC_CTRL;
if (cfg->sync_edge)
reg |= LCD_SYNC_EDGE;
else
reg &= ~LCD_SYNC_EDGE;
- if (cfg->invert_line_clock)
+ if (panel->sync & FB_SYNC_HOR_HIGH_ACT)
reg |= LCD_INVERT_LINE_CLOCK;
else
reg &= ~LCD_INVERT_LINE_CLOCK;
- if (cfg->invert_frm_clock)
+ if (panel->sync & FB_SYNC_VERT_HIGH_ACT)
reg |= LCD_INVERT_FRAME_CLOCK;
else
reg &= ~LCD_INVERT_FRAME_CLOCK;
@@ -728,7 +716,7 @@ static void lcd_calc_clk_divider(struct da8xx_fb_par *par)
}
static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
- struct da8xx_panel *panel)
+ struct fb_videomode *panel)
{
u32 bpp;
int ret = 0;
@@ -738,7 +726,7 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
/* Calculate the divider */
lcd_calc_clk_divider(par);
- if (panel->invert_pxl_clk)
+ if (panel->sync & FB_SYNC_CLK_INVERT)
lcdc_write((lcdc_read(LCD_RASTER_TIMING_2_REG) |
LCD_INVERT_PIXEL_CLOCK), LCD_RASTER_TIMING_2_REG);
else
@@ -750,30 +738,23 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
if (ret < 0)
return ret;
- /* Configure the AC bias properties. */
- lcd_cfg_ac_bias(cfg->ac_bias, cfg->ac_bias_intrpt);
-
/* Configure the vertical and horizontal sync properties. */
- lcd_cfg_vertical_sync(panel->vbp, panel->vsw, panel->vfp);
- lcd_cfg_horizontal_sync(panel->hbp, panel->hsw, panel->hfp);
+ lcd_cfg_vertical_sync(panel->lower_margin, panel->vsync_len,
+ panel->upper_margin);
+ lcd_cfg_horizontal_sync(panel->right_margin, panel->hsync_len,
+ panel->left_margin);
/* Configure for disply */
- ret = lcd_cfg_display(cfg);
+ ret = lcd_cfg_display(cfg, panel);
if (ret < 0)
return ret;
- if (QVGA != cfg->p_disp_panel->panel_type)
- return -EINVAL;
+ bpp = cfg->bpp;
- if (cfg->bpp <= cfg->p_disp_panel->max_bpp &&
- cfg->bpp >= cfg->p_disp_panel->min_bpp)
- bpp = cfg->bpp;
- else
- bpp = cfg->p_disp_panel->max_bpp;
if (bpp == 12)
bpp = 16;
- ret = lcd_cfg_frame_buffer(par, (unsigned int)panel->width,
- (unsigned int)panel->height, bpp,
+ ret = lcd_cfg_frame_buffer(par, (unsigned int)panel->xres,
+ (unsigned int)panel->yres, bpp,
cfg->raster_order);
if (ret < 0)
return ret;
@@ -1235,7 +1216,7 @@ static int __devinit fb_probe(struct platform_device *device)
struct da8xx_lcdc_platform_data *fb_pdata =
device->dev.platform_data;
struct lcd_ctrl_config *lcd_cfg;
- struct da8xx_panel *lcdc_info;
+ struct fb_videomode *lcdc_info;
struct fb_info *da8xx_fb_info;
struct clk *fb_clk = NULL;
struct da8xx_fb_par *par;
@@ -1267,7 +1248,7 @@ static int __devinit fb_probe(struct platform_device *device)
goto err_request_mem;
}
- fb_clk = clk_get(&device->dev, NULL);
+ fb_clk = clk_get(&device->dev, "fck");
if (IS_ERR(fb_clk)) {
dev_err(&device->dev, "Can not get device clock\n");
ret = -ENODEV;
@@ -1283,6 +1264,7 @@ static int __devinit fb_probe(struct platform_device *device)
lcd_revision = LCD_VERSION_1;
break;
case 0x4F200800:
+ case 0x4F201000:
lcd_revision = LCD_VERSION_2;
break;
default:
@@ -1323,7 +1305,7 @@ static int __devinit fb_probe(struct platform_device *device)
#ifdef CONFIG_CPU_FREQ
par->lcd_fck_rate = clk_get_rate(fb_clk);
#endif
- par->pxl_clk = lcdc_info->pxl_clk;
+ par->pxl_clk = lcdc_info->pixclock;
if (fb_pdata->panel_power_ctrl) {
par->panel_power_ctrl = fb_pdata->panel_power_ctrl;
par->panel_power_ctrl(1);
@@ -1336,8 +1318,8 @@ static int __devinit fb_probe(struct platform_device *device)
}
/* allocate frame buffer */
- par->vram_size = lcdc_info->width * lcdc_info->height * lcd_cfg->bpp;
- ulcm = lcm((lcdc_info->width * lcd_cfg->bpp)/8, PAGE_SIZE);
+ par->vram_size = lcdc_info->xres * lcdc_info->yres * lcd_cfg->bpp;
+ ulcm = lcm((lcdc_info->xres * lcd_cfg->bpp)/8, PAGE_SIZE);
par->vram_size = roundup(par->vram_size/8, ulcm);
par->vram_size = par->vram_size * LCD_NUM_BUFFERS;
@@ -1355,10 +1337,10 @@ static int __devinit fb_probe(struct platform_device *device)
da8xx_fb_info->screen_base = (char __iomem *) par->vram_virt;
da8xx_fb_fix.smem_start = par->vram_phys;
da8xx_fb_fix.smem_len = par->vram_size;
- da8xx_fb_fix.line_length = (lcdc_info->width * lcd_cfg->bpp) / 8;
+ da8xx_fb_fix.line_length = (lcdc_info->xres * lcd_cfg->bpp) / 8;
par->dma_start = par->vram_phys;
- par->dma_end = par->dma_start + lcdc_info->height *
+ par->dma_end = par->dma_start + lcdc_info->yres *
da8xx_fb_fix.line_length - 1;
/* allocate palette buffer */
@@ -1384,22 +1366,22 @@ static int __devinit fb_probe(struct platform_device *device)
/* Initialize par */
da8xx_fb_info->var.bits_per_pixel = lcd_cfg->bpp;
- da8xx_fb_var.xres = lcdc_info->width;
- da8xx_fb_var.xres_virtual = lcdc_info->width;
+ da8xx_fb_var.xres = lcdc_info->xres;
+ da8xx_fb_var.xres_virtual = lcdc_info->xres;
- da8xx_fb_var.yres = lcdc_info->height;
- da8xx_fb_var.yres_virtual = lcdc_info->height * LCD_NUM_BUFFERS;
+ da8xx_fb_var.yres = lcdc_info->yres;
+ da8xx_fb_var.yres_virtual = lcdc_info->yres * LCD_NUM_BUFFERS;
da8xx_fb_var.grayscale =
- lcd_cfg->p_disp_panel->panel_shade == MONOCHROME ? 1 : 0;
+ lcd_cfg->panel_shade == MONOCHROME ? 1 : 0;
da8xx_fb_var.bits_per_pixel = lcd_cfg->bpp;
- da8xx_fb_var.hsync_len = lcdc_info->hsw;
- da8xx_fb_var.vsync_len = lcdc_info->vsw;
- da8xx_fb_var.right_margin = lcdc_info->hfp;
- da8xx_fb_var.left_margin = lcdc_info->hbp;
- da8xx_fb_var.lower_margin = lcdc_info->vfp;
- da8xx_fb_var.upper_margin = lcdc_info->vbp;
+ da8xx_fb_var.hsync_len = lcdc_info->hsync_len;
+ da8xx_fb_var.vsync_len = lcdc_info->vsync_len;
+ da8xx_fb_var.right_margin = lcdc_info->right_margin;
+ da8xx_fb_var.left_margin = lcdc_info->left_margin;
+ da8xx_fb_var.lower_margin = lcdc_info->lower_margin;
+ da8xx_fb_var.upper_margin = lcdc_info->upper_margin;
da8xx_fb_var.pixclock = da8xxfb_pixel_clk_period(par);
/* Initialize fbinfo */
diff --git a/drivers/video/exynos/exynos_dp_core.c b/drivers/video/exynos/exynos_dp_core.c
index d55470e7541..28fd686c6b8 100644
--- a/drivers/video/exynos/exynos_dp_core.c
+++ b/drivers/video/exynos/exynos_dp_core.c
@@ -18,6 +18,7 @@
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
+#include <linux/of.h>
#include <video/exynos_dp.h>
@@ -48,10 +49,6 @@ static int exynos_dp_detect_hpd(struct exynos_dp_device *dp)
{
int timeout_loop = 0;
- exynos_dp_init_hpd(dp);
-
- usleep_range(200, 210);
-
while (exynos_dp_get_plug_in_status(dp) != 0) {
timeout_loop++;
if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
@@ -90,9 +87,11 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
*/
/* Read Extension Flag, Number of 128-byte EDID extension blocks */
- exynos_dp_read_byte_from_i2c(dp, I2C_EDID_DEVICE_ADDR,
+ retval = exynos_dp_read_byte_from_i2c(dp, I2C_EDID_DEVICE_ADDR,
EDID_EXTENSION_FLAG,
&extend_block);
+ if (retval)
+ return retval;
if (extend_block > 0) {
dev_dbg(dp->dev, "EDID data includes a single extension!\n");
@@ -181,14 +180,15 @@ static int exynos_dp_handle_edid(struct exynos_dp_device *dp)
int retval;
/* Read DPCD DPCD_ADDR_DPCD_REV~RECEIVE_PORT1_CAP_1 */
- exynos_dp_read_bytes_from_dpcd(dp,
- DPCD_ADDR_DPCD_REV,
- 12, buf);
+ retval = exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_DPCD_REV,
+ 12, buf);
+ if (retval)
+ return retval;
/* Read EDID */
for (i = 0; i < 3; i++) {
retval = exynos_dp_read_edid(dp);
- if (retval == 0)
+ if (!retval)
break;
}
@@ -261,11 +261,10 @@ static void exynos_dp_set_lane_lane_pre_emphasis(struct exynos_dp_device *dp,
}
}
-static void exynos_dp_link_start(struct exynos_dp_device *dp)
+static int exynos_dp_link_start(struct exynos_dp_device *dp)
{
u8 buf[4];
- int lane;
- int lane_count;
+ int lane, lane_count, pll_tries, retval;
lane_count = dp->link_train.lane_count;
@@ -275,10 +274,6 @@ static void exynos_dp_link_start(struct exynos_dp_device *dp)
for (lane = 0; lane < lane_count; lane++)
dp->link_train.cr_loop[lane] = 0;
- /* Set sink to D0 (Sink Not Ready) mode. */
- exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_SINK_POWER_STATE,
- DPCD_SET_POWER_STATE_D0);
-
/* Set link rate and count as you want to establish*/
exynos_dp_set_link_bandwidth(dp, dp->link_train.link_rate);
exynos_dp_set_lane_count(dp, dp->link_train.lane_count);
@@ -286,29 +281,46 @@ static void exynos_dp_link_start(struct exynos_dp_device *dp)
/* Setup RX configuration */
buf[0] = dp->link_train.link_rate;
buf[1] = dp->link_train.lane_count;
- exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_LINK_BW_SET,
+ retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_LINK_BW_SET,
2, buf);
+ if (retval)
+ return retval;
/* Set TX pre-emphasis to minimum */
for (lane = 0; lane < lane_count; lane++)
exynos_dp_set_lane_lane_pre_emphasis(dp,
PRE_EMPHASIS_LEVEL_0, lane);
+ /* Wait for PLL lock */
+ pll_tries = 0;
+ while (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
+ if (pll_tries == DP_TIMEOUT_LOOP_COUNT) {
+ dev_err(dp->dev, "Wait for PLL lock timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ pll_tries++;
+ usleep_range(90, 120);
+ }
+
/* Set training pattern 1 */
exynos_dp_set_training_pattern(dp, TRAINING_PTN1);
/* Set RX training pattern */
- exynos_dp_write_byte_to_dpcd(dp,
- DPCD_ADDR_TRAINING_PATTERN_SET,
- DPCD_SCRAMBLING_DISABLED |
- DPCD_TRAINING_PATTERN_1);
+ retval = exynos_dp_write_byte_to_dpcd(dp,
+ DPCD_ADDR_TRAINING_PATTERN_SET,
+ DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_1);
+ if (retval)
+ return retval;
for (lane = 0; lane < lane_count; lane++)
buf[lane] = DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 |
DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0;
- exynos_dp_write_bytes_to_dpcd(dp,
- DPCD_ADDR_TRAINING_LANE0_SET,
- lane_count, buf);
+
+ retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_TRAINING_LANE0_SET,
+ lane_count, buf);
+
+ return retval;
}
static unsigned char exynos_dp_get_lane_status(u8 link_status[2], int lane)
@@ -332,18 +344,17 @@ static int exynos_dp_clock_recovery_ok(u8 link_status[2], int lane_count)
return 0;
}
-static int exynos_dp_channel_eq_ok(u8 link_align[3], int lane_count)
+static int exynos_dp_channel_eq_ok(u8 link_status[2], u8 link_align,
+ int lane_count)
{
int lane;
- u8 lane_align;
u8 lane_status;
- lane_align = link_align[2];
- if ((lane_align & DPCD_INTERLANE_ALIGN_DONE) == 0)
+ if ((link_align & DPCD_INTERLANE_ALIGN_DONE) == 0)
return -EINVAL;
for (lane = 0; lane < lane_count; lane++) {
- lane_status = exynos_dp_get_lane_status(link_align, lane);
+ lane_status = exynos_dp_get_lane_status(link_status, lane);
lane_status &= DPCD_CHANNEL_EQ_BITS;
if (lane_status != DPCD_CHANNEL_EQ_BITS)
return -EINVAL;
@@ -427,60 +438,60 @@ static void exynos_dp_reduce_link_rate(struct exynos_dp_device *dp)
dp->link_train.lt_state = FAILED;
}
-static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
+static void exynos_dp_get_adjust_training_lane(struct exynos_dp_device *dp,
+ u8 adjust_request[2])
{
- u8 link_status[2];
- int lane;
- int lane_count;
+ int lane, lane_count;
+ u8 voltage_swing, pre_emphasis, training_lane;
- u8 adjust_request[2];
- u8 voltage_swing;
- u8 pre_emphasis;
- u8 training_lane;
+ lane_count = dp->link_train.lane_count;
+ for (lane = 0; lane < lane_count; lane++) {
+ voltage_swing = exynos_dp_get_adjust_request_voltage(
+ adjust_request, lane);
+ pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis(
+ adjust_request, lane);
+ training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) |
+ DPCD_PRE_EMPHASIS_SET(pre_emphasis);
+
+ if (voltage_swing == VOLTAGE_LEVEL_3)
+ training_lane |= DPCD_MAX_SWING_REACHED;
+ if (pre_emphasis == PRE_EMPHASIS_LEVEL_3)
+ training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED;
+
+ dp->link_train.training_lane[lane] = training_lane;
+ }
+}
+
+static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
+{
+ int lane, lane_count, retval;
+ u8 voltage_swing, pre_emphasis, training_lane;
+ u8 link_status[2], adjust_request[2];
usleep_range(100, 101);
lane_count = dp->link_train.lane_count;
- exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS,
- 2, link_status);
+ retval = exynos_dp_read_bytes_from_dpcd(dp,
+ DPCD_ADDR_LANE0_1_STATUS, 2, link_status);
+ if (retval)
+ return retval;
+
+ retval = exynos_dp_read_bytes_from_dpcd(dp,
+ DPCD_ADDR_ADJUST_REQUEST_LANE0_1, 2, adjust_request);
+ if (retval)
+ return retval;
if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) {
/* set training pattern 2 for EQ */
exynos_dp_set_training_pattern(dp, TRAINING_PTN2);
- for (lane = 0; lane < lane_count; lane++) {
- exynos_dp_read_bytes_from_dpcd(dp,
- DPCD_ADDR_ADJUST_REQUEST_LANE0_1,
- 2, adjust_request);
- voltage_swing = exynos_dp_get_adjust_request_voltage(
- adjust_request, lane);
- pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis(
- adjust_request, lane);
- training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) |
- DPCD_PRE_EMPHASIS_SET(pre_emphasis);
-
- if (voltage_swing == VOLTAGE_LEVEL_3)
- training_lane |= DPCD_MAX_SWING_REACHED;
- if (pre_emphasis == PRE_EMPHASIS_LEVEL_3)
- training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED;
-
- dp->link_train.training_lane[lane] = training_lane;
-
- exynos_dp_set_lane_link_training(dp,
- dp->link_train.training_lane[lane],
- lane);
- }
-
- exynos_dp_write_byte_to_dpcd(dp,
- DPCD_ADDR_TRAINING_PATTERN_SET,
- DPCD_SCRAMBLING_DISABLED |
- DPCD_TRAINING_PATTERN_2);
-
- exynos_dp_write_bytes_to_dpcd(dp,
- DPCD_ADDR_TRAINING_LANE0_SET,
- lane_count,
- dp->link_train.training_lane);
+ retval = exynos_dp_write_byte_to_dpcd(dp,
+ DPCD_ADDR_TRAINING_PATTERN_SET,
+ DPCD_SCRAMBLING_DISABLED |
+ DPCD_TRAINING_PATTERN_2);
+ if (retval)
+ return retval;
dev_info(dp->dev, "Link Training Clock Recovery success\n");
dp->link_train.lt_state = EQUALIZER_TRAINING;
@@ -488,152 +499,116 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
for (lane = 0; lane < lane_count; lane++) {
training_lane = exynos_dp_get_lane_link_training(
dp, lane);
- exynos_dp_read_bytes_from_dpcd(dp,
- DPCD_ADDR_ADJUST_REQUEST_LANE0_1,
- 2, adjust_request);
voltage_swing = exynos_dp_get_adjust_request_voltage(
adjust_request, lane);
pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis(
adjust_request, lane);
- if (voltage_swing == VOLTAGE_LEVEL_3 ||
- pre_emphasis == PRE_EMPHASIS_LEVEL_3) {
- dev_err(dp->dev, "voltage or pre emphasis reached max level\n");
- goto reduce_link_rate;
- }
-
- if ((DPCD_VOLTAGE_SWING_GET(training_lane) ==
- voltage_swing) &&
- (DPCD_PRE_EMPHASIS_GET(training_lane) ==
- pre_emphasis)) {
+ if (DPCD_VOLTAGE_SWING_GET(training_lane) ==
+ voltage_swing &&
+ DPCD_PRE_EMPHASIS_GET(training_lane) ==
+ pre_emphasis)
dp->link_train.cr_loop[lane]++;
- if (dp->link_train.cr_loop[lane] == MAX_CR_LOOP) {
- dev_err(dp->dev, "CR Max loop\n");
- goto reduce_link_rate;
- }
- }
-
- training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) |
- DPCD_PRE_EMPHASIS_SET(pre_emphasis);
- if (voltage_swing == VOLTAGE_LEVEL_3)
- training_lane |= DPCD_MAX_SWING_REACHED;
- if (pre_emphasis == PRE_EMPHASIS_LEVEL_3)
- training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED;
+ if (dp->link_train.cr_loop[lane] == MAX_CR_LOOP ||
+ voltage_swing == VOLTAGE_LEVEL_3 ||
+ pre_emphasis == PRE_EMPHASIS_LEVEL_3) {
+ dev_err(dp->dev, "CR Max reached (%d,%d,%d)\n",
+ dp->link_train.cr_loop[lane],
+ voltage_swing, pre_emphasis);
+ exynos_dp_reduce_link_rate(dp);
+ return -EIO;
+ }
+ }
+ }
- dp->link_train.training_lane[lane] = training_lane;
+ exynos_dp_get_adjust_training_lane(dp, adjust_request);
- exynos_dp_set_lane_link_training(dp,
- dp->link_train.training_lane[lane], lane);
- }
+ for (lane = 0; lane < lane_count; lane++)
+ exynos_dp_set_lane_link_training(dp,
+ dp->link_train.training_lane[lane], lane);
- exynos_dp_write_bytes_to_dpcd(dp,
- DPCD_ADDR_TRAINING_LANE0_SET,
- lane_count,
+ retval = exynos_dp_write_bytes_to_dpcd(dp,
+ DPCD_ADDR_TRAINING_LANE0_SET, lane_count,
dp->link_train.training_lane);
- }
-
- return 0;
+ if (retval)
+ return retval;
-reduce_link_rate:
- exynos_dp_reduce_link_rate(dp);
- return -EIO;
+ return retval;
}
static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
{
- u8 link_status[2];
- u8 link_align[3];
- int lane;
- int lane_count;
+ int lane, lane_count, retval;
u32 reg;
-
- u8 adjust_request[2];
- u8 voltage_swing;
- u8 pre_emphasis;
- u8 training_lane;
+ u8 link_align, link_status[2], adjust_request[2];
usleep_range(400, 401);
lane_count = dp->link_train.lane_count;
- exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS,
- 2, link_status);
+ retval = exynos_dp_read_bytes_from_dpcd(dp,
+ DPCD_ADDR_LANE0_1_STATUS, 2, link_status);
+ if (retval)
+ return retval;
- if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) {
- link_align[0] = link_status[0];
- link_align[1] = link_status[1];
+ if (exynos_dp_clock_recovery_ok(link_status, lane_count)) {
+ exynos_dp_reduce_link_rate(dp);
+ return -EIO;
+ }
- exynos_dp_read_byte_from_dpcd(dp,
- DPCD_ADDR_LANE_ALIGN_STATUS_UPDATED,
- &link_align[2]);
+ retval = exynos_dp_read_bytes_from_dpcd(dp,
+ DPCD_ADDR_ADJUST_REQUEST_LANE0_1, 2, adjust_request);
+ if (retval)
+ return retval;
- for (lane = 0; lane < lane_count; lane++) {
- exynos_dp_read_bytes_from_dpcd(dp,
- DPCD_ADDR_ADJUST_REQUEST_LANE0_1,
- 2, adjust_request);
- voltage_swing = exynos_dp_get_adjust_request_voltage(
- adjust_request, lane);
- pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis(
- adjust_request, lane);
- training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) |
- DPCD_PRE_EMPHASIS_SET(pre_emphasis);
+ retval = exynos_dp_read_byte_from_dpcd(dp,
+ DPCD_ADDR_LANE_ALIGN_STATUS_UPDATED, &link_align);
+ if (retval)
+ return retval;
- if (voltage_swing == VOLTAGE_LEVEL_3)
- training_lane |= DPCD_MAX_SWING_REACHED;
- if (pre_emphasis == PRE_EMPHASIS_LEVEL_3)
- training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED;
+ exynos_dp_get_adjust_training_lane(dp, adjust_request);
- dp->link_train.training_lane[lane] = training_lane;
- }
+ if (!exynos_dp_channel_eq_ok(link_status, link_align, lane_count)) {
+ /* traing pattern Set to Normal */
+ exynos_dp_training_pattern_dis(dp);
- if (exynos_dp_channel_eq_ok(link_align, lane_count) == 0) {
- /* traing pattern Set to Normal */
- exynos_dp_training_pattern_dis(dp);
+ dev_info(dp->dev, "Link Training success!\n");
- dev_info(dp->dev, "Link Training success!\n");
-
- exynos_dp_get_link_bandwidth(dp, &reg);
- dp->link_train.link_rate = reg;
- dev_dbg(dp->dev, "final bandwidth = %.2x\n",
- dp->link_train.link_rate);
+ exynos_dp_get_link_bandwidth(dp, &reg);
+ dp->link_train.link_rate = reg;
+ dev_dbg(dp->dev, "final bandwidth = %.2x\n",
+ dp->link_train.link_rate);
- exynos_dp_get_lane_count(dp, &reg);
- dp->link_train.lane_count = reg;
- dev_dbg(dp->dev, "final lane count = %.2x\n",
- dp->link_train.lane_count);
+ exynos_dp_get_lane_count(dp, &reg);
+ dp->link_train.lane_count = reg;
+ dev_dbg(dp->dev, "final lane count = %.2x\n",
+ dp->link_train.lane_count);
- /* set enhanced mode if available */
- exynos_dp_set_enhanced_mode(dp);
- dp->link_train.lt_state = FINISHED;
- } else {
- /* not all locked */
- dp->link_train.eq_loop++;
+ /* set enhanced mode if available */
+ exynos_dp_set_enhanced_mode(dp);
+ dp->link_train.lt_state = FINISHED;
- if (dp->link_train.eq_loop > MAX_EQ_LOOP) {
- dev_err(dp->dev, "EQ Max loop\n");
- goto reduce_link_rate;
- }
+ return 0;
+ }
- for (lane = 0; lane < lane_count; lane++)
- exynos_dp_set_lane_link_training(dp,
- dp->link_train.training_lane[lane],
- lane);
+ /* not all locked */
+ dp->link_train.eq_loop++;
- exynos_dp_write_bytes_to_dpcd(dp,
- DPCD_ADDR_TRAINING_LANE0_SET,
- lane_count,
- dp->link_train.training_lane);
- }
- } else {
- goto reduce_link_rate;
+ if (dp->link_train.eq_loop > MAX_EQ_LOOP) {
+ dev_err(dp->dev, "EQ Max loop\n");
+ exynos_dp_reduce_link_rate(dp);
+ return -EIO;
}
- return 0;
+ for (lane = 0; lane < lane_count; lane++)
+ exynos_dp_set_lane_link_training(dp,
+ dp->link_train.training_lane[lane], lane);
+
+ retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_TRAINING_LANE0_SET,
+ lane_count, dp->link_train.training_lane);
-reduce_link_rate:
- exynos_dp_reduce_link_rate(dp);
- return -EIO;
+ return retval;
}
static void exynos_dp_get_max_rx_bandwidth(struct exynos_dp_device *dp,
@@ -701,16 +676,17 @@ static void exynos_dp_init_training(struct exynos_dp_device *dp,
static int exynos_dp_sw_link_training(struct exynos_dp_device *dp)
{
- int retval = 0;
- int training_finished = 0;
+ int retval = 0, training_finished = 0;
dp->link_train.lt_state = START;
/* Process here */
- while (!training_finished) {
+ while (!retval && !training_finished) {
switch (dp->link_train.lt_state) {
case START:
- exynos_dp_link_start(dp);
+ retval = exynos_dp_link_start(dp);
+ if (retval)
+ dev_err(dp->dev, "LT link start failed!\n");
break;
case CLOCK_RECOVERY:
retval = exynos_dp_process_clock_recovery(dp);
@@ -729,6 +705,8 @@ static int exynos_dp_sw_link_training(struct exynos_dp_device *dp)
return -EREMOTEIO;
}
}
+ if (retval)
+ dev_err(dp->dev, "eDP link training failed (%d)\n", retval);
return retval;
}
@@ -752,19 +730,15 @@ static int exynos_dp_set_link_train(struct exynos_dp_device *dp,
return retval;
}
-static int exynos_dp_config_video(struct exynos_dp_device *dp,
- struct video_info *video_info)
+static int exynos_dp_config_video(struct exynos_dp_device *dp)
{
int retval = 0;
int timeout_loop = 0;
int done_count = 0;
- exynos_dp_config_video_slave_mode(dp, video_info);
+ exynos_dp_config_video_slave_mode(dp);
- exynos_dp_set_video_color_format(dp, video_info->color_depth,
- video_info->color_space,
- video_info->dynamic_range,
- video_info->ycbcr_coeff);
+ exynos_dp_set_video_color_format(dp);
if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
dev_err(dp->dev, "PLL is not locked yet.\n");
@@ -852,10 +826,213 @@ static irqreturn_t exynos_dp_irq_handler(int irq, void *arg)
{
struct exynos_dp_device *dp = arg;
- dev_err(dp->dev, "exynos_dp_irq_handler\n");
+ enum dp_irq_type irq_type;
+
+ irq_type = exynos_dp_get_irq_type(dp);
+ switch (irq_type) {
+ case DP_IRQ_TYPE_HP_CABLE_IN:
+ dev_dbg(dp->dev, "Received irq - cable in\n");
+ schedule_work(&dp->hotplug_work);
+ exynos_dp_clear_hotplug_interrupts(dp);
+ break;
+ case DP_IRQ_TYPE_HP_CABLE_OUT:
+ dev_dbg(dp->dev, "Received irq - cable out\n");
+ exynos_dp_clear_hotplug_interrupts(dp);
+ break;
+ case DP_IRQ_TYPE_HP_CHANGE:
+ /*
+ * We get these change notifications once in a while, but there
+ * is nothing we can do with them. Just ignore it for now and
+ * only handle cable changes.
+ */
+ dev_dbg(dp->dev, "Received irq - hotplug change; ignoring.\n");
+ exynos_dp_clear_hotplug_interrupts(dp);
+ break;
+ default:
+ dev_err(dp->dev, "Received irq - unknown type!\n");
+ break;
+ }
return IRQ_HANDLED;
}
+static void exynos_dp_hotplug(struct work_struct *work)
+{
+ struct exynos_dp_device *dp;
+ int ret;
+
+ dp = container_of(work, struct exynos_dp_device, hotplug_work);
+
+ ret = exynos_dp_detect_hpd(dp);
+ if (ret) {
+ /* Cable has been disconnected, we're done */
+ return;
+ }
+
+ ret = exynos_dp_handle_edid(dp);
+ if (ret) {
+ dev_err(dp->dev, "unable to handle edid\n");
+ return;
+ }
+
+ ret = exynos_dp_set_link_train(dp, dp->video_info->lane_count,
+ dp->video_info->link_rate);
+ if (ret) {
+ dev_err(dp->dev, "unable to do link train\n");
+ return;
+ }
+
+ exynos_dp_enable_scramble(dp, 1);
+ exynos_dp_enable_rx_to_enhanced_mode(dp, 1);
+ exynos_dp_enable_enhanced_mode(dp, 1);
+
+ exynos_dp_set_lane_count(dp, dp->video_info->lane_count);
+ exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate);
+
+ exynos_dp_init_video(dp);
+ ret = exynos_dp_config_video(dp);
+ if (ret)
+ dev_err(dp->dev, "unable to config video\n");
+}
+
+#ifdef CONFIG_OF
+static struct exynos_dp_platdata *exynos_dp_dt_parse_pdata(struct device *dev)
+{
+ struct device_node *dp_node = dev->of_node;
+ struct exynos_dp_platdata *pd;
+ struct video_info *dp_video_config;
+
+ pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+ if (!pd) {
+ dev_err(dev, "memory allocation for pdata failed\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ dp_video_config = devm_kzalloc(dev,
+ sizeof(*dp_video_config), GFP_KERNEL);
+
+ if (!dp_video_config) {
+ dev_err(dev, "memory allocation for video config failed\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ pd->video_info = dp_video_config;
+
+ dp_video_config->h_sync_polarity =
+ of_property_read_bool(dp_node, "hsync-active-high");
+
+ dp_video_config->v_sync_polarity =
+ of_property_read_bool(dp_node, "vsync-active-high");
+
+ dp_video_config->interlaced =
+ of_property_read_bool(dp_node, "interlaced");
+
+ if (of_property_read_u32(dp_node, "samsung,color-space",
+ &dp_video_config->color_space)) {
+ dev_err(dev, "failed to get color-space\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_u32(dp_node, "samsung,dynamic-range",
+ &dp_video_config->dynamic_range)) {
+ dev_err(dev, "failed to get dynamic-range\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_u32(dp_node, "samsung,ycbcr-coeff",
+ &dp_video_config->ycbcr_coeff)) {
+ dev_err(dev, "failed to get ycbcr-coeff\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_u32(dp_node, "samsung,color-depth",
+ &dp_video_config->color_depth)) {
+ dev_err(dev, "failed to get color-depth\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_u32(dp_node, "samsung,link-rate",
+ &dp_video_config->link_rate)) {
+ dev_err(dev, "failed to get link-rate\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_u32(dp_node, "samsung,lane-count",
+ &dp_video_config->lane_count)) {
+ dev_err(dev, "failed to get lane-count\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ return pd;
+}
+
+static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp)
+{
+ struct device_node *dp_phy_node;
+ u32 phy_base;
+
+ dp_phy_node = of_find_node_by_name(dp->dev->of_node, "dptx-phy");
+ if (!dp_phy_node) {
+ dev_err(dp->dev, "could not find dptx-phy node\n");
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32(dp_phy_node, "reg", &phy_base)) {
+ dev_err(dp->dev, "faild to get reg for dptx-phy\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(dp_phy_node, "samsung,enable-mask",
+ &dp->enable_mask)) {
+ dev_err(dp->dev, "faild to get enable-mask for dptx-phy\n");
+ return -EINVAL;
+ }
+
+ dp->phy_addr = ioremap(phy_base, SZ_4);
+ if (!dp->phy_addr) {
+ dev_err(dp->dev, "failed to ioremap dp-phy\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void exynos_dp_phy_init(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ reg = __raw_readl(dp->phy_addr);
+ reg |= dp->enable_mask;
+ __raw_writel(reg, dp->phy_addr);
+}
+
+static void exynos_dp_phy_exit(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ reg = __raw_readl(dp->phy_addr);
+ reg &= ~(dp->enable_mask);
+ __raw_writel(reg, dp->phy_addr);
+}
+#else
+static struct exynos_dp_platdata *exynos_dp_dt_parse_pdata(struct device *dev)
+{
+ return NULL;
+}
+
+static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp)
+{
+ return -EINVAL;
+}
+
+static void exynos_dp_phy_init(struct exynos_dp_device *dp)
+{
+ return;
+}
+
+static void exynos_dp_phy_exit(struct exynos_dp_device *dp)
+{
+ return;
+}
+#endif /* CONFIG_OF */
+
static int __devinit exynos_dp_probe(struct platform_device *pdev)
{
struct resource *res;
@@ -864,12 +1041,6 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev)
int ret = 0;
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data\n");
- return -EINVAL;
- }
-
dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
GFP_KERNEL);
if (!dp) {
@@ -879,6 +1050,22 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev)
dp->dev = &pdev->dev;
+ if (pdev->dev.of_node) {
+ pdata = exynos_dp_dt_parse_pdata(&pdev->dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+
+ ret = exynos_dp_dt_parse_phydata(dp);
+ if (ret)
+ return ret;
+ } else {
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -EINVAL;
+ }
+ }
+
dp->clock = devm_clk_get(&pdev->dev, "dp");
if (IS_ERR(dp->clock)) {
dev_err(&pdev->dev, "failed to get clock\n");
@@ -896,50 +1083,29 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev)
}
dp->irq = platform_get_irq(pdev, 0);
- if (!dp->irq) {
+ if (dp->irq == -ENXIO) {
dev_err(&pdev->dev, "failed to get irq\n");
return -ENODEV;
}
- ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler, 0,
- "exynos-dp", dp);
- if (ret) {
- dev_err(&pdev->dev, "failed to request irq\n");
- return ret;
- }
+ INIT_WORK(&dp->hotplug_work, exynos_dp_hotplug);
dp->video_info = pdata->video_info;
- if (pdata->phy_init)
- pdata->phy_init();
-
- exynos_dp_init_dp(dp);
-
- ret = exynos_dp_detect_hpd(dp);
- if (ret) {
- dev_err(&pdev->dev, "unable to detect hpd\n");
- return ret;
- }
- exynos_dp_handle_edid(dp);
-
- ret = exynos_dp_set_link_train(dp, dp->video_info->lane_count,
- dp->video_info->link_rate);
- if (ret) {
- dev_err(&pdev->dev, "unable to do link train\n");
- return ret;
+ if (pdev->dev.of_node) {
+ if (dp->phy_addr)
+ exynos_dp_phy_init(dp);
+ } else {
+ if (pdata->phy_init)
+ pdata->phy_init();
}
- exynos_dp_enable_scramble(dp, 1);
- exynos_dp_enable_rx_to_enhanced_mode(dp, 1);
- exynos_dp_enable_enhanced_mode(dp, 1);
-
- exynos_dp_set_lane_count(dp, dp->video_info->lane_count);
- exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate);
+ exynos_dp_init_dp(dp);
- exynos_dp_init_video(dp);
- ret = exynos_dp_config_video(dp, dp->video_info);
+ ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler, 0,
+ "exynos-dp", dp);
if (ret) {
- dev_err(&pdev->dev, "unable to config video\n");
+ dev_err(&pdev->dev, "failed to request irq\n");
return ret;
}
@@ -953,23 +1119,41 @@ static int __devexit exynos_dp_remove(struct platform_device *pdev)
struct exynos_dp_platdata *pdata = pdev->dev.platform_data;
struct exynos_dp_device *dp = platform_get_drvdata(pdev);
- if (pdata && pdata->phy_exit)
- pdata->phy_exit();
+ disable_irq(dp->irq);
+
+ if (work_pending(&dp->hotplug_work))
+ flush_work(&dp->hotplug_work);
+
+ if (pdev->dev.of_node) {
+ if (dp->phy_addr)
+ exynos_dp_phy_exit(dp);
+ } else {
+ if (pdata->phy_exit)
+ pdata->phy_exit();
+ }
clk_disable_unprepare(dp->clock);
+
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int exynos_dp_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct exynos_dp_platdata *pdata = pdev->dev.platform_data;
- struct exynos_dp_device *dp = platform_get_drvdata(pdev);
+ struct exynos_dp_platdata *pdata = dev->platform_data;
+ struct exynos_dp_device *dp = dev_get_drvdata(dev);
- if (pdata && pdata->phy_exit)
- pdata->phy_exit();
+ if (work_pending(&dp->hotplug_work))
+ flush_work(&dp->hotplug_work);
+
+ if (dev->of_node) {
+ if (dp->phy_addr)
+ exynos_dp_phy_exit(dp);
+ } else {
+ if (pdata->phy_exit)
+ pdata->phy_exit();
+ }
clk_disable_unprepare(dp->clock);
@@ -978,32 +1162,22 @@ static int exynos_dp_suspend(struct device *dev)
static int exynos_dp_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct exynos_dp_platdata *pdata = pdev->dev.platform_data;
- struct exynos_dp_device *dp = platform_get_drvdata(pdev);
+ struct exynos_dp_platdata *pdata = dev->platform_data;
+ struct exynos_dp_device *dp = dev_get_drvdata(dev);
- if (pdata && pdata->phy_init)
- pdata->phy_init();
+ if (dev->of_node) {
+ if (dp->phy_addr)
+ exynos_dp_phy_init(dp);
+ } else {
+ if (pdata->phy_init)
+ pdata->phy_init();
+ }
clk_prepare_enable(dp->clock);
exynos_dp_init_dp(dp);
- exynos_dp_detect_hpd(dp);
- exynos_dp_handle_edid(dp);
-
- exynos_dp_set_link_train(dp, dp->video_info->lane_count,
- dp->video_info->link_rate);
-
- exynos_dp_enable_scramble(dp, 1);
- exynos_dp_enable_rx_to_enhanced_mode(dp, 1);
- exynos_dp_enable_enhanced_mode(dp, 1);
-
- exynos_dp_set_lane_count(dp, dp->video_info->lane_count);
- exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate);
-
- exynos_dp_init_video(dp);
- exynos_dp_config_video(dp, dp->video_info);
+ enable_irq(dp->irq);
return 0;
}
@@ -1013,6 +1187,12 @@ static const struct dev_pm_ops exynos_dp_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(exynos_dp_suspend, exynos_dp_resume)
};
+static const struct of_device_id exynos_dp_match[] = {
+ { .compatible = "samsung,exynos5-dp" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, exynos_dp_match);
+
static struct platform_driver exynos_dp_driver = {
.probe = exynos_dp_probe,
.remove = __devexit_p(exynos_dp_remove),
@@ -1020,6 +1200,7 @@ static struct platform_driver exynos_dp_driver = {
.name = "exynos-dp",
.owner = THIS_MODULE,
.pm = &exynos_dp_pm_ops,
+ .of_match_table = of_match_ptr(exynos_dp_match),
},
};
diff --git a/drivers/video/exynos/exynos_dp_core.h b/drivers/video/exynos/exynos_dp_core.h
index 57b8a6531c0..6c567bbf2fb 100644
--- a/drivers/video/exynos/exynos_dp_core.h
+++ b/drivers/video/exynos/exynos_dp_core.h
@@ -13,6 +13,13 @@
#ifndef _EXYNOS_DP_CORE_H
#define _EXYNOS_DP_CORE_H
+enum dp_irq_type {
+ DP_IRQ_TYPE_HP_CABLE_IN,
+ DP_IRQ_TYPE_HP_CABLE_OUT,
+ DP_IRQ_TYPE_HP_CHANGE,
+ DP_IRQ_TYPE_UNKNOWN,
+};
+
struct link_train {
int eq_loop;
int cr_loop[4];
@@ -29,9 +36,12 @@ struct exynos_dp_device {
struct clk *clock;
unsigned int irq;
void __iomem *reg_base;
+ void __iomem *phy_addr;
+ unsigned int enable_mask;
struct video_info *video_info;
struct link_train link_train;
+ struct work_struct hotplug_work;
};
/* exynos_dp_reg.c */
@@ -50,6 +60,8 @@ void exynos_dp_set_analog_power_down(struct exynos_dp_device *dp,
bool enable);
void exynos_dp_init_analog_func(struct exynos_dp_device *dp);
void exynos_dp_init_hpd(struct exynos_dp_device *dp);
+enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp);
+void exynos_dp_clear_hotplug_interrupts(struct exynos_dp_device *dp);
void exynos_dp_reset_aux(struct exynos_dp_device *dp);
void exynos_dp_init_aux(struct exynos_dp_device *dp);
int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp);
@@ -107,11 +119,7 @@ u32 exynos_dp_get_lane3_link_training(struct exynos_dp_device *dp);
void exynos_dp_reset_macro(struct exynos_dp_device *dp);
void exynos_dp_init_video(struct exynos_dp_device *dp);
-void exynos_dp_set_video_color_format(struct exynos_dp_device *dp,
- u32 color_depth,
- u32 color_space,
- u32 dynamic_range,
- u32 ycbcr_coeff);
+void exynos_dp_set_video_color_format(struct exynos_dp_device *dp);
int exynos_dp_is_slave_video_stream_clock_on(struct exynos_dp_device *dp);
void exynos_dp_set_video_cr_mn(struct exynos_dp_device *dp,
enum clock_recovery_m_value_type type,
@@ -121,8 +129,7 @@ void exynos_dp_set_video_timing_mode(struct exynos_dp_device *dp, u32 type);
void exynos_dp_enable_video_master(struct exynos_dp_device *dp, bool enable);
void exynos_dp_start_video(struct exynos_dp_device *dp);
int exynos_dp_is_video_stream_on(struct exynos_dp_device *dp);
-void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp,
- struct video_info *video_info);
+void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp);
void exynos_dp_enable_scrambling(struct exynos_dp_device *dp);
void exynos_dp_disable_scrambling(struct exynos_dp_device *dp);
diff --git a/drivers/video/exynos/exynos_dp_reg.c b/drivers/video/exynos/exynos_dp_reg.c
index 3f5ca8a0d5e..29d9d035c73 100644
--- a/drivers/video/exynos/exynos_dp_reg.c
+++ b/drivers/video/exynos/exynos_dp_reg.c
@@ -19,11 +19,11 @@
#include "exynos_dp_core.h"
#include "exynos_dp_reg.h"
-#define COMMON_INT_MASK_1 (0)
-#define COMMON_INT_MASK_2 (0)
-#define COMMON_INT_MASK_3 (0)
-#define COMMON_INT_MASK_4 (0)
-#define INT_STA_MASK (0)
+#define COMMON_INT_MASK_1 0
+#define COMMON_INT_MASK_2 0
+#define COMMON_INT_MASK_3 0
+#define COMMON_INT_MASK_4 (HOTPLUG_CHG | HPD_LOST | PLUG)
+#define INT_STA_MASK INT_HPD
void exynos_dp_enable_video_mute(struct exynos_dp_device *dp, bool enable)
{
@@ -88,7 +88,7 @@ void exynos_dp_init_analog_param(struct exynos_dp_device *dp)
void exynos_dp_init_interrupt(struct exynos_dp_device *dp)
{
/* Set interrupt pin assertion polarity as high */
- writel(INT_POL, dp->reg_base + EXYNOS_DP_INT_CTL);
+ writel(INT_POL1 | INT_POL0, dp->reg_base + EXYNOS_DP_INT_CTL);
/* Clear pending regisers */
writel(0xff, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_1);
@@ -324,7 +324,7 @@ void exynos_dp_init_analog_func(struct exynos_dp_device *dp)
writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2);
}
-void exynos_dp_init_hpd(struct exynos_dp_device *dp)
+void exynos_dp_clear_hotplug_interrupts(struct exynos_dp_device *dp)
{
u32 reg;
@@ -333,12 +333,38 @@ void exynos_dp_init_hpd(struct exynos_dp_device *dp)
reg = INT_HPD;
writel(reg, dp->reg_base + EXYNOS_DP_INT_STA);
+}
+
+void exynos_dp_init_hpd(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ exynos_dp_clear_hotplug_interrupts(dp);
reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
reg &= ~(F_HPD | HPD_CTRL);
writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_3);
}
+enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ /* Parse hotplug interrupt status register */
+ reg = readl(dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
+
+ if (reg & PLUG)
+ return DP_IRQ_TYPE_HP_CABLE_IN;
+
+ if (reg & HPD_LOST)
+ return DP_IRQ_TYPE_HP_CABLE_OUT;
+
+ if (reg & HOTPLUG_CHG)
+ return DP_IRQ_TYPE_HP_CHANGE;
+
+ return DP_IRQ_TYPE_UNKNOWN;
+}
+
void exynos_dp_reset_aux(struct exynos_dp_device *dp)
{
u32 reg;
@@ -491,7 +517,7 @@ int exynos_dp_read_byte_from_dpcd(struct exynos_dp_device *dp,
int i;
int retval;
- for (i = 0; i < 10; i++) {
+ for (i = 0; i < 3; i++) {
/* Clear AUX CH data buffer */
reg = BUF_CLR;
writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
@@ -552,7 +578,7 @@ int exynos_dp_write_bytes_to_dpcd(struct exynos_dp_device *dp,
else
cur_data_count = count - start_offset;
- for (i = 0; i < 10; i++) {
+ for (i = 0; i < 3; i++) {
/* Select DPCD device address */
reg = AUX_ADDR_7_0(reg_addr + start_offset);
writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0);
@@ -617,7 +643,7 @@ int exynos_dp_read_bytes_from_dpcd(struct exynos_dp_device *dp,
cur_data_count = count - start_offset;
/* AUX CH Request Transaction process */
- for (i = 0; i < 10; i++) {
+ for (i = 0; i < 3; i++) {
/* Select DPCD device address */
reg = AUX_ADDR_7_0(reg_addr + start_offset);
writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0);
@@ -700,17 +726,15 @@ int exynos_dp_read_byte_from_i2c(struct exynos_dp_device *dp,
int i;
int retval;
- for (i = 0; i < 10; i++) {
+ for (i = 0; i < 3; i++) {
/* Clear AUX CH data buffer */
reg = BUF_CLR;
writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
/* Select EDID device */
retval = exynos_dp_select_i2c_device(dp, device_addr, reg_addr);
- if (retval != 0) {
- dev_err(dp->dev, "Select EDID device fail!\n");
+ if (retval != 0)
continue;
- }
/*
* Set I2C transaction and read data
@@ -750,7 +774,7 @@ int exynos_dp_read_bytes_from_i2c(struct exynos_dp_device *dp,
int retval = 0;
for (i = 0; i < count; i += 16) {
- for (j = 0; j < 100; j++) {
+ for (j = 0; j < 3; j++) {
/* Clear AUX CH data buffer */
reg = BUF_CLR;
writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
@@ -1034,24 +1058,20 @@ void exynos_dp_init_video(struct exynos_dp_device *dp)
writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_8);
}
-void exynos_dp_set_video_color_format(struct exynos_dp_device *dp,
- u32 color_depth,
- u32 color_space,
- u32 dynamic_range,
- u32 ycbcr_coeff)
+void exynos_dp_set_video_color_format(struct exynos_dp_device *dp)
{
u32 reg;
/* Configure the input color depth, color space, dynamic range */
- reg = (dynamic_range << IN_D_RANGE_SHIFT) |
- (color_depth << IN_BPC_SHIFT) |
- (color_space << IN_COLOR_F_SHIFT);
+ reg = (dp->video_info->dynamic_range << IN_D_RANGE_SHIFT) |
+ (dp->video_info->color_depth << IN_BPC_SHIFT) |
+ (dp->video_info->color_space << IN_COLOR_F_SHIFT);
writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_2);
/* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */
reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_3);
reg &= ~IN_YC_COEFFI_MASK;
- if (ycbcr_coeff)
+ if (dp->video_info->ycbcr_coeff)
reg |= IN_YC_COEFFI_ITU709;
else
reg |= IN_YC_COEFFI_ITU601;
@@ -1178,8 +1198,7 @@ int exynos_dp_is_video_stream_on(struct exynos_dp_device *dp)
return 0;
}
-void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp,
- struct video_info *video_info)
+void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp)
{
u32 reg;
@@ -1190,17 +1209,17 @@ void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp,
reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
reg &= ~INTERACE_SCAN_CFG;
- reg |= (video_info->interlaced << 2);
+ reg |= (dp->video_info->interlaced << 2);
writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
reg &= ~VSYNC_POLARITY_CFG;
- reg |= (video_info->v_sync_polarity << 1);
+ reg |= (dp->video_info->v_sync_polarity << 1);
writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
reg &= ~HSYNC_POLARITY_CFG;
- reg |= (video_info->h_sync_polarity << 0);
+ reg |= (dp->video_info->h_sync_polarity << 0);
writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE;
diff --git a/drivers/video/exynos/exynos_dp_reg.h b/drivers/video/exynos/exynos_dp_reg.h
index 1f2f014cfe8..2e9bd0e0b9f 100644
--- a/drivers/video/exynos/exynos_dp_reg.h
+++ b/drivers/video/exynos/exynos_dp_reg.h
@@ -242,7 +242,8 @@
/* EXYNOS_DP_INT_CTL */
#define SOFT_INT_CTRL (0x1 << 2)
-#define INT_POL (0x1 << 0)
+#define INT_POL1 (0x1 << 1)
+#define INT_POL0 (0x1 << 0)
/* EXYNOS_DP_SYS_CTL_1 */
#define DET_STA (0x1 << 2)
diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c
index ede9e55413f..d3fc92eaee8 100644
--- a/drivers/video/fsl-diu-fb.c
+++ b/drivers/video/fsl-diu-fb.c
@@ -337,13 +337,11 @@ struct mfb_info {
int registered;
unsigned long pseudo_palette[16];
struct diu_ad *ad;
- int cursor_reset;
unsigned char g_alpha;
unsigned int count;
int x_aoi_d; /* aoi display x offset to physical screen */
int y_aoi_d; /* aoi display y offset to physical screen */
struct fsl_diu_data *parent;
- u8 *edid_data;
};
/**
@@ -378,6 +376,8 @@ struct fsl_diu_data {
struct diu_ad ad[NUM_AOIS] __aligned(8);
u8 gamma[256 * 3] __aligned(32);
u8 cursor[MAX_CURS * MAX_CURS * 2] __aligned(32);
+ uint8_t edid_data[EDID_LENGTH];
+ bool has_edid;
} __aligned(32);
/* Determine the DMA address of a member of the fsl_diu_data structure */
@@ -430,6 +430,22 @@ static struct mfb_info mfb_template[] = {
},
};
+#ifdef DEBUG
+static void __attribute__ ((unused)) fsl_diu_dump(struct diu __iomem *hw)
+{
+ mb();
+ pr_debug("DIU: desc=%08x,%08x,%08x, gamma=%08x pallete=%08x "
+ "cursor=%08x curs_pos=%08x diu_mode=%08x bgnd=%08x "
+ "disp_size=%08x hsyn_para=%08x vsyn_para=%08x syn_pol=%08x "
+ "thresholds=%08x int_mask=%08x plut=%08x\n",
+ hw->desc[0], hw->desc[1], hw->desc[2], hw->gamma,
+ hw->pallete, hw->cursor, hw->curs_pos, hw->diu_mode,
+ hw->bgnd, hw->disp_size, hw->hsyn_para, hw->vsyn_para,
+ hw->syn_pol, hw->thresholds, hw->int_mask, hw->plut);
+ rmb();
+}
+#endif
+
/**
* fsl_diu_name_to_port - convert a port name to a monitor port enum
*
@@ -481,8 +497,7 @@ static void fsl_diu_enable_panel(struct fb_info *info)
switch (mfbi->index) {
case PLANE0:
- if (hw->desc[0] != ad->paddr)
- wr_reg_wa(&hw->desc[0], ad->paddr);
+ wr_reg_wa(&hw->desc[0], ad->paddr);
break;
case PLANE1_AOI0:
cmfbi = &data->mfb[2];
@@ -534,8 +549,7 @@ static void fsl_diu_disable_panel(struct fb_info *info)
switch (mfbi->index) {
case PLANE0:
- if (hw->desc[0] != data->dummy_ad.paddr)
- wr_reg_wa(&hw->desc[0], data->dummy_ad.paddr);
+ wr_reg_wa(&hw->desc[0], 0);
break;
case PLANE1_AOI0:
cmfbi = &data->mfb[2];
@@ -792,7 +806,8 @@ static void update_lcdc(struct fb_info *info)
hw = data->diu_reg;
- diu_ops.set_monitor_port(data->monitor_port);
+ if (diu_ops.set_monitor_port)
+ diu_ops.set_monitor_port(data->monitor_port);
gamma_table_base = data->gamma;
/* Prep for DIU init - gamma table, cursor table */
@@ -811,12 +826,8 @@ static void update_lcdc(struct fb_info *info)
out_be32(&hw->gamma, DMA_ADDR(data, gamma));
out_be32(&hw->cursor, DMA_ADDR(data, cursor));
- out_be32(&hw->bgnd, 0x007F7F7F); /* BGND */
- out_be32(&hw->bgnd_wb, 0); /* BGND_WB */
- out_be32(&hw->disp_size, (var->yres << 16 | var->xres));
- /* DISP SIZE */
- out_be32(&hw->wb_size, 0); /* WB SIZE */
- out_be32(&hw->wb_mem_addr, 0); /* WB MEM ADDR */
+ out_be32(&hw->bgnd, 0x007F7F7F); /* Set background to grey */
+ out_be32(&hw->disp_size, (var->yres << 16) | var->xres);
/* Horizontal and vertical configuration register */
temp = var->left_margin << 22 | /* BP_H */
@@ -833,9 +844,20 @@ static void update_lcdc(struct fb_info *info)
diu_ops.set_pixel_clock(var->pixclock);
- out_be32(&hw->syn_pol, 0); /* SYNC SIGNALS POLARITY */
- out_be32(&hw->int_status, 0); /* INTERRUPT STATUS */
+#ifndef CONFIG_PPC_MPC512x
+ /*
+ * The PLUT register is defined differently on the MPC5121 than it
+ * is on other SOCs. Unfortunately, there's no documentation that
+ * explains how it's supposed to be programmed, so for now, we leave
+ * it at the default value on the MPC5121.
+ *
+ * For other SOCs, program it for the highest priority, which will
+ * reduce the chance of underrun. Technically, we should scale the
+ * priority to match the screen resolution, but doing that properly
+ * requires delicate fine-tuning for each use-case.
+ */
out_be32(&hw->plut, 0x01F5F666);
+#endif
/* Enable the DIU */
enable_lcdc(info);
@@ -965,7 +987,6 @@ static int fsl_diu_set_par(struct fb_info *info)
hw = data->diu_reg;
set_fix(info);
- mfbi->cursor_reset = 1;
len = info->var.yres_virtual * info->fix.line_length;
/* Alloc & dealloc each time resolution/bpp change */
@@ -1107,6 +1128,12 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd,
if (!arg)
return -EINVAL;
+
+ dev_dbg(info->dev, "ioctl %08x (dir=%s%s type=%u nr=%u size=%u)\n", cmd,
+ _IOC_DIR(cmd) & _IOC_READ ? "R" : "",
+ _IOC_DIR(cmd) & _IOC_WRITE ? "W" : "",
+ _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd));
+
switch (cmd) {
case MFB_SET_PIXFMT_OLD:
dev_warn(info->dev,
@@ -1180,6 +1207,23 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd,
ad->ckmin_b = ck.blue_min;
}
break;
+#ifdef CONFIG_PPC_MPC512x
+ case MFB_SET_GAMMA: {
+ struct fsl_diu_data *data = mfbi->parent;
+
+ if (copy_from_user(data->gamma, buf, sizeof(data->gamma)))
+ return -EFAULT;
+ setbits32(&data->diu_reg->gamma, 0); /* Force table reload */
+ break;
+ }
+ case MFB_GET_GAMMA: {
+ struct fsl_diu_data *data = mfbi->parent;
+
+ if (copy_to_user(buf, data->gamma, sizeof(data->gamma)))
+ return -EFAULT;
+ break;
+ }
+#endif
default:
dev_err(info->dev, "unknown ioctl command (0x%08X)\n", cmd);
return -ENOIOCTLCMD;
@@ -1206,8 +1250,22 @@ static int fsl_diu_open(struct fb_info *info, int user)
res = fsl_diu_set_par(info);
if (res < 0)
mfbi->count--;
- else
+ else {
+ struct fsl_diu_data *data = mfbi->parent;
+
+#ifdef CONFIG_NOT_COHERENT_CACHE
+ /*
+ * Enable underrun detection and vertical sync
+ * interrupts.
+ */
+ clrbits32(&data->diu_reg->int_mask,
+ INT_UNDRUN | INT_VSYNC);
+#else
+ /* Enable underrun detection */
+ clrbits32(&data->diu_reg->int_mask, INT_UNDRUN);
+#endif
fsl_diu_enable_panel(info);
+ }
}
spin_unlock(&diu_lock);
@@ -1223,8 +1281,13 @@ static int fsl_diu_release(struct fb_info *info, int user)
spin_lock(&diu_lock);
mfbi->count--;
- if (mfbi->count == 0)
+ if (mfbi->count == 0) {
+ struct fsl_diu_data *data = mfbi->parent;
+
+ /* Disable interrupts */
+ out_be32(&data->diu_reg->int_mask, 0xffffffff);
fsl_diu_disable_panel(info);
+ }
spin_unlock(&diu_lock);
return res;
@@ -1248,6 +1311,7 @@ static int __devinit install_fb(struct fb_info *info)
{
int rc;
struct mfb_info *mfbi = info->par;
+ struct fsl_diu_data *data = mfbi->parent;
const char *aoi_mode, *init_aoi_mode = "320x240";
struct fb_videomode *db = fsl_diu_mode_db;
unsigned int dbsize = ARRAY_SIZE(fsl_diu_mode_db);
@@ -1264,9 +1328,9 @@ static int __devinit install_fb(struct fb_info *info)
return rc;
if (mfbi->index == PLANE0) {
- if (mfbi->edid_data) {
+ if (data->has_edid) {
/* Now build modedb from EDID */
- fb_edid_to_monspecs(mfbi->edid_data, &info->monspecs);
+ fb_edid_to_monspecs(data->edid_data, &info->monspecs);
fb_videomode_to_modelist(info->monspecs.modedb,
info->monspecs.modedb_len,
&info->modelist);
@@ -1284,7 +1348,7 @@ static int __devinit install_fb(struct fb_info *info)
* For plane 0 we continue and look into
* driver's internal modedb.
*/
- if ((mfbi->index == PLANE0) && mfbi->edid_data)
+ if ((mfbi->index == PLANE0) && data->has_edid)
has_default_mode = 0;
else
return -EINVAL;
@@ -1348,9 +1412,6 @@ static void uninstall_fb(struct fb_info *info)
if (!mfbi->registered)
return;
- if (mfbi->index == PLANE0)
- kfree(mfbi->edid_data);
-
unregister_framebuffer(info);
unmap_video_memory(info);
if (&info->cmap)
@@ -1362,7 +1423,7 @@ static void uninstall_fb(struct fb_info *info)
static irqreturn_t fsl_diu_isr(int irq, void *dev_id)
{
struct diu __iomem *hw = dev_id;
- unsigned int status = in_be32(&hw->int_status);
+ uint32_t status = in_be32(&hw->int_status);
if (status) {
/* This is the workaround for underrun */
@@ -1387,40 +1448,6 @@ static irqreturn_t fsl_diu_isr(int irq, void *dev_id)
return IRQ_NONE;
}
-static int request_irq_local(struct fsl_diu_data *data)
-{
- struct diu __iomem *hw = data->diu_reg;
- u32 ints;
- int ret;
-
- /* Read to clear the status */
- in_be32(&hw->int_status);
-
- ret = request_irq(data->irq, fsl_diu_isr, 0, "fsl-diu-fb", hw);
- if (!ret) {
- ints = INT_PARERR | INT_LS_BF_VS;
-#if !defined(CONFIG_NOT_COHERENT_CACHE)
- ints |= INT_VSYNC;
-#endif
-
- /* Read to clear the status */
- in_be32(&hw->int_status);
- out_be32(&hw->int_mask, ints);
- }
-
- return ret;
-}
-
-static void free_irq_local(struct fsl_diu_data *data)
-{
- struct diu __iomem *hw = data->diu_reg;
-
- /* Disable all LCDC interrupt */
- out_be32(&hw->int_mask, 0x1f);
-
- free_irq(data->irq, NULL);
-}
-
#ifdef CONFIG_PM
/*
* Power management hooks. Note that we won't be called from IRQ context,
@@ -1496,8 +1523,8 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
struct mfb_info *mfbi;
struct fsl_diu_data *data;
- int diu_mode;
dma_addr_t dma_addr; /* DMA addr of fsl_diu_data struct */
+ const void *prop;
unsigned int i;
int ret;
@@ -1541,17 +1568,13 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info));
mfbi->parent = data;
mfbi->ad = &data->ad[i];
+ }
- if (mfbi->index == PLANE0) {
- const u8 *prop;
- int len;
-
- /* Get EDID */
- prop = of_get_property(np, "edid", &len);
- if (prop && len == EDID_LENGTH)
- mfbi->edid_data = kmemdup(prop, EDID_LENGTH,
- GFP_KERNEL);
- }
+ /* Get the EDID data from the device tree, if present */
+ prop = of_get_property(np, "edid", &ret);
+ if (prop && ret == EDID_LENGTH) {
+ memcpy(data->edid_data, prop, EDID_LENGTH);
+ data->has_edid = true;
}
data->diu_reg = of_iomap(np, 0);
@@ -1561,10 +1584,6 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
goto error;
}
- diu_mode = in_be32(&data->diu_reg->diu_mode);
- if (diu_mode == MFB_MODE0)
- out_be32(&data->diu_reg->diu_mode, 0); /* disable DIU */
-
/* Get the IRQ of the DIU */
data->irq = irq_of_parse_and_map(np, 0);
@@ -1586,11 +1605,11 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
data->dummy_ad.paddr = DMA_ADDR(data, dummy_ad);
/*
- * Let DIU display splash screen if it was pre-initialized
- * by the bootloader, set dummy area descriptor otherwise.
+ * Let DIU continue to display splash screen if it was pre-initialized
+ * by the bootloader; otherwise, clear the display.
*/
- if (diu_mode == MFB_MODE0)
- out_be32(&data->diu_reg->desc[0], data->dummy_ad.paddr);
+ if (in_be32(&data->diu_reg->diu_mode) == MFB_MODE0)
+ out_be32(&data->diu_reg->desc[0], 0);
out_be32(&data->diu_reg->desc[1], data->dummy_ad.paddr);
out_be32(&data->diu_reg->desc[2], data->dummy_ad.paddr);
@@ -1603,7 +1622,16 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
}
}
- if (request_irq_local(data)) {
+ /*
+ * Older versions of U-Boot leave interrupts enabled, so disable
+ * all of them and clear the status register.
+ */
+ out_be32(&data->diu_reg->int_mask, 0xffffffff);
+ in_be32(&data->diu_reg->int_status);
+
+ ret = request_irq(data->irq, fsl_diu_isr, 0, "fsl-diu-fb",
+ &data->diu_reg);
+ if (ret) {
dev_err(&pdev->dev, "could not claim irq\n");
goto error;
}
@@ -1638,7 +1666,8 @@ static int fsl_diu_remove(struct platform_device *pdev)
data = dev_get_drvdata(&pdev->dev);
disable_lcdc(&data->fsl_diu_info[0]);
- free_irq_local(data);
+
+ free_irq(data->irq, &data->diu_reg);
for (i = 0; i < NUM_AOIS; i++)
uninstall_fb(&data->fsl_diu_info[i]);
@@ -1741,6 +1770,9 @@ static int __init fsl_diu_init(void)
coherence_data_size = be32_to_cpup(prop) * 13;
coherence_data_size /= 8;
+ pr_debug("fsl-diu-fb: coherence data size is %zu bytes\n",
+ coherence_data_size);
+
prop = of_get_property(np, "d-cache-line-size", NULL);
if (prop == NULL) {
pr_err("fsl-diu-fb: missing 'd-cache-line-size' property' "
@@ -1750,10 +1782,17 @@ static int __init fsl_diu_init(void)
}
d_cache_line_size = be32_to_cpup(prop);
+ pr_debug("fsl-diu-fb: cache lines size is %u bytes\n",
+ d_cache_line_size);
+
of_node_put(np);
coherence_data = vmalloc(coherence_data_size);
- if (!coherence_data)
+ if (!coherence_data) {
+ pr_err("fsl-diu-fb: could not allocate coherence data "
+ "(size=%zu)\n", coherence_data_size);
return -ENOMEM;
+ }
+
#endif
ret = platform_driver_register(&fsl_diu_driver);
diff --git a/drivers/video/omap2/Kconfig b/drivers/video/omap2/Kconfig
index d877c361abd..346d67d6cf4 100644
--- a/drivers/video/omap2/Kconfig
+++ b/drivers/video/omap2/Kconfig
@@ -1,6 +1,3 @@
-config OMAP2_VRAM
- bool
-
config OMAP2_VRFB
bool
diff --git a/drivers/video/omap2/Makefile b/drivers/video/omap2/Makefile
index 5ddef129f79..5ea7cb9aed1 100644
--- a/drivers/video/omap2/Makefile
+++ b/drivers/video/omap2/Makefile
@@ -1,4 +1,3 @@
-obj-$(CONFIG_OMAP2_VRAM) += vram.o
obj-$(CONFIG_OMAP2_VRFB) += vrfb.o
obj-$(CONFIG_OMAP2_DSS) += dss/
diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c
index c835aa70f96..65eb76c840a 100644
--- a/drivers/video/omap2/displays/panel-acx565akm.c
+++ b/drivers/video/omap2/displays/panel-acx565akm.c
@@ -710,27 +710,6 @@ static void acx_panel_disable(struct omap_dss_device *dssdev)
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
-static int acx_panel_suspend(struct omap_dss_device *dssdev)
-{
- dev_dbg(&dssdev->dev, "%s\n", __func__);
- acx_panel_power_off(dssdev);
- dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
- return 0;
-}
-
-static int acx_panel_resume(struct omap_dss_device *dssdev)
-{
- int r;
-
- dev_dbg(&dssdev->dev, "%s\n", __func__);
- r = acx_panel_power_on(dssdev);
- if (r)
- return r;
-
- dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
- return 0;
-}
-
static void acx_panel_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
@@ -752,8 +731,6 @@ static struct omap_dss_driver acx_panel_driver = {
.enable = acx_panel_enable,
.disable = acx_panel_disable,
- .suspend = acx_panel_suspend,
- .resume = acx_panel_resume,
.set_timings = acx_panel_set_timings,
.check_timings = acx_panel_check_timings,
diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c
index 88295c52681..54ca8ae2107 100644
--- a/drivers/video/omap2/displays/panel-generic-dpi.c
+++ b/drivers/video/omap2/displays/panel-generic-dpi.c
@@ -688,40 +688,6 @@ static void generic_dpi_panel_disable(struct omap_dss_device *dssdev)
mutex_unlock(&drv_data->lock);
}
-static int generic_dpi_panel_suspend(struct omap_dss_device *dssdev)
-{
- struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
-
- mutex_lock(&drv_data->lock);
-
- generic_dpi_panel_power_off(dssdev);
-
- dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
-
- mutex_unlock(&drv_data->lock);
-
- return 0;
-}
-
-static int generic_dpi_panel_resume(struct omap_dss_device *dssdev)
-{
- struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev);
- int r;
-
- mutex_lock(&drv_data->lock);
-
- r = generic_dpi_panel_power_on(dssdev);
- if (r)
- goto err;
-
- dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
-err:
- mutex_unlock(&drv_data->lock);
-
- return r;
-}
-
static void generic_dpi_panel_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
@@ -769,8 +735,6 @@ static struct omap_dss_driver dpi_driver = {
.enable = generic_dpi_panel_enable,
.disable = generic_dpi_panel_disable,
- .suspend = generic_dpi_panel_suspend,
- .resume = generic_dpi_panel_resume,
.set_timings = generic_dpi_panel_set_timings,
.get_timings = generic_dpi_panel_get_timings,
diff --git a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c
index 90c1cabf244..ace419b801e 100644
--- a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c
+++ b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c
@@ -143,46 +143,12 @@ static void lb035q02_panel_disable(struct omap_dss_device *dssdev)
mutex_unlock(&ld->lock);
}
-static int lb035q02_panel_suspend(struct omap_dss_device *dssdev)
-{
- struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev);
-
- mutex_lock(&ld->lock);
-
- lb035q02_panel_power_off(dssdev);
- dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
-
- mutex_unlock(&ld->lock);
- return 0;
-}
-
-static int lb035q02_panel_resume(struct omap_dss_device *dssdev)
-{
- struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev);
- int r;
-
- mutex_lock(&ld->lock);
-
- r = lb035q02_panel_power_on(dssdev);
- if (r)
- goto err;
- dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
- mutex_unlock(&ld->lock);
- return 0;
-err:
- mutex_unlock(&ld->lock);
- return r;
-}
-
static struct omap_dss_driver lb035q02_driver = {
.probe = lb035q02_panel_probe,
.remove = lb035q02_panel_remove,
.enable = lb035q02_panel_enable,
.disable = lb035q02_panel_disable,
- .suspend = lb035q02_panel_suspend,
- .resume = lb035q02_panel_resume,
.driver = {
.name = "lgphilips_lb035q02_panel",
diff --git a/drivers/video/omap2/displays/panel-n8x0.c b/drivers/video/omap2/displays/panel-n8x0.c
index 3fc5ad081a2..d1cb722fcdb 100644
--- a/drivers/video/omap2/displays/panel-n8x0.c
+++ b/drivers/video/omap2/displays/panel-n8x0.c
@@ -574,54 +574,6 @@ static void n8x0_panel_disable(struct omap_dss_device *dssdev)
mutex_unlock(&ddata->lock);
}
-static int n8x0_panel_suspend(struct omap_dss_device *dssdev)
-{
- struct panel_drv_data *ddata = get_drv_data(dssdev);
-
- dev_dbg(&dssdev->dev, "suspend\n");
-
- mutex_lock(&ddata->lock);
-
- rfbi_bus_lock();
-
- n8x0_panel_power_off(dssdev);
-
- rfbi_bus_unlock();
-
- dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
-
- mutex_unlock(&ddata->lock);
-
- return 0;
-}
-
-static int n8x0_panel_resume(struct omap_dss_device *dssdev)
-{
- struct panel_drv_data *ddata = get_drv_data(dssdev);
- int r;
-
- dev_dbg(&dssdev->dev, "resume\n");
-
- mutex_lock(&ddata->lock);
-
- rfbi_bus_lock();
-
- r = n8x0_panel_power_on(dssdev);
-
- rfbi_bus_unlock();
-
- if (r) {
- mutex_unlock(&ddata->lock);
- return r;
- }
-
- dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
- mutex_unlock(&ddata->lock);
-
- return 0;
-}
-
static void n8x0_panel_get_resolution(struct omap_dss_device *dssdev,
u16 *xres, u16 *yres)
{
@@ -683,8 +635,6 @@ static struct omap_dss_driver n8x0_panel_driver = {
.enable = n8x0_panel_enable,
.disable = n8x0_panel_disable,
- .suspend = n8x0_panel_suspend,
- .resume = n8x0_panel_resume,
.update = n8x0_panel_update,
.sync = n8x0_panel_sync,
@@ -702,18 +652,25 @@ static struct omap_dss_driver n8x0_panel_driver = {
static int mipid_spi_probe(struct spi_device *spi)
{
+ int r;
+
dev_dbg(&spi->dev, "mipid_spi_probe\n");
spi->mode = SPI_MODE_0;
s_drv_data.spidev = spi;
- return 0;
+ r = omap_dss_register_driver(&n8x0_panel_driver);
+ if (r)
+ pr_err("n8x0_panel: dss driver registration failed\n");
+
+ return r;
}
static int mipid_spi_remove(struct spi_device *spi)
{
dev_dbg(&spi->dev, "mipid_spi_remove\n");
+ omap_dss_unregister_driver(&n8x0_panel_driver);
return 0;
}
@@ -725,34 +682,6 @@ static struct spi_driver mipid_spi_driver = {
.probe = mipid_spi_probe,
.remove = __devexit_p(mipid_spi_remove),
};
+module_spi_driver(mipid_spi_driver);
-static int __init n8x0_panel_drv_init(void)
-{
- int r;
-
- r = spi_register_driver(&mipid_spi_driver);
- if (r) {
- pr_err("n8x0_panel: spi driver registration failed\n");
- return r;
- }
-
- r = omap_dss_register_driver(&n8x0_panel_driver);
- if (r) {
- pr_err("n8x0_panel: dss driver registration failed\n");
- spi_unregister_driver(&mipid_spi_driver);
- return r;
- }
-
- return 0;
-}
-
-static void __exit n8x0_panel_drv_exit(void)
-{
- spi_unregister_driver(&mipid_spi_driver);
-
- omap_dss_unregister_driver(&n8x0_panel_driver);
-}
-
-module_init(n8x0_panel_drv_init);
-module_exit(n8x0_panel_drv_exit);
MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c
index 908fd268f3d..2a79c283beb 100644
--- a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c
+++ b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c
@@ -236,28 +236,6 @@ static void nec_8048_panel_disable(struct omap_dss_device *dssdev)
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
-static int nec_8048_panel_suspend(struct omap_dss_device *dssdev)
-{
- nec_8048_panel_power_off(dssdev);
-
- dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
-
- return 0;
-}
-
-static int nec_8048_panel_resume(struct omap_dss_device *dssdev)
-{
- int r;
-
- r = nec_8048_panel_power_on(dssdev);
- if (r)
- return r;
-
- dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
- return 0;
-}
-
static int nec_8048_recommended_bpp(struct omap_dss_device *dssdev)
{
return 16;
@@ -268,8 +246,6 @@ static struct omap_dss_driver nec_8048_driver = {
.remove = nec_8048_panel_remove,
.enable = nec_8048_panel_enable,
.disable = nec_8048_panel_disable,
- .suspend = nec_8048_panel_suspend,
- .resume = nec_8048_panel_resume,
.get_recommended_bpp = nec_8048_recommended_bpp,
.driver = {
diff --git a/drivers/video/omap2/displays/panel-picodlp.c b/drivers/video/omap2/displays/panel-picodlp.c
index 9df87640ddd..1b94018aac3 100644
--- a/drivers/video/omap2/displays/panel-picodlp.c
+++ b/drivers/video/omap2/displays/panel-picodlp.c
@@ -50,6 +50,7 @@ struct picodlp_i2c_data {
static struct i2c_device_id picodlp_i2c_id[] = {
{ "picodlp_i2c_driver", 0 },
+ { }
};
struct picodlp_i2c_command {
@@ -503,47 +504,6 @@ static void picodlp_panel_disable(struct omap_dss_device *dssdev)
dev_dbg(&dssdev->dev, "disabling picodlp panel\n");
}
-static int picodlp_panel_suspend(struct omap_dss_device *dssdev)
-{
- struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev);
-
- mutex_lock(&picod->lock);
- /* Turn off DLP Power */
- if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
- mutex_unlock(&picod->lock);
- dev_err(&dssdev->dev, "unable to suspend picodlp panel,"
- " panel is not ACTIVE\n");
- return -EINVAL;
- }
-
- picodlp_panel_power_off(dssdev);
-
- dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
- mutex_unlock(&picod->lock);
-
- dev_dbg(&dssdev->dev, "suspending picodlp panel\n");
- return 0;
-}
-
-static int picodlp_panel_resume(struct omap_dss_device *dssdev)
-{
- struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev);
- int r;
-
- mutex_lock(&picod->lock);
- if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
- mutex_unlock(&picod->lock);
- dev_err(&dssdev->dev, "unable to resume picodlp panel,"
- " panel is not ACTIVE\n");
- return -EINVAL;
- }
-
- r = picodlp_panel_power_on(dssdev);
- mutex_unlock(&picod->lock);
- dev_dbg(&dssdev->dev, "resuming picodlp panel\n");
- return r;
-}
-
static void picodlp_get_resolution(struct omap_dss_device *dssdev,
u16 *xres, u16 *yres)
{
@@ -560,9 +520,6 @@ static struct omap_dss_driver picodlp_driver = {
.get_resolution = picodlp_get_resolution,
- .suspend = picodlp_panel_suspend,
- .resume = picodlp_panel_resume,
-
.driver = {
.name = "picodlp_panel",
.owner = THIS_MODULE,
diff --git a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c
index 1ec3b277ff1..cada8c621e0 100644
--- a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c
+++ b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c
@@ -194,29 +194,12 @@ static void sharp_ls_panel_disable(struct omap_dss_device *dssdev)
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
-static int sharp_ls_panel_suspend(struct omap_dss_device *dssdev)
-{
- sharp_ls_power_off(dssdev);
- dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
- return 0;
-}
-
-static int sharp_ls_panel_resume(struct omap_dss_device *dssdev)
-{
- int r;
- r = sharp_ls_power_on(dssdev);
- dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
- return r;
-}
-
static struct omap_dss_driver sharp_ls_driver = {
.probe = sharp_ls_panel_probe,
.remove = __exit_p(sharp_ls_panel_remove),
.enable = sharp_ls_panel_enable,
.disable = sharp_ls_panel_disable,
- .suspend = sharp_ls_panel_suspend,
- .resume = sharp_ls_panel_resume,
.driver = {
.name = "sharp_ls_panel",
diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c
index f2f644680ca..a32407a5735 100644
--- a/drivers/video/omap2/displays/panel-taal.c
+++ b/drivers/video/omap2/displays/panel-taal.c
@@ -1245,76 +1245,6 @@ static void taal_disable(struct omap_dss_device *dssdev)
mutex_unlock(&td->lock);
}
-static int taal_suspend(struct omap_dss_device *dssdev)
-{
- struct taal_data *td = dev_get_drvdata(&dssdev->dev);
- int r;
-
- dev_dbg(&dssdev->dev, "suspend\n");
-
- mutex_lock(&td->lock);
-
- if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
- r = -EINVAL;
- goto err;
- }
-
- taal_cancel_ulps_work(dssdev);
- taal_cancel_esd_work(dssdev);
-
- dsi_bus_lock(dssdev);
-
- r = taal_wake_up(dssdev);
- if (!r)
- taal_power_off(dssdev);
-
- dsi_bus_unlock(dssdev);
-
- dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
-
- mutex_unlock(&td->lock);
-
- return 0;
-err:
- mutex_unlock(&td->lock);
- return r;
-}
-
-static int taal_resume(struct omap_dss_device *dssdev)
-{
- struct taal_data *td = dev_get_drvdata(&dssdev->dev);
- int r;
-
- dev_dbg(&dssdev->dev, "resume\n");
-
- mutex_lock(&td->lock);
-
- if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
- r = -EINVAL;
- goto err;
- }
-
- dsi_bus_lock(dssdev);
-
- r = taal_power_on(dssdev);
-
- dsi_bus_unlock(dssdev);
-
- if (r) {
- dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
- } else {
- dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
- taal_queue_esd_work(dssdev);
- }
-
- mutex_unlock(&td->lock);
-
- return r;
-err:
- mutex_unlock(&td->lock);
- return r;
-}
-
static void taal_framedone_cb(int err, void *data)
{
struct omap_dss_device *dssdev = data;
@@ -1818,8 +1748,6 @@ static struct omap_dss_driver taal_driver = {
.enable = taal_enable,
.disable = taal_disable,
- .suspend = taal_suspend,
- .resume = taal_resume,
.update = taal_update,
.sync = taal_sync,
diff --git a/drivers/video/omap2/displays/panel-tfp410.c b/drivers/video/omap2/displays/panel-tfp410.c
index 383811cf864..8281baafe1e 100644
--- a/drivers/video/omap2/displays/panel-tfp410.c
+++ b/drivers/video/omap2/displays/panel-tfp410.c
@@ -189,37 +189,6 @@ static void tfp410_disable(struct omap_dss_device *dssdev)
mutex_unlock(&ddata->lock);
}
-static int tfp410_suspend(struct omap_dss_device *dssdev)
-{
- struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
-
- mutex_lock(&ddata->lock);
-
- tfp410_power_off(dssdev);
-
- dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
-
- mutex_unlock(&ddata->lock);
-
- return 0;
-}
-
-static int tfp410_resume(struct omap_dss_device *dssdev)
-{
- struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev);
- int r;
-
- mutex_lock(&ddata->lock);
-
- r = tfp410_power_on(dssdev);
- if (r == 0)
- dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
- mutex_unlock(&ddata->lock);
-
- return r;
-}
-
static void tfp410_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
@@ -355,8 +324,6 @@ static struct omap_dss_driver tfp410_driver = {
.enable = tfp410_enable,
.disable = tfp410_disable,
- .suspend = tfp410_suspend,
- .resume = tfp410_resume,
.set_timings = tfp410_set_timings,
.get_timings = tfp410_get_timings,
diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
index b5e6dbc59f0..316b3da6d2c 100644
--- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
+++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
@@ -401,24 +401,6 @@ static void tpo_td043_disable(struct omap_dss_device *dssdev)
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
-static int tpo_td043_suspend(struct omap_dss_device *dssdev)
-{
- dev_dbg(&dssdev->dev, "suspend\n");
-
- tpo_td043_disable_dss(dssdev);
-
- dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
-
- return 0;
-}
-
-static int tpo_td043_resume(struct omap_dss_device *dssdev)
-{
- dev_dbg(&dssdev->dev, "resume\n");
-
- return tpo_td043_enable_dss(dssdev);
-}
-
static int tpo_td043_probe(struct omap_dss_device *dssdev)
{
struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
@@ -500,8 +482,6 @@ static struct omap_dss_driver tpo_td043_driver = {
.enable = tpo_td043_enable,
.disable = tpo_td043_disable,
- .suspend = tpo_td043_suspend,
- .resume = tpo_td043_resume,
.set_mirror = tpo_td043_set_hmirror,
.get_mirror = tpo_td043_get_hmirror,
diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig
index 80f5390aa13..cb0f145c707 100644
--- a/drivers/video/omap2/dss/Kconfig
+++ b/drivers/video/omap2/dss/Kconfig
@@ -1,33 +1,30 @@
menuconfig OMAP2_DSS
tristate "OMAP2+ Display Subsystem support"
- depends on ARCH_OMAP2PLUS
help
OMAP2+ Display Subsystem support.
if OMAP2_DSS
-config OMAP2_VRAM_SIZE
- int "VRAM size (MB)"
- range 0 32
- default 0
+config OMAP2_DSS_DEBUG
+ bool "Debug support"
+ default n
help
- The amount of SDRAM to reserve at boot time for video RAM use.
- This VRAM will be used by omapfb and other drivers that need
- large continuous RAM area for video use.
+ This enables printing of debug messages. Alternatively, debug messages
+ can also be enabled by setting CONFIG_DYNAMIC_DEBUG and then setting
+ appropriate flags in <debugfs>/dynamic_debug/control.
- You can also set this with "vram=<bytes>" kernel argument, or
- in the board file.
-
-config OMAP2_DSS_DEBUG_SUPPORT
- bool "Debug support"
- default y
+config OMAP2_DSS_DEBUGFS
+ bool "Debugfs filesystem support"
+ depends on DEBUG_FS
+ default n
help
- This enables debug messages. You need to enable printing
- with 'debug' module parameter.
+ This enables debugfs for OMAPDSS at <debugfs>/omapdss. This enables
+ querying about clock configuration and register configuration of dss,
+ dispc, dsi, hdmi and rfbi.
config OMAP2_DSS_COLLECT_IRQ_STATS
bool "Collect DSS IRQ statistics"
- depends on OMAP2_DSS_DEBUG_SUPPORT
+ depends on OMAP2_DSS_DEBUGFS
default n
help
Collect DSS IRQ statistics, printable via debugfs.
@@ -62,7 +59,6 @@ config OMAP2_DSS_VENC
config OMAP4_DSS_HDMI
bool "HDMI support"
- depends on ARCH_OMAP4
default y
help
HDMI Interface. This adds the High Definition Multimedia Interface.
@@ -70,11 +66,9 @@ config OMAP4_DSS_HDMI
config OMAP4_DSS_HDMI_AUDIO
bool
- depends on OMAP4_DSS_HDMI
config OMAP2_DSS_SDI
bool "SDI support"
- depends on ARCH_OMAP3
default n
help
SDI (Serial Display Interface) support.
@@ -84,7 +78,6 @@ config OMAP2_DSS_SDI
config OMAP2_DSS_DSI
bool "DSI support"
- depends on ARCH_OMAP3 || ARCH_OMAP4 || ARCH_OMAP5
default n
help
MIPI DSI (Display Serial Interface) support.
diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile
index 4549869bfe1..61949ff7940 100644
--- a/drivers/video/omap2/dss/Makefile
+++ b/drivers/video/omap2/dss/Makefile
@@ -1,6 +1,10 @@
obj-$(CONFIG_OMAP2_DSS) += omapdss.o
+# Core DSS files
omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o display.o \
- manager.o manager-sysfs.o overlay.o overlay-sysfs.o output.o apply.o
+ output.o
+# DSS compat layer files
+omapdss-y += manager.o manager-sysfs.o overlay.o overlay-sysfs.o apply.o \
+ dispc-compat.o display-sysfs.o
omapdss-$(CONFIG_OMAP2_DSS_DPI) += dpi.o
omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o
omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o venc_panel.o
@@ -8,3 +12,4 @@ omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o
omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o
omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o \
hdmi_panel.o ti_hdmi_4xxx_ip.o
+ccflags-$(CONFIG_OMAP2_DSS_DEBUG) += -DDEBUG
diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c
index 19d66f471b4..d446bdfc4c8 100644
--- a/drivers/video/omap2/dss/apply.c
+++ b/drivers/video/omap2/dss/apply.c
@@ -18,6 +18,7 @@
#define DSS_SUBSYS_NAME "APPLY"
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/jiffies.h>
@@ -26,6 +27,7 @@
#include "dss.h"
#include "dss_features.h"
+#include "dispc-compat.h"
/*
* We have 4 levels of cache for the dispc settings. First two are in SW and
@@ -70,7 +72,6 @@ struct ovl_priv_data {
bool shadow_extra_info_dirty;
bool enabled;
- enum omap_channel channel;
u32 fifo_low, fifo_high;
/*
@@ -105,6 +106,9 @@ struct mgr_priv_data {
struct omap_video_timings timings;
struct dss_lcd_mgr_config lcd_config;
+
+ void (*framedone_handler)(void *);
+ void *framedone_handler_data;
};
static struct {
@@ -132,7 +136,7 @@ static struct mgr_priv_data *get_mgr_priv(struct omap_overlay_manager *mgr)
return &dss_data.mgr_priv_data_array[mgr->id];
}
-void dss_apply_init(void)
+static void apply_init_priv(void)
{
const int num_ovls = dss_feat_get_num_ovls();
struct mgr_priv_data *mp;
@@ -414,11 +418,46 @@ static void wait_pending_extra_info_updates(void)
r = wait_for_completion_timeout(&extra_updated_completion, t);
if (r == 0)
DSSWARN("timeout in wait_pending_extra_info_updates\n");
- else if (r < 0)
- DSSERR("wait_pending_extra_info_updates failed: %d\n", r);
}
-int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
+static inline struct omap_dss_device *dss_ovl_get_device(struct omap_overlay *ovl)
+{
+ return ovl->manager ?
+ (ovl->manager->output ? ovl->manager->output->device : NULL) :
+ NULL;
+}
+
+static inline struct omap_dss_device *dss_mgr_get_device(struct omap_overlay_manager *mgr)
+{
+ return mgr->output ? mgr->output->device : NULL;
+}
+
+static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr)
+{
+ unsigned long timeout = msecs_to_jiffies(500);
+ struct omap_dss_device *dssdev = mgr->get_device(mgr);
+ u32 irq;
+ int r;
+
+ r = dispc_runtime_get();
+ if (r)
+ return r;
+
+ if (dssdev->type == OMAP_DISPLAY_TYPE_VENC)
+ irq = DISPC_IRQ_EVSYNC_ODD;
+ else if (dssdev->type == OMAP_DISPLAY_TYPE_HDMI)
+ irq = DISPC_IRQ_EVSYNC_EVEN;
+ else
+ irq = dispc_mgr_get_vsync_irq(mgr->id);
+
+ r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
+
+ dispc_runtime_put();
+
+ return r;
+}
+
+static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
{
unsigned long timeout = msecs_to_jiffies(500);
struct mgr_priv_data *mp = get_mgr_priv(mgr);
@@ -488,7 +527,7 @@ int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
return r;
}
-int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
+static int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
{
unsigned long timeout = msecs_to_jiffies(500);
struct ovl_priv_data *op;
@@ -573,7 +612,7 @@ static void dss_ovl_write_regs(struct omap_overlay *ovl)
struct mgr_priv_data *mp;
int r;
- DSSDBGF("%d", ovl->id);
+ DSSDBG("writing ovl %d regs", ovl->id);
if (!op->enabled || !op->info_dirty)
return;
@@ -608,7 +647,7 @@ static void dss_ovl_write_regs_extra(struct omap_overlay *ovl)
struct ovl_priv_data *op = get_ovl_priv(ovl);
struct mgr_priv_data *mp;
- DSSDBGF("%d", ovl->id);
+ DSSDBG("writing ovl %d regs extra", ovl->id);
if (!op->extra_info_dirty)
return;
@@ -617,7 +656,6 @@ static void dss_ovl_write_regs_extra(struct omap_overlay *ovl)
* disabled */
dispc_ovl_enable(ovl->id, op->enabled);
- dispc_ovl_set_channel_out(ovl->id, op->channel);
dispc_ovl_set_fifo_threshold(ovl->id, op->fifo_low, op->fifo_high);
mp = get_mgr_priv(ovl->manager);
@@ -632,7 +670,7 @@ static void dss_mgr_write_regs(struct omap_overlay_manager *mgr)
struct mgr_priv_data *mp = get_mgr_priv(mgr);
struct omap_overlay *ovl;
- DSSDBGF("%d", mgr->id);
+ DSSDBG("writing mgr %d regs", mgr->id);
if (!mp->enabled)
return;
@@ -658,7 +696,7 @@ static void dss_mgr_write_regs_extra(struct omap_overlay_manager *mgr)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
- DSSDBGF("%d", mgr->id);
+ DSSDBG("writing mgr %d regs extra", mgr->id);
if (!mp->extra_info_dirty)
return;
@@ -666,22 +704,8 @@ static void dss_mgr_write_regs_extra(struct omap_overlay_manager *mgr)
dispc_mgr_set_timings(mgr->id, &mp->timings);
/* lcd_config parameters */
- if (dss_mgr_is_lcd(mgr->id)) {
- dispc_mgr_set_io_pad_mode(mp->lcd_config.io_pad_mode);
-
- dispc_mgr_enable_stallmode(mgr->id, mp->lcd_config.stallmode);
- dispc_mgr_enable_fifohandcheck(mgr->id,
- mp->lcd_config.fifohandcheck);
-
- dispc_mgr_set_clock_div(mgr->id, &mp->lcd_config.clock_info);
-
- dispc_mgr_set_tft_data_lines(mgr->id,
- mp->lcd_config.video_port_width);
-
- dispc_lcd_enable_signal_polarity(mp->lcd_config.lcden_sig_polarity);
-
- dispc_mgr_set_lcd_type_tft(mgr->id);
- }
+ if (dss_mgr_is_lcd(mgr->id))
+ dispc_mgr_set_lcd_config(mgr->id, &mp->lcd_config);
mp->extra_info_dirty = false;
if (mp->updating)
@@ -761,7 +785,7 @@ static void mgr_clear_shadow_dirty(struct omap_overlay_manager *mgr)
}
}
-void dss_mgr_start_update(struct omap_overlay_manager *mgr)
+static void dss_mgr_start_update_compat(struct omap_overlay_manager *mgr)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
unsigned long flags;
@@ -786,9 +810,7 @@ void dss_mgr_start_update(struct omap_overlay_manager *mgr)
if (!dss_data.irq_enabled && need_isr())
dss_register_vsync_isr();
- dispc_mgr_enable(mgr->id, true);
-
- mgr_clear_shadow_dirty(mgr);
+ dispc_mgr_enable_sync(mgr->id);
spin_unlock_irqrestore(&data_lock, flags);
}
@@ -845,7 +867,6 @@ static void dss_apply_irq_handler(void *data, u32 mask)
for (i = 0; i < num_mgrs; i++) {
struct omap_overlay_manager *mgr;
struct mgr_priv_data *mp;
- bool was_updating;
mgr = omap_dss_get_overlay_manager(i);
mp = get_mgr_priv(mgr);
@@ -853,7 +874,6 @@ static void dss_apply_irq_handler(void *data, u32 mask)
if (!mp->enabled)
continue;
- was_updating = mp->updating;
mp->updating = dispc_mgr_is_enabled(i);
if (!mgr_manual_update(mgr)) {
@@ -872,6 +892,21 @@ static void dss_apply_irq_handler(void *data, u32 mask)
if (!extra_updating)
complete_all(&extra_updated_completion);
+ /* call framedone handlers for manual update displays */
+ for (i = 0; i < num_mgrs; i++) {
+ struct omap_overlay_manager *mgr;
+ struct mgr_priv_data *mp;
+
+ mgr = omap_dss_get_overlay_manager(i);
+ mp = get_mgr_priv(mgr);
+
+ if (!mgr_manual_update(mgr) || !mp->framedone_handler)
+ continue;
+
+ if (mask & dispc_mgr_get_framedone_irq(i))
+ mp->framedone_handler(mp->framedone_handler_data);
+ }
+
if (!need_isr())
dss_unregister_vsync_isr();
@@ -906,7 +941,7 @@ static void omap_dss_mgr_apply_mgr(struct omap_overlay_manager *mgr)
mp->info = mp->user_info;
}
-int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
+static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
{
unsigned long flags;
struct omap_overlay *ovl;
@@ -1005,7 +1040,7 @@ static void dss_setup_fifos(void)
}
}
-int dss_mgr_enable(struct omap_overlay_manager *mgr)
+static int dss_mgr_enable_compat(struct omap_overlay_manager *mgr)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
unsigned long flags;
@@ -1035,10 +1070,13 @@ int dss_mgr_enable(struct omap_overlay_manager *mgr)
if (!mgr_manual_update(mgr))
mp->updating = true;
+ if (!dss_data.irq_enabled && need_isr())
+ dss_register_vsync_isr();
+
spin_unlock_irqrestore(&data_lock, flags);
if (!mgr_manual_update(mgr))
- dispc_mgr_enable(mgr->id, true);
+ dispc_mgr_enable_sync(mgr->id);
out:
mutex_unlock(&apply_lock);
@@ -1052,7 +1090,7 @@ err:
return r;
}
-void dss_mgr_disable(struct omap_overlay_manager *mgr)
+static void dss_mgr_disable_compat(struct omap_overlay_manager *mgr)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
unsigned long flags;
@@ -1063,7 +1101,7 @@ void dss_mgr_disable(struct omap_overlay_manager *mgr)
goto out;
if (!mgr_manual_update(mgr))
- dispc_mgr_enable(mgr->id, false);
+ dispc_mgr_disable_sync(mgr->id);
spin_lock_irqsave(&data_lock, flags);
@@ -1076,7 +1114,7 @@ out:
mutex_unlock(&apply_lock);
}
-int dss_mgr_set_info(struct omap_overlay_manager *mgr,
+static int dss_mgr_set_info(struct omap_overlay_manager *mgr,
struct omap_overlay_manager_info *info)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
@@ -1097,7 +1135,7 @@ int dss_mgr_set_info(struct omap_overlay_manager *mgr,
return 0;
}
-void dss_mgr_get_info(struct omap_overlay_manager *mgr,
+static void dss_mgr_get_info(struct omap_overlay_manager *mgr,
struct omap_overlay_manager_info *info)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
@@ -1110,7 +1148,7 @@ void dss_mgr_get_info(struct omap_overlay_manager *mgr,
spin_unlock_irqrestore(&data_lock, flags);
}
-int dss_mgr_set_output(struct omap_overlay_manager *mgr,
+static int dss_mgr_set_output(struct omap_overlay_manager *mgr,
struct omap_dss_output *output)
{
int r;
@@ -1142,7 +1180,7 @@ err:
return r;
}
-int dss_mgr_unset_output(struct omap_overlay_manager *mgr)
+static int dss_mgr_unset_output(struct omap_overlay_manager *mgr)
{
int r;
struct mgr_priv_data *mp = get_mgr_priv(mgr);
@@ -1189,7 +1227,7 @@ static void dss_apply_mgr_timings(struct omap_overlay_manager *mgr,
mp->extra_info_dirty = true;
}
-void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
+static void dss_mgr_set_timings_compat(struct omap_overlay_manager *mgr,
const struct omap_video_timings *timings)
{
unsigned long flags;
@@ -1217,7 +1255,7 @@ static void dss_apply_mgr_lcd_config(struct omap_overlay_manager *mgr,
mp->extra_info_dirty = true;
}
-void dss_mgr_set_lcd_config(struct omap_overlay_manager *mgr,
+static void dss_mgr_set_lcd_config_compat(struct omap_overlay_manager *mgr,
const struct dss_lcd_mgr_config *config)
{
unsigned long flags;
@@ -1236,7 +1274,7 @@ out:
spin_unlock_irqrestore(&data_lock, flags);
}
-int dss_ovl_set_info(struct omap_overlay *ovl,
+static int dss_ovl_set_info(struct omap_overlay *ovl,
struct omap_overlay_info *info)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
@@ -1257,7 +1295,7 @@ int dss_ovl_set_info(struct omap_overlay *ovl,
return 0;
}
-void dss_ovl_get_info(struct omap_overlay *ovl,
+static void dss_ovl_get_info(struct omap_overlay *ovl,
struct omap_overlay_info *info)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
@@ -1270,7 +1308,7 @@ void dss_ovl_get_info(struct omap_overlay *ovl,
spin_unlock_irqrestore(&data_lock, flags);
}
-int dss_ovl_set_manager(struct omap_overlay *ovl,
+static int dss_ovl_set_manager(struct omap_overlay *ovl,
struct omap_overlay_manager *mgr)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
@@ -1289,45 +1327,40 @@ int dss_ovl_set_manager(struct omap_overlay *ovl,
goto err;
}
+ r = dispc_runtime_get();
+ if (r)
+ goto err;
+
spin_lock_irqsave(&data_lock, flags);
if (op->enabled) {
spin_unlock_irqrestore(&data_lock, flags);
DSSERR("overlay has to be disabled to change the manager\n");
r = -EINVAL;
- goto err;
+ goto err1;
}
- op->channel = mgr->id;
- op->extra_info_dirty = true;
+ dispc_ovl_set_channel_out(ovl->id, mgr->id);
ovl->manager = mgr;
list_add_tail(&ovl->list, &mgr->overlays);
spin_unlock_irqrestore(&data_lock, flags);
- /* XXX: When there is an overlay on a DSI manual update display, and
- * the overlay is first disabled, then moved to tv, and enabled, we
- * seem to get SYNC_LOST_DIGIT error.
- *
- * Waiting doesn't seem to help, but updating the manual update display
- * after disabling the overlay seems to fix this. This hints that the
- * overlay is perhaps somehow tied to the LCD output until the output
- * is updated.
- *
- * Userspace workaround for this is to update the LCD after disabling
- * the overlay, but before moving the overlay to TV.
- */
+ dispc_runtime_put();
mutex_unlock(&apply_lock);
return 0;
+
+err1:
+ dispc_runtime_put();
err:
mutex_unlock(&apply_lock);
return r;
}
-int dss_ovl_unset_manager(struct omap_overlay *ovl)
+static int dss_ovl_unset_manager(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
@@ -1355,9 +1388,24 @@ int dss_ovl_unset_manager(struct omap_overlay *ovl)
/* wait for pending extra_info updates to ensure the ovl is disabled */
wait_pending_extra_info_updates();
+ /*
+ * For a manual update display, there is no guarantee that the overlay
+ * is really disabled in HW, we may need an extra update from this
+ * manager before the configurations can go in. Return an error if the
+ * overlay needed an update from the manager.
+ *
+ * TODO: Instead of returning an error, try to do a dummy manager update
+ * here to disable the overlay in hardware. Use the *GATED fields in
+ * the DISPC_CONFIG registers to do a dummy update.
+ */
spin_lock_irqsave(&data_lock, flags);
- op->channel = -1;
+ if (ovl_manual_update(ovl) && op->extra_info_dirty) {
+ spin_unlock_irqrestore(&data_lock, flags);
+ DSSERR("need an update to change the manager\n");
+ r = -EINVAL;
+ goto err;
+ }
ovl->manager = NULL;
list_del(&ovl->list);
@@ -1372,7 +1420,7 @@ err:
return r;
}
-bool dss_ovl_is_enabled(struct omap_overlay *ovl)
+static bool dss_ovl_is_enabled(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
@@ -1387,7 +1435,7 @@ bool dss_ovl_is_enabled(struct omap_overlay *ovl)
return e;
}
-int dss_ovl_enable(struct omap_overlay *ovl)
+static int dss_ovl_enable(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
@@ -1437,7 +1485,7 @@ err1:
return r;
}
-int dss_ovl_disable(struct omap_overlay *ovl)
+static int dss_ovl_disable(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
@@ -1472,3 +1520,152 @@ err:
return r;
}
+static int dss_mgr_register_framedone_handler_compat(struct omap_overlay_manager *mgr,
+ void (*handler)(void *), void *data)
+{
+ struct mgr_priv_data *mp = get_mgr_priv(mgr);
+
+ if (mp->framedone_handler)
+ return -EBUSY;
+
+ mp->framedone_handler = handler;
+ mp->framedone_handler_data = data;
+
+ return 0;
+}
+
+static void dss_mgr_unregister_framedone_handler_compat(struct omap_overlay_manager *mgr,
+ void (*handler)(void *), void *data)
+{
+ struct mgr_priv_data *mp = get_mgr_priv(mgr);
+
+ WARN_ON(mp->framedone_handler != handler ||
+ mp->framedone_handler_data != data);
+
+ mp->framedone_handler = NULL;
+ mp->framedone_handler_data = NULL;
+}
+
+static const struct dss_mgr_ops apply_mgr_ops = {
+ .start_update = dss_mgr_start_update_compat,
+ .enable = dss_mgr_enable_compat,
+ .disable = dss_mgr_disable_compat,
+ .set_timings = dss_mgr_set_timings_compat,
+ .set_lcd_config = dss_mgr_set_lcd_config_compat,
+ .register_framedone_handler = dss_mgr_register_framedone_handler_compat,
+ .unregister_framedone_handler = dss_mgr_unregister_framedone_handler_compat,
+};
+
+static int compat_refcnt;
+static DEFINE_MUTEX(compat_init_lock);
+
+int omapdss_compat_init(void)
+{
+ struct platform_device *pdev = dss_get_core_pdev();
+ struct omap_dss_device *dssdev = NULL;
+ int i, r;
+
+ mutex_lock(&compat_init_lock);
+
+ if (compat_refcnt++ > 0)
+ goto out;
+
+ apply_init_priv();
+
+ dss_init_overlay_managers(pdev);
+ dss_init_overlays(pdev);
+
+ for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) {
+ struct omap_overlay_manager *mgr;
+
+ mgr = omap_dss_get_overlay_manager(i);
+
+ mgr->set_output = &dss_mgr_set_output;
+ mgr->unset_output = &dss_mgr_unset_output;
+ mgr->apply = &omap_dss_mgr_apply;
+ mgr->set_manager_info = &dss_mgr_set_info;
+ mgr->get_manager_info = &dss_mgr_get_info;
+ mgr->wait_for_go = &dss_mgr_wait_for_go;
+ mgr->wait_for_vsync = &dss_mgr_wait_for_vsync;
+ mgr->get_device = &dss_mgr_get_device;
+ }
+
+ for (i = 0; i < omap_dss_get_num_overlays(); i++) {
+ struct omap_overlay *ovl = omap_dss_get_overlay(i);
+
+ ovl->is_enabled = &dss_ovl_is_enabled;
+ ovl->enable = &dss_ovl_enable;
+ ovl->disable = &dss_ovl_disable;
+ ovl->set_manager = &dss_ovl_set_manager;
+ ovl->unset_manager = &dss_ovl_unset_manager;
+ ovl->set_overlay_info = &dss_ovl_set_info;
+ ovl->get_overlay_info = &dss_ovl_get_info;
+ ovl->wait_for_go = &dss_mgr_wait_for_go_ovl;
+ ovl->get_device = &dss_ovl_get_device;
+ }
+
+ r = dss_install_mgr_ops(&apply_mgr_ops);
+ if (r)
+ goto err_mgr_ops;
+
+ for_each_dss_dev(dssdev) {
+ r = display_init_sysfs(pdev, dssdev);
+ /* XXX uninit sysfs files on error */
+ if (r)
+ goto err_disp_sysfs;
+ }
+
+ dispc_runtime_get();
+
+ r = dss_dispc_initialize_irq();
+ if (r)
+ goto err_init_irq;
+
+ dispc_runtime_put();
+
+out:
+ mutex_unlock(&compat_init_lock);
+
+ return 0;
+
+err_init_irq:
+ dispc_runtime_put();
+
+err_disp_sysfs:
+ dss_uninstall_mgr_ops();
+
+err_mgr_ops:
+ dss_uninit_overlay_managers(pdev);
+ dss_uninit_overlays(pdev);
+
+ compat_refcnt--;
+
+ mutex_unlock(&compat_init_lock);
+
+ return r;
+}
+EXPORT_SYMBOL(omapdss_compat_init);
+
+void omapdss_compat_uninit(void)
+{
+ struct platform_device *pdev = dss_get_core_pdev();
+ struct omap_dss_device *dssdev = NULL;
+
+ mutex_lock(&compat_init_lock);
+
+ if (--compat_refcnt > 0)
+ goto out;
+
+ dss_dispc_uninitialize_irq();
+
+ for_each_dss_dev(dssdev)
+ display_uninit_sysfs(pdev, dssdev);
+
+ dss_uninstall_mgr_ops();
+
+ dss_uninit_overlay_managers(pdev);
+ dss_uninit_overlays(pdev);
+out:
+ mutex_unlock(&compat_init_lock);
+}
+EXPORT_SYMBOL(omapdss_compat_uninit);
diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c
index d94ef9e31a3..f8779d4750b 100644
--- a/drivers/video/omap2/dss/core.c
+++ b/drivers/video/omap2/dss/core.c
@@ -53,15 +53,23 @@ static char *def_disp_name;
module_param_named(def_disp, def_disp_name, charp, 0);
MODULE_PARM_DESC(def_disp, "default display name");
-#ifdef DEBUG
-bool dss_debug;
-module_param_named(debug, dss_debug, bool, 0644);
-#endif
-
-const char *dss_get_default_display_name(void)
+const char *omapdss_get_default_display_name(void)
{
return core.default_display_name;
}
+EXPORT_SYMBOL(omapdss_get_default_display_name);
+
+enum omapdss_version omapdss_get_version(void)
+{
+ struct omap_dss_board_info *pdata = core.pdev->dev.platform_data;
+ return pdata->version;
+}
+EXPORT_SYMBOL(omapdss_get_version);
+
+struct platform_device *dss_get_core_pdev(void)
+{
+ return core.pdev;
+}
/* REGULATORS */
@@ -93,21 +101,6 @@ struct regulator *dss_get_vdds_sdi(void)
return reg;
}
-int dss_get_ctx_loss_count(struct device *dev)
-{
- struct omap_dss_board_info *board_data = core.pdev->dev.platform_data;
- int cnt;
-
- if (!board_data->get_context_loss_count)
- return -ENOENT;
-
- cnt = board_data->get_context_loss_count(dev);
-
- WARN_ONCE(cnt < 0, "get_context_loss_count failed: %d\n", cnt);
-
- return cnt;
-}
-
int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask)
{
struct omap_dss_board_info *board_data = core.pdev->dev.platform_data;
@@ -122,7 +115,7 @@ void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask)
{
struct omap_dss_board_info *board_data = core.pdev->dev.platform_data;
- if (!board_data->dsi_enable_pads)
+ if (!board_data->dsi_disable_pads)
return;
return board_data->dsi_disable_pads(dsi_id, lane_mask);
@@ -138,7 +131,7 @@ int dss_set_min_bus_tput(struct device *dev, unsigned long tput)
return 0;
}
-#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
+#if defined(CONFIG_OMAP2_DSS_DEBUGFS)
static int dss_debug_show(struct seq_file *s, void *unused)
{
void (*func)(struct seq_file *) = s->private;
@@ -193,7 +186,7 @@ int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
return 0;
}
-#else /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */
+#else /* CONFIG_OMAP2_DSS_DEBUGFS */
static inline int dss_initialize_debugfs(void)
{
return 0;
@@ -205,7 +198,7 @@ int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
{
return 0;
}
-#endif /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */
+#endif /* CONFIG_OMAP2_DSS_DEBUGFS */
/* PLATFORM DEVICE */
static int omap_dss_pm_notif(struct notifier_block *b, unsigned long v, void *d)
@@ -237,12 +230,7 @@ static int __init omap_dss_probe(struct platform_device *pdev)
core.pdev = pdev;
- dss_features_init(pdata->version);
-
- dss_apply_init();
-
- dss_init_overlay_managers(pdev);
- dss_init_overlays(pdev);
+ dss_features_init(omapdss_get_version());
r = dss_initialize_debugfs();
if (r)
@@ -268,9 +256,6 @@ static int omap_dss_remove(struct platform_device *pdev)
dss_uninitialize_debugfs();
- dss_uninit_overlays(pdev);
- dss_uninit_overlay_managers(pdev);
-
return 0;
}
@@ -358,15 +343,10 @@ static int dss_driver_probe(struct device *dev)
dev_name(dev), dssdev->driver_name,
dssdrv->driver.name);
- r = dss_init_device(core.pdev, dssdev);
- if (r)
- return r;
-
r = dssdrv->probe(dssdev);
if (r) {
DSSERR("driver probe failed: %d\n", r);
- dss_uninit_device(core.pdev, dssdev);
return r;
}
@@ -387,8 +367,6 @@ static int dss_driver_remove(struct device *dev)
dssdrv->remove(dssdev);
- dss_uninit_device(core.pdev, dssdev);
-
dssdev->driver = NULL;
return 0;
@@ -507,6 +485,9 @@ static int __init omap_dss_bus_register(void)
/* INIT */
static int (*dss_output_drv_reg_funcs[])(void) __initdata = {
+#ifdef CONFIG_OMAP2_DSS_DSI
+ dsi_init_platform_driver,
+#endif
#ifdef CONFIG_OMAP2_DSS_DPI
dpi_init_platform_driver,
#endif
@@ -519,15 +500,15 @@ static int (*dss_output_drv_reg_funcs[])(void) __initdata = {
#ifdef CONFIG_OMAP2_DSS_VENC
venc_init_platform_driver,
#endif
-#ifdef CONFIG_OMAP2_DSS_DSI
- dsi_init_platform_driver,
-#endif
#ifdef CONFIG_OMAP4_DSS_HDMI
hdmi_init_platform_driver,
#endif
};
static void (*dss_output_drv_unreg_funcs[])(void) __exitdata = {
+#ifdef CONFIG_OMAP2_DSS_DSI
+ dsi_uninit_platform_driver,
+#endif
#ifdef CONFIG_OMAP2_DSS_DPI
dpi_uninit_platform_driver,
#endif
@@ -540,9 +521,6 @@ static void (*dss_output_drv_unreg_funcs[])(void) __exitdata = {
#ifdef CONFIG_OMAP2_DSS_VENC
venc_uninit_platform_driver,
#endif
-#ifdef CONFIG_OMAP2_DSS_DSI
- dsi_uninit_platform_driver,
-#endif
#ifdef CONFIG_OMAP4_DSS_HDMI
hdmi_uninit_platform_driver,
#endif
diff --git a/drivers/video/omap2/dss/dispc-compat.c b/drivers/video/omap2/dss/dispc-compat.c
new file mode 100644
index 00000000000..928884c9a0a
--- /dev/null
+++ b/drivers/video/omap2/dss/dispc-compat.c
@@ -0,0 +1,667 @@
+/*
+ * Copyright (C) 2012 Texas Instruments
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define DSS_SUBSYS_NAME "APPLY"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/jiffies.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/seq_file.h>
+
+#include <video/omapdss.h>
+
+#include "dss.h"
+#include "dss_features.h"
+#include "dispc-compat.h"
+
+#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \
+ DISPC_IRQ_OCP_ERR | \
+ DISPC_IRQ_VID1_FIFO_UNDERFLOW | \
+ DISPC_IRQ_VID2_FIFO_UNDERFLOW | \
+ DISPC_IRQ_SYNC_LOST | \
+ DISPC_IRQ_SYNC_LOST_DIGIT)
+
+#define DISPC_MAX_NR_ISRS 8
+
+struct omap_dispc_isr_data {
+ omap_dispc_isr_t isr;
+ void *arg;
+ u32 mask;
+};
+
+struct dispc_irq_stats {
+ unsigned long last_reset;
+ unsigned irq_count;
+ unsigned irqs[32];
+};
+
+static struct {
+ spinlock_t irq_lock;
+ u32 irq_error_mask;
+ struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS];
+ u32 error_irqs;
+ struct work_struct error_work;
+
+#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
+ spinlock_t irq_stats_lock;
+ struct dispc_irq_stats irq_stats;
+#endif
+} dispc_compat;
+
+
+#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
+static void dispc_dump_irqs(struct seq_file *s)
+{
+ unsigned long flags;
+ struct dispc_irq_stats stats;
+
+ spin_lock_irqsave(&dispc_compat.irq_stats_lock, flags);
+
+ stats = dispc_compat.irq_stats;
+ memset(&dispc_compat.irq_stats, 0, sizeof(dispc_compat.irq_stats));
+ dispc_compat.irq_stats.last_reset = jiffies;
+
+ spin_unlock_irqrestore(&dispc_compat.irq_stats_lock, flags);
+
+ seq_printf(s, "period %u ms\n",
+ jiffies_to_msecs(jiffies - stats.last_reset));
+
+ seq_printf(s, "irqs %d\n", stats.irq_count);
+#define PIS(x) \
+ seq_printf(s, "%-20s %10d\n", #x, stats.irqs[ffs(DISPC_IRQ_##x)-1]);
+
+ PIS(FRAMEDONE);
+ PIS(VSYNC);
+ PIS(EVSYNC_EVEN);
+ PIS(EVSYNC_ODD);
+ PIS(ACBIAS_COUNT_STAT);
+ PIS(PROG_LINE_NUM);
+ PIS(GFX_FIFO_UNDERFLOW);
+ PIS(GFX_END_WIN);
+ PIS(PAL_GAMMA_MASK);
+ PIS(OCP_ERR);
+ PIS(VID1_FIFO_UNDERFLOW);
+ PIS(VID1_END_WIN);
+ PIS(VID2_FIFO_UNDERFLOW);
+ PIS(VID2_END_WIN);
+ if (dss_feat_get_num_ovls() > 3) {
+ PIS(VID3_FIFO_UNDERFLOW);
+ PIS(VID3_END_WIN);
+ }
+ PIS(SYNC_LOST);
+ PIS(SYNC_LOST_DIGIT);
+ PIS(WAKEUP);
+ if (dss_has_feature(FEAT_MGR_LCD2)) {
+ PIS(FRAMEDONE2);
+ PIS(VSYNC2);
+ PIS(ACBIAS_COUNT_STAT2);
+ PIS(SYNC_LOST2);
+ }
+ if (dss_has_feature(FEAT_MGR_LCD3)) {
+ PIS(FRAMEDONE3);
+ PIS(VSYNC3);
+ PIS(ACBIAS_COUNT_STAT3);
+ PIS(SYNC_LOST3);
+ }
+#undef PIS
+}
+#endif
+
+/* dispc.irq_lock has to be locked by the caller */
+static void _omap_dispc_set_irqs(void)
+{
+ u32 mask;
+ int i;
+ struct omap_dispc_isr_data *isr_data;
+
+ mask = dispc_compat.irq_error_mask;
+
+ for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
+ isr_data = &dispc_compat.registered_isr[i];
+
+ if (isr_data->isr == NULL)
+ continue;
+
+ mask |= isr_data->mask;
+ }
+
+ dispc_write_irqenable(mask);
+}
+
+int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
+{
+ int i;
+ int ret;
+ unsigned long flags;
+ struct omap_dispc_isr_data *isr_data;
+
+ if (isr == NULL)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dispc_compat.irq_lock, flags);
+
+ /* check for duplicate entry */
+ for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
+ isr_data = &dispc_compat.registered_isr[i];
+ if (isr_data->isr == isr && isr_data->arg == arg &&
+ isr_data->mask == mask) {
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ isr_data = NULL;
+ ret = -EBUSY;
+
+ for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
+ isr_data = &dispc_compat.registered_isr[i];
+
+ if (isr_data->isr != NULL)
+ continue;
+
+ isr_data->isr = isr;
+ isr_data->arg = arg;
+ isr_data->mask = mask;
+ ret = 0;
+
+ break;
+ }
+
+ if (ret)
+ goto err;
+
+ _omap_dispc_set_irqs();
+
+ spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
+
+ return 0;
+err:
+ spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(omap_dispc_register_isr);
+
+int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
+{
+ int i;
+ unsigned long flags;
+ int ret = -EINVAL;
+ struct omap_dispc_isr_data *isr_data;
+
+ spin_lock_irqsave(&dispc_compat.irq_lock, flags);
+
+ for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
+ isr_data = &dispc_compat.registered_isr[i];
+ if (isr_data->isr != isr || isr_data->arg != arg ||
+ isr_data->mask != mask)
+ continue;
+
+ /* found the correct isr */
+
+ isr_data->isr = NULL;
+ isr_data->arg = NULL;
+ isr_data->mask = 0;
+
+ ret = 0;
+ break;
+ }
+
+ if (ret == 0)
+ _omap_dispc_set_irqs();
+
+ spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(omap_dispc_unregister_isr);
+
+static void print_irq_status(u32 status)
+{
+ if ((status & dispc_compat.irq_error_mask) == 0)
+ return;
+
+#define PIS(x) (status & DISPC_IRQ_##x) ? (#x " ") : ""
+
+ pr_debug("DISPC IRQ: 0x%x: %s%s%s%s%s%s%s%s%s\n",
+ status,
+ PIS(OCP_ERR),
+ PIS(GFX_FIFO_UNDERFLOW),
+ PIS(VID1_FIFO_UNDERFLOW),
+ PIS(VID2_FIFO_UNDERFLOW),
+ dss_feat_get_num_ovls() > 3 ? PIS(VID3_FIFO_UNDERFLOW) : "",
+ PIS(SYNC_LOST),
+ PIS(SYNC_LOST_DIGIT),
+ dss_has_feature(FEAT_MGR_LCD2) ? PIS(SYNC_LOST2) : "",
+ dss_has_feature(FEAT_MGR_LCD3) ? PIS(SYNC_LOST3) : "");
+#undef PIS
+}
+
+/* Called from dss.c. Note that we don't touch clocks here,
+ * but we presume they are on because we got an IRQ. However,
+ * an irq handler may turn the clocks off, so we may not have
+ * clock later in the function. */
+static irqreturn_t omap_dispc_irq_handler(int irq, void *arg)
+{
+ int i;
+ u32 irqstatus, irqenable;
+ u32 handledirqs = 0;
+ u32 unhandled_errors;
+ struct omap_dispc_isr_data *isr_data;
+ struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS];
+
+ spin_lock(&dispc_compat.irq_lock);
+
+ irqstatus = dispc_read_irqstatus();
+ irqenable = dispc_read_irqenable();
+
+ /* IRQ is not for us */
+ if (!(irqstatus & irqenable)) {
+ spin_unlock(&dispc_compat.irq_lock);
+ return IRQ_NONE;
+ }
+
+#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
+ spin_lock(&dispc_compat.irq_stats_lock);
+ dispc_compat.irq_stats.irq_count++;
+ dss_collect_irq_stats(irqstatus, dispc_compat.irq_stats.irqs);
+ spin_unlock(&dispc_compat.irq_stats_lock);
+#endif
+
+ print_irq_status(irqstatus);
+
+ /* Ack the interrupt. Do it here before clocks are possibly turned
+ * off */
+ dispc_clear_irqstatus(irqstatus);
+ /* flush posted write */
+ dispc_read_irqstatus();
+
+ /* make a copy and unlock, so that isrs can unregister
+ * themselves */
+ memcpy(registered_isr, dispc_compat.registered_isr,
+ sizeof(registered_isr));
+
+ spin_unlock(&dispc_compat.irq_lock);
+
+ for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
+ isr_data = &registered_isr[i];
+
+ if (!isr_data->isr)
+ continue;
+
+ if (isr_data->mask & irqstatus) {
+ isr_data->isr(isr_data->arg, irqstatus);
+ handledirqs |= isr_data->mask;
+ }
+ }
+
+ spin_lock(&dispc_compat.irq_lock);
+
+ unhandled_errors = irqstatus & ~handledirqs & dispc_compat.irq_error_mask;
+
+ if (unhandled_errors) {
+ dispc_compat.error_irqs |= unhandled_errors;
+
+ dispc_compat.irq_error_mask &= ~unhandled_errors;
+ _omap_dispc_set_irqs();
+
+ schedule_work(&dispc_compat.error_work);
+ }
+
+ spin_unlock(&dispc_compat.irq_lock);
+
+ return IRQ_HANDLED;
+}
+
+static void dispc_error_worker(struct work_struct *work)
+{
+ int i;
+ u32 errors;
+ unsigned long flags;
+ static const unsigned fifo_underflow_bits[] = {
+ DISPC_IRQ_GFX_FIFO_UNDERFLOW,
+ DISPC_IRQ_VID1_FIFO_UNDERFLOW,
+ DISPC_IRQ_VID2_FIFO_UNDERFLOW,
+ DISPC_IRQ_VID3_FIFO_UNDERFLOW,
+ };
+
+ spin_lock_irqsave(&dispc_compat.irq_lock, flags);
+ errors = dispc_compat.error_irqs;
+ dispc_compat.error_irqs = 0;
+ spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
+
+ dispc_runtime_get();
+
+ for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+ struct omap_overlay *ovl;
+ unsigned bit;
+
+ ovl = omap_dss_get_overlay(i);
+ bit = fifo_underflow_bits[i];
+
+ if (bit & errors) {
+ DSSERR("FIFO UNDERFLOW on %s, disabling the overlay\n",
+ ovl->name);
+ dispc_ovl_enable(ovl->id, false);
+ dispc_mgr_go(ovl->manager->id);
+ msleep(50);
+ }
+ }
+
+ for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
+ struct omap_overlay_manager *mgr;
+ unsigned bit;
+
+ mgr = omap_dss_get_overlay_manager(i);
+ bit = dispc_mgr_get_sync_lost_irq(i);
+
+ if (bit & errors) {
+ int j;
+
+ DSSERR("SYNC_LOST on channel %s, restarting the output "
+ "with video overlays disabled\n",
+ mgr->name);
+
+ dss_mgr_disable(mgr);
+
+ for (j = 0; j < omap_dss_get_num_overlays(); ++j) {
+ struct omap_overlay *ovl;
+ ovl = omap_dss_get_overlay(j);
+
+ if (ovl->id != OMAP_DSS_GFX &&
+ ovl->manager == mgr)
+ ovl->disable(ovl);
+ }
+
+ dss_mgr_enable(mgr);
+ }
+ }
+
+ if (errors & DISPC_IRQ_OCP_ERR) {
+ DSSERR("OCP_ERR\n");
+ for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
+ struct omap_overlay_manager *mgr;
+
+ mgr = omap_dss_get_overlay_manager(i);
+ dss_mgr_disable(mgr);
+ }
+ }
+
+ spin_lock_irqsave(&dispc_compat.irq_lock, flags);
+ dispc_compat.irq_error_mask |= errors;
+ _omap_dispc_set_irqs();
+ spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
+
+ dispc_runtime_put();
+}
+
+int dss_dispc_initialize_irq(void)
+{
+ int r;
+
+#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
+ spin_lock_init(&dispc_compat.irq_stats_lock);
+ dispc_compat.irq_stats.last_reset = jiffies;
+ dss_debugfs_create_file("dispc_irq", dispc_dump_irqs);
+#endif
+
+ spin_lock_init(&dispc_compat.irq_lock);
+
+ memset(dispc_compat.registered_isr, 0,
+ sizeof(dispc_compat.registered_isr));
+
+ dispc_compat.irq_error_mask = DISPC_IRQ_MASK_ERROR;
+ if (dss_has_feature(FEAT_MGR_LCD2))
+ dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST2;
+ if (dss_has_feature(FEAT_MGR_LCD3))
+ dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST3;
+ if (dss_feat_get_num_ovls() > 3)
+ dispc_compat.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW;
+
+ /*
+ * there's SYNC_LOST_DIGIT waiting after enabling the DSS,
+ * so clear it
+ */
+ dispc_clear_irqstatus(dispc_read_irqstatus());
+
+ INIT_WORK(&dispc_compat.error_work, dispc_error_worker);
+
+ _omap_dispc_set_irqs();
+
+ r = dispc_request_irq(omap_dispc_irq_handler, &dispc_compat);
+ if (r) {
+ DSSERR("dispc_request_irq failed\n");
+ return r;
+ }
+
+ return 0;
+}
+
+void dss_dispc_uninitialize_irq(void)
+{
+ dispc_free_irq(&dispc_compat);
+}
+
+static void dispc_mgr_disable_isr(void *data, u32 mask)
+{
+ struct completion *compl = data;
+ complete(compl);
+}
+
+static void dispc_mgr_enable_lcd_out(enum omap_channel channel)
+{
+ dispc_mgr_enable(channel, true);
+}
+
+static void dispc_mgr_disable_lcd_out(enum omap_channel channel)
+{
+ DECLARE_COMPLETION_ONSTACK(framedone_compl);
+ int r;
+ u32 irq;
+
+ if (dispc_mgr_is_enabled(channel) == false)
+ return;
+
+ /*
+ * When we disable LCD output, we need to wait for FRAMEDONE to know
+ * that DISPC has finished with the LCD output.
+ */
+
+ irq = dispc_mgr_get_framedone_irq(channel);
+
+ r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl,
+ irq);
+ if (r)
+ DSSERR("failed to register FRAMEDONE isr\n");
+
+ dispc_mgr_enable(channel, false);
+
+ /* if we couldn't register for framedone, just sleep and exit */
+ if (r) {
+ msleep(100);
+ return;
+ }
+
+ if (!wait_for_completion_timeout(&framedone_compl,
+ msecs_to_jiffies(100)))
+ DSSERR("timeout waiting for FRAME DONE\n");
+
+ r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl,
+ irq);
+ if (r)
+ DSSERR("failed to unregister FRAMEDONE isr\n");
+}
+
+static void dispc_digit_out_enable_isr(void *data, u32 mask)
+{
+ struct completion *compl = data;
+
+ /* ignore any sync lost interrupts */
+ if (mask & (DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD))
+ complete(compl);
+}
+
+static void dispc_mgr_enable_digit_out(void)
+{
+ DECLARE_COMPLETION_ONSTACK(vsync_compl);
+ int r;
+ u32 irq_mask;
+
+ if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT) == true)
+ return;
+
+ /*
+ * Digit output produces some sync lost interrupts during the first
+ * frame when enabling. Those need to be ignored, so we register for the
+ * sync lost irq to prevent the error handler from triggering.
+ */
+
+ irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT) |
+ dispc_mgr_get_sync_lost_irq(OMAP_DSS_CHANNEL_DIGIT);
+
+ r = omap_dispc_register_isr(dispc_digit_out_enable_isr, &vsync_compl,
+ irq_mask);
+ if (r) {
+ DSSERR("failed to register %x isr\n", irq_mask);
+ return;
+ }
+
+ dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, true);
+
+ /* wait for the first evsync */
+ if (!wait_for_completion_timeout(&vsync_compl, msecs_to_jiffies(100)))
+ DSSERR("timeout waiting for digit out to start\n");
+
+ r = omap_dispc_unregister_isr(dispc_digit_out_enable_isr, &vsync_compl,
+ irq_mask);
+ if (r)
+ DSSERR("failed to unregister %x isr\n", irq_mask);
+}
+
+static void dispc_mgr_disable_digit_out(void)
+{
+ DECLARE_COMPLETION_ONSTACK(framedone_compl);
+ int r, i;
+ u32 irq_mask;
+ int num_irqs;
+
+ if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT) == false)
+ return;
+
+ /*
+ * When we disable the digit output, we need to wait for FRAMEDONE to
+ * know that DISPC has finished with the output.
+ */
+
+ irq_mask = dispc_mgr_get_framedone_irq(OMAP_DSS_CHANNEL_DIGIT);
+ num_irqs = 1;
+
+ if (!irq_mask) {
+ /*
+ * omap 2/3 don't have framedone irq for TV, so we need to use
+ * vsyncs for this.
+ */
+
+ irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT);
+ /*
+ * We need to wait for both even and odd vsyncs. Note that this
+ * is not totally reliable, as we could get a vsync interrupt
+ * before we disable the output, which leads to timeout in the
+ * wait_for_completion.
+ */
+ num_irqs = 2;
+ }
+
+ r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl,
+ irq_mask);
+ if (r)
+ DSSERR("failed to register %x isr\n", irq_mask);
+
+ dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, false);
+
+ /* if we couldn't register the irq, just sleep and exit */
+ if (r) {
+ msleep(100);
+ return;
+ }
+
+ for (i = 0; i < num_irqs; ++i) {
+ if (!wait_for_completion_timeout(&framedone_compl,
+ msecs_to_jiffies(100)))
+ DSSERR("timeout waiting for digit out to stop\n");
+ }
+
+ r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl,
+ irq_mask);
+ if (r)
+ DSSERR("failed to unregister %x isr\n", irq_mask);
+}
+
+void dispc_mgr_enable_sync(enum omap_channel channel)
+{
+ if (dss_mgr_is_lcd(channel))
+ dispc_mgr_enable_lcd_out(channel);
+ else if (channel == OMAP_DSS_CHANNEL_DIGIT)
+ dispc_mgr_enable_digit_out();
+ else
+ WARN_ON(1);
+}
+
+void dispc_mgr_disable_sync(enum omap_channel channel)
+{
+ if (dss_mgr_is_lcd(channel))
+ dispc_mgr_disable_lcd_out(channel);
+ else if (channel == OMAP_DSS_CHANNEL_DIGIT)
+ dispc_mgr_disable_digit_out();
+ else
+ WARN_ON(1);
+}
+
+int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
+ unsigned long timeout)
+{
+ void dispc_irq_wait_handler(void *data, u32 mask)
+ {
+ complete((struct completion *)data);
+ }
+
+ int r;
+ DECLARE_COMPLETION_ONSTACK(completion);
+
+ r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion,
+ irqmask);
+
+ if (r)
+ return r;
+
+ timeout = wait_for_completion_interruptible_timeout(&completion,
+ timeout);
+
+ omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask);
+
+ if (timeout == 0)
+ return -ETIMEDOUT;
+
+ if (timeout == -ERESTARTSYS)
+ return -ERESTARTSYS;
+
+ return 0;
+}
diff --git a/drivers/video/omap2/dss/dispc-compat.h b/drivers/video/omap2/dss/dispc-compat.h
new file mode 100644
index 00000000000..14a69b3d4fb
--- /dev/null
+++ b/drivers/video/omap2/dss/dispc-compat.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2012 Texas Instruments
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __OMAP2_DSS_DISPC_COMPAT_H
+#define __OMAP2_DSS_DISPC_COMPAT_H
+
+void dispc_mgr_enable_sync(enum omap_channel channel);
+void dispc_mgr_disable_sync(enum omap_channel channel);
+
+int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
+ unsigned long timeout);
+
+int dss_dispc_initialize_irq(void);
+void dss_dispc_uninitialize_irq(void);
+
+#endif
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index a5ab354f267..05ff2b91d9e 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -33,9 +33,9 @@
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/hardirq.h>
-#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/sizes.h>
#include <video/omapdss.h>
@@ -46,21 +46,6 @@
/* DISPC */
#define DISPC_SZ_REGS SZ_4K
-#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \
- DISPC_IRQ_OCP_ERR | \
- DISPC_IRQ_VID1_FIFO_UNDERFLOW | \
- DISPC_IRQ_VID2_FIFO_UNDERFLOW | \
- DISPC_IRQ_SYNC_LOST | \
- DISPC_IRQ_SYNC_LOST_DIGIT)
-
-#define DISPC_MAX_NR_ISRS 8
-
-struct omap_dispc_isr_data {
- omap_dispc_isr_t isr;
- void *arg;
- u32 mask;
-};
-
enum omap_burst_size {
BURST_SIZE_X2 = 0,
BURST_SIZE_X4 = 1,
@@ -73,12 +58,6 @@ enum omap_burst_size {
#define REG_FLD_MOD(idx, val, start, end) \
dispc_write_reg(idx, FLD_MOD(dispc_read_reg(idx), val, start, end))
-struct dispc_irq_stats {
- unsigned long last_reset;
- unsigned irq_count;
- unsigned irqs[32];
-};
-
struct dispc_features {
u8 sw_start;
u8 fp_start;
@@ -86,19 +65,26 @@ struct dispc_features {
u16 sw_max;
u16 vp_max;
u16 hp_max;
- int (*calc_scaling) (enum omap_plane plane,
+ u8 mgr_width_start;
+ u8 mgr_height_start;
+ u16 mgr_width_max;
+ u16 mgr_height_max;
+ int (*calc_scaling) (unsigned long pclk, unsigned long lclk,
const struct omap_video_timings *mgr_timings,
u16 width, u16 height, u16 out_width, u16 out_height,
enum omap_color_mode color_mode, bool *five_taps,
int *x_predecim, int *y_predecim, int *decim_x, int *decim_y,
u16 pos_x, unsigned long *core_clk, bool mem_to_mem);
- unsigned long (*calc_core_clk) (enum omap_plane plane,
+ unsigned long (*calc_core_clk) (unsigned long pclk,
u16 width, u16 height, u16 out_width, u16 out_height,
bool mem_to_mem);
u8 num_fifos;
/* swap GFX & WB fifos */
bool gfx_fifo_workaround:1;
+
+ /* no DISPC_IRQ_FRAMEDONETV on this SoC */
+ bool no_framedone_tv:1;
};
#define DISPC_MAX_NR_FIFOS 5
@@ -110,27 +96,15 @@ static struct {
int ctx_loss_cnt;
int irq;
- struct clk *dss_clk;
u32 fifo_size[DISPC_MAX_NR_FIFOS];
/* maps which plane is using a fifo. fifo-id -> plane-id */
int fifo_assignment[DISPC_MAX_NR_FIFOS];
- spinlock_t irq_lock;
- u32 irq_error_mask;
- struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS];
- u32 error_irqs;
- struct work_struct error_work;
-
bool ctx_valid;
u32 ctx[DISPC_SZ_REGS / sizeof(u32)];
const struct dispc_features *feat;
-
-#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
- spinlock_t irq_stats_lock;
- struct dispc_irq_stats irq_stats;
-#endif
} dispc;
enum omap_color_component {
@@ -186,7 +160,7 @@ static const struct {
[OMAP_DSS_CHANNEL_DIGIT] = {
.name = "DIGIT",
.vsync_irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN,
- .framedone_irq = 0,
+ .framedone_irq = DISPC_IRQ_FRAMEDONETV,
.sync_lost_irq = DISPC_IRQ_SYNC_LOST_DIGIT,
.reg_desc = {
[DISPC_MGR_FLD_ENABLE] = { DISPC_CONTROL, 1, 1 },
@@ -241,7 +215,6 @@ struct color_conv_coef {
int full_range;
};
-static void _omap_dispc_set_irqs(void);
static unsigned long dispc_plane_pclk_rate(enum omap_plane plane);
static unsigned long dispc_plane_lclk_rate(enum omap_plane plane);
@@ -374,7 +347,7 @@ static void dispc_save_context(void)
if (dss_has_feature(FEAT_CORE_CLK_DIV))
SR(DIVISOR);
- dispc.ctx_loss_cnt = dss_get_ctx_loss_count(&dispc.pdev->dev);
+ dispc.ctx_loss_cnt = dss_get_ctx_loss_count();
dispc.ctx_valid = true;
DSSDBG("context saved, ctx_loss_count %d\n", dispc.ctx_loss_cnt);
@@ -389,7 +362,7 @@ static void dispc_restore_context(void)
if (!dispc.ctx_valid)
return;
- ctx = dss_get_ctx_loss_count(&dispc.pdev->dev);
+ ctx = dss_get_ctx_loss_count();
if (ctx >= 0 && ctx == dispc.ctx_loss_cnt)
return;
@@ -496,7 +469,7 @@ static void dispc_restore_context(void)
if (dss_has_feature(FEAT_MGR_LCD3))
RR(CONTROL3);
/* clear spurious SYNC_LOST_DIGIT interrupts */
- dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT);
+ dispc_clear_irqstatus(DISPC_IRQ_SYNC_LOST_DIGIT);
/*
* enable last so IRQs won't trigger before
@@ -520,6 +493,7 @@ int dispc_runtime_get(void)
WARN_ON(r < 0);
return r < 0 ? r : 0;
}
+EXPORT_SYMBOL(dispc_runtime_get);
void dispc_runtime_put(void)
{
@@ -530,16 +504,28 @@ void dispc_runtime_put(void)
r = pm_runtime_put_sync(&dispc.pdev->dev);
WARN_ON(r < 0 && r != -ENOSYS);
}
+EXPORT_SYMBOL(dispc_runtime_put);
u32 dispc_mgr_get_vsync_irq(enum omap_channel channel)
{
return mgr_desc[channel].vsync_irq;
}
+EXPORT_SYMBOL(dispc_mgr_get_vsync_irq);
u32 dispc_mgr_get_framedone_irq(enum omap_channel channel)
{
+ if (channel == OMAP_DSS_CHANNEL_DIGIT && dispc.feat->no_framedone_tv)
+ return 0;
+
return mgr_desc[channel].framedone_irq;
}
+EXPORT_SYMBOL(dispc_mgr_get_framedone_irq);
+
+u32 dispc_mgr_get_sync_lost_irq(enum omap_channel channel)
+{
+ return mgr_desc[channel].sync_lost_irq;
+}
+EXPORT_SYMBOL(dispc_mgr_get_sync_lost_irq);
u32 dispc_wb_get_framedone_irq(void)
{
@@ -550,28 +536,18 @@ bool dispc_mgr_go_busy(enum omap_channel channel)
{
return mgr_fld_read(channel, DISPC_MGR_FLD_GO) == 1;
}
+EXPORT_SYMBOL(dispc_mgr_go_busy);
void dispc_mgr_go(enum omap_channel channel)
{
- bool enable_bit, go_bit;
-
- /* if the channel is not enabled, we don't need GO */
- enable_bit = mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE) == 1;
-
- if (!enable_bit)
- return;
-
- go_bit = mgr_fld_read(channel, DISPC_MGR_FLD_GO) == 1;
-
- if (go_bit) {
- DSSERR("GO bit not down for channel %d\n", channel);
- return;
- }
+ WARN_ON(dispc_mgr_is_enabled(channel) == false);
+ WARN_ON(dispc_mgr_go_busy(channel));
DSSDBG("GO %s\n", mgr_desc[channel].name);
mgr_fld_write(channel, DISPC_MGR_FLD_GO, 1);
}
+EXPORT_SYMBOL(dispc_mgr_go);
bool dispc_wb_go_busy(void)
{
@@ -975,6 +951,7 @@ void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel)
}
dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val);
}
+EXPORT_SYMBOL(dispc_ovl_set_channel_out);
static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane plane)
{
@@ -1040,7 +1017,7 @@ static void dispc_configure_burst_sizes(void)
const int burst_size = BURST_SIZE_X8;
/* Configure burst size always to maximum size */
- for (i = 0; i < omap_dss_get_num_overlays(); ++i)
+ for (i = 0; i < dss_feat_get_num_ovls(); ++i)
dispc_ovl_set_burst_size(i, burst_size);
}
@@ -1074,7 +1051,7 @@ static void dispc_mgr_enable_cpr(enum omap_channel channel, bool enable)
}
static void dispc_mgr_set_cpr_coef(enum omap_channel channel,
- struct omap_dss_cpr_coefs *coefs)
+ const struct omap_dss_cpr_coefs *coefs)
{
u32 coef_r, coef_g, coef_b;
@@ -1122,7 +1099,9 @@ static void dispc_mgr_set_size(enum omap_channel channel, u16 width,
{
u32 val;
- val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
+ val = FLD_VAL(height - 1, dispc.feat->mgr_height_start, 16) |
+ FLD_VAL(width - 1, dispc.feat->mgr_width_start, 0);
+
dispc_write_reg(DISPC_SIZE_MGR(channel), val);
}
@@ -1244,7 +1223,7 @@ void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane,
if (use_fifomerge) {
total_fifo_size = 0;
- for (i = 0; i < omap_dss_get_num_overlays(); ++i)
+ for (i = 0; i < dss_feat_get_num_ovls(); ++i)
total_fifo_size += dispc_ovl_get_fifo_size(i);
} else {
total_fifo_size = ovl_fifo_size;
@@ -1989,16 +1968,14 @@ static void calc_tiler_rotation_offset(u16 screen_width, u16 width,
* This function is used to avoid synclosts in OMAP3, because of some
* undocumented horizontal position and timing related limitations.
*/
-static int check_horiz_timing_omap3(enum omap_plane plane,
+static int check_horiz_timing_omap3(unsigned long pclk, unsigned long lclk,
const struct omap_video_timings *t, u16 pos_x,
u16 width, u16 height, u16 out_width, u16 out_height)
{
- int DS = DIV_ROUND_UP(height, out_height);
+ const int ds = DIV_ROUND_UP(height, out_height);
unsigned long nonactive;
static const u8 limits[3] = { 8, 10, 20 };
u64 val, blank;
- unsigned long pclk = dispc_plane_pclk_rate(plane);
- unsigned long lclk = dispc_plane_lclk_rate(plane);
int i;
nonactive = t->x_res + t->hfp + t->hsw + t->hbp - out_width;
@@ -2020,8 +1997,8 @@ static int check_horiz_timing_omap3(enum omap_plane plane,
*/
val = div_u64((u64)(nonactive - pos_x) * lclk, pclk);
DSSDBG("(nonactive - pos_x) * pcd = %llu max(0, DS - 2) * width = %d\n",
- val, max(0, DS - 2) * width);
- if (val < max(0, DS - 2) * width)
+ val, max(0, ds - 2) * width);
+ if (val < max(0, ds - 2) * width)
return -EINVAL;
/*
@@ -2031,21 +2008,20 @@ static int check_horiz_timing_omap3(enum omap_plane plane,
*/
val = div_u64((u64)nonactive * lclk, pclk);
DSSDBG("nonactive * pcd = %llu, max(0, DS - 1) * width = %d\n",
- val, max(0, DS - 1) * width);
- if (val < max(0, DS - 1) * width)
+ val, max(0, ds - 1) * width);
+ if (val < max(0, ds - 1) * width)
return -EINVAL;
return 0;
}
-static unsigned long calc_core_clk_five_taps(enum omap_plane plane,
+static unsigned long calc_core_clk_five_taps(unsigned long pclk,
const struct omap_video_timings *mgr_timings, u16 width,
u16 height, u16 out_width, u16 out_height,
enum omap_color_mode color_mode)
{
u32 core_clk = 0;
u64 tmp;
- unsigned long pclk = dispc_plane_pclk_rate(plane);
if (height <= out_height && width <= out_width)
return (unsigned long) pclk;
@@ -2079,22 +2055,19 @@ static unsigned long calc_core_clk_five_taps(enum omap_plane plane,
return core_clk;
}
-static unsigned long calc_core_clk_24xx(enum omap_plane plane, u16 width,
+static unsigned long calc_core_clk_24xx(unsigned long pclk, u16 width,
u16 height, u16 out_width, u16 out_height, bool mem_to_mem)
{
- unsigned long pclk = dispc_plane_pclk_rate(plane);
-
if (height > out_height && width > out_width)
return pclk * 4;
else
return pclk * 2;
}
-static unsigned long calc_core_clk_34xx(enum omap_plane plane, u16 width,
+static unsigned long calc_core_clk_34xx(unsigned long pclk, u16 width,
u16 height, u16 out_width, u16 out_height, bool mem_to_mem)
{
unsigned int hf, vf;
- unsigned long pclk = dispc_plane_pclk_rate(plane);
/*
* FIXME how to determine the 'A' factor
@@ -2117,11 +2090,9 @@ static unsigned long calc_core_clk_34xx(enum omap_plane plane, u16 width,
return pclk * vf * hf;
}
-static unsigned long calc_core_clk_44xx(enum omap_plane plane, u16 width,
+static unsigned long calc_core_clk_44xx(unsigned long pclk, u16 width,
u16 height, u16 out_width, u16 out_height, bool mem_to_mem)
{
- unsigned long pclk;
-
/*
* If the overlay/writeback is in mem to mem mode, there are no
* downscaling limitations with respect to pixel clock, return 1 as
@@ -2131,15 +2102,13 @@ static unsigned long calc_core_clk_44xx(enum omap_plane plane, u16 width,
if (mem_to_mem)
return 1;
- pclk = dispc_plane_pclk_rate(plane);
-
if (width > out_width)
return DIV_ROUND_UP(pclk, out_width) * width;
else
return pclk;
}
-static int dispc_ovl_calc_scaling_24xx(enum omap_plane plane,
+static int dispc_ovl_calc_scaling_24xx(unsigned long pclk, unsigned long lclk,
const struct omap_video_timings *mgr_timings,
u16 width, u16 height, u16 out_width, u16 out_height,
enum omap_color_mode color_mode, bool *five_taps,
@@ -2157,7 +2126,7 @@ static int dispc_ovl_calc_scaling_24xx(enum omap_plane plane,
do {
in_height = DIV_ROUND_UP(height, *decim_y);
in_width = DIV_ROUND_UP(width, *decim_x);
- *core_clk = dispc.feat->calc_core_clk(plane, in_width,
+ *core_clk = dispc.feat->calc_core_clk(pclk, in_width,
in_height, out_width, out_height, mem_to_mem);
error = (in_width > maxsinglelinewidth || !*core_clk ||
*core_clk > dispc_core_clk_rate());
@@ -2180,7 +2149,7 @@ static int dispc_ovl_calc_scaling_24xx(enum omap_plane plane,
return 0;
}
-static int dispc_ovl_calc_scaling_34xx(enum omap_plane plane,
+static int dispc_ovl_calc_scaling_34xx(unsigned long pclk, unsigned long lclk,
const struct omap_video_timings *mgr_timings,
u16 width, u16 height, u16 out_width, u16 out_height,
enum omap_color_mode color_mode, bool *five_taps,
@@ -2196,10 +2165,10 @@ static int dispc_ovl_calc_scaling_34xx(enum omap_plane plane,
do {
in_height = DIV_ROUND_UP(height, *decim_y);
in_width = DIV_ROUND_UP(width, *decim_x);
- *core_clk = calc_core_clk_five_taps(plane, mgr_timings,
+ *core_clk = calc_core_clk_five_taps(pclk, mgr_timings,
in_width, in_height, out_width, out_height, color_mode);
- error = check_horiz_timing_omap3(plane, mgr_timings,
+ error = check_horiz_timing_omap3(pclk, lclk, mgr_timings,
pos_x, in_width, in_height, out_width,
out_height);
@@ -2208,7 +2177,7 @@ static int dispc_ovl_calc_scaling_34xx(enum omap_plane plane,
in_height < out_height * 2)
*five_taps = false;
if (!*five_taps)
- *core_clk = dispc.feat->calc_core_clk(plane, in_width,
+ *core_clk = dispc.feat->calc_core_clk(pclk, in_width,
in_height, out_width, out_height,
mem_to_mem);
@@ -2227,8 +2196,8 @@ static int dispc_ovl_calc_scaling_34xx(enum omap_plane plane,
}
} while (*decim_x <= *x_predecim && *decim_y <= *y_predecim && error);
- if (check_horiz_timing_omap3(plane, mgr_timings, pos_x, width, height,
- out_width, out_height)){
+ if (check_horiz_timing_omap3(pclk, lclk, mgr_timings, pos_x, width,
+ height, out_width, out_height)){
DSSERR("horizontal timing too tight\n");
return -EINVAL;
}
@@ -2246,7 +2215,7 @@ static int dispc_ovl_calc_scaling_34xx(enum omap_plane plane,
return 0;
}
-static int dispc_ovl_calc_scaling_44xx(enum omap_plane plane,
+static int dispc_ovl_calc_scaling_44xx(unsigned long pclk, unsigned long lclk,
const struct omap_video_timings *mgr_timings,
u16 width, u16 height, u16 out_width, u16 out_height,
enum omap_color_mode color_mode, bool *five_taps,
@@ -2258,14 +2227,14 @@ static int dispc_ovl_calc_scaling_44xx(enum omap_plane plane,
u16 in_height = DIV_ROUND_UP(height, *decim_y);
const int maxsinglelinewidth =
dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH);
- unsigned long pclk = dispc_plane_pclk_rate(plane);
const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE);
- if (mem_to_mem)
- in_width_max = DIV_ROUND_UP(out_width, maxdownscale);
- else
+ if (mem_to_mem) {
+ in_width_max = out_width * maxdownscale;
+ } else {
in_width_max = dispc_core_clk_rate() /
DIV_ROUND_UP(pclk, out_width);
+ }
*decim_x = DIV_ROUND_UP(width, in_width_max);
@@ -2283,12 +2252,12 @@ static int dispc_ovl_calc_scaling_44xx(enum omap_plane plane,
return -EINVAL;
}
- *core_clk = dispc.feat->calc_core_clk(plane, in_width, in_height,
+ *core_clk = dispc.feat->calc_core_clk(pclk, in_width, in_height,
out_width, out_height, mem_to_mem);
return 0;
}
-static int dispc_ovl_calc_scaling(enum omap_plane plane,
+static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk,
enum omap_overlay_caps caps,
const struct omap_video_timings *mgr_timings,
u16 width, u16 height, u16 out_width, u16 out_height,
@@ -2307,9 +2276,14 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane,
if ((caps & OMAP_DSS_OVL_CAP_SCALE) == 0)
return -EINVAL;
- *x_predecim = max_decim_limit;
- *y_predecim = (rotation_type == OMAP_DSS_ROT_TILER &&
- dss_has_feature(FEAT_BURST_2D)) ? 2 : max_decim_limit;
+ if (mem_to_mem) {
+ *x_predecim = *y_predecim = 1;
+ } else {
+ *x_predecim = max_decim_limit;
+ *y_predecim = (rotation_type == OMAP_DSS_ROT_TILER &&
+ dss_has_feature(FEAT_BURST_2D)) ?
+ 2 : max_decim_limit;
+ }
if (color_mode == OMAP_DSS_COLOR_CLUT1 ||
color_mode == OMAP_DSS_COLOR_CLUT2 ||
@@ -2330,7 +2304,7 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane,
if (decim_y > *y_predecim || out_height > height * 8)
return -EINVAL;
- ret = dispc.feat->calc_scaling(plane, mgr_timings, width, height,
+ ret = dispc.feat->calc_scaling(pclk, lclk, mgr_timings, width, height,
out_width, out_height, color_mode, five_taps,
x_predecim, y_predecim, &decim_x, &decim_y, pos_x, &core_clk,
mem_to_mem);
@@ -2353,6 +2327,47 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane,
return 0;
}
+int dispc_ovl_check(enum omap_plane plane, enum omap_channel channel,
+ const struct omap_overlay_info *oi,
+ const struct omap_video_timings *timings,
+ int *x_predecim, int *y_predecim)
+{
+ enum omap_overlay_caps caps = dss_feat_get_overlay_caps(plane);
+ bool five_taps = true;
+ bool fieldmode = 0;
+ u16 in_height = oi->height;
+ u16 in_width = oi->width;
+ bool ilace = timings->interlace;
+ u16 out_width, out_height;
+ int pos_x = oi->pos_x;
+ unsigned long pclk = dispc_mgr_pclk_rate(channel);
+ unsigned long lclk = dispc_mgr_lclk_rate(channel);
+
+ out_width = oi->out_width == 0 ? oi->width : oi->out_width;
+ out_height = oi->out_height == 0 ? oi->height : oi->out_height;
+
+ if (ilace && oi->height == out_height)
+ fieldmode = 1;
+
+ if (ilace) {
+ if (fieldmode)
+ in_height /= 2;
+ out_height /= 2;
+
+ DSSDBG("adjusting for ilace: height %d, out_height %d\n",
+ in_height, out_height);
+ }
+
+ if (!dss_feat_color_mode_supported(plane, oi->color_mode))
+ return -EINVAL;
+
+ return dispc_ovl_calc_scaling(pclk, lclk, caps, timings, in_width,
+ in_height, out_width, out_height, oi->color_mode,
+ &five_taps, x_predecim, y_predecim, pos_x,
+ oi->rotation_type, false);
+}
+EXPORT_SYMBOL(dispc_ovl_check);
+
static int dispc_ovl_setup_common(enum omap_plane plane,
enum omap_overlay_caps caps, u32 paddr, u32 p_uv_addr,
u16 screen_width, int pos_x, int pos_y, u16 width, u16 height,
@@ -2368,12 +2383,14 @@ static int dispc_ovl_setup_common(enum omap_plane plane,
unsigned offset0, offset1;
s32 row_inc;
s32 pix_inc;
- u16 frame_height = height;
+ u16 frame_width, frame_height;
unsigned int field_offset = 0;
u16 in_height = height;
u16 in_width = width;
int x_predecim = 1, y_predecim = 1;
bool ilace = mgr_timings->interlace;
+ unsigned long pclk = dispc_plane_pclk_rate(plane);
+ unsigned long lclk = dispc_plane_lclk_rate(plane);
if (paddr == 0)
return -EINVAL;
@@ -2398,7 +2415,7 @@ static int dispc_ovl_setup_common(enum omap_plane plane,
if (!dss_feat_color_mode_supported(plane, color_mode))
return -EINVAL;
- r = dispc_ovl_calc_scaling(plane, caps, mgr_timings, in_width,
+ r = dispc_ovl_calc_scaling(pclk, lclk, caps, mgr_timings, in_width,
in_height, out_width, out_height, color_mode,
&five_taps, &x_predecim, &y_predecim, pos_x,
rotation_type, mem_to_mem);
@@ -2436,20 +2453,28 @@ static int dispc_ovl_setup_common(enum omap_plane plane,
row_inc = 0;
pix_inc = 0;
+ if (plane == OMAP_DSS_WB) {
+ frame_width = out_width;
+ frame_height = out_height;
+ } else {
+ frame_width = in_width;
+ frame_height = height;
+ }
+
if (rotation_type == OMAP_DSS_ROT_TILER)
- calc_tiler_rotation_offset(screen_width, in_width,
+ calc_tiler_rotation_offset(screen_width, frame_width,
color_mode, fieldmode, field_offset,
&offset0, &offset1, &row_inc, &pix_inc,
x_predecim, y_predecim);
else if (rotation_type == OMAP_DSS_ROT_DMA)
- calc_dma_rotation_offset(rotation, mirror,
- screen_width, in_width, frame_height,
+ calc_dma_rotation_offset(rotation, mirror, screen_width,
+ frame_width, frame_height,
color_mode, fieldmode, field_offset,
&offset0, &offset1, &row_inc, &pix_inc,
x_predecim, y_predecim);
else
calc_vrfb_rotation_offset(rotation, mirror,
- screen_width, in_width, frame_height,
+ screen_width, frame_width, frame_height,
color_mode, fieldmode, field_offset,
&offset0, &offset1, &row_inc, &pix_inc,
x_predecim, y_predecim);
@@ -2503,7 +2528,7 @@ int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi,
bool mem_to_mem)
{
int r;
- struct omap_overlay *ovl = omap_dss_get_overlay(plane);
+ enum omap_overlay_caps caps = dss_feat_get_overlay_caps(plane);
enum omap_channel channel;
channel = dispc_ovl_get_channel_out(plane);
@@ -2514,7 +2539,7 @@ int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi,
oi->pos_y, oi->width, oi->height, oi->out_width, oi->out_height,
oi->color_mode, oi->rotation, oi->mirror, channel, replication);
- r = dispc_ovl_setup_common(plane, ovl->caps, oi->paddr, oi->p_uv_addr,
+ r = dispc_ovl_setup_common(plane, caps, oi->paddr, oi->p_uv_addr,
oi->screen_width, oi->pos_x, oi->pos_y, oi->width, oi->height,
oi->out_width, oi->out_height, oi->color_mode, oi->rotation,
oi->mirror, oi->zorder, oi->pre_mult_alpha, oi->global_alpha,
@@ -2522,6 +2547,7 @@ int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi,
return r;
}
+EXPORT_SYMBOL(dispc_ovl_setup);
int dispc_wb_setup(const struct omap_dss_writeback_info *wi,
bool mem_to_mem, const struct omap_video_timings *mgr_timings)
@@ -2582,192 +2608,39 @@ int dispc_ovl_enable(enum omap_plane plane, bool enable)
return 0;
}
+EXPORT_SYMBOL(dispc_ovl_enable);
-static void dispc_disable_isr(void *data, u32 mask)
+bool dispc_ovl_enabled(enum omap_plane plane)
{
- struct completion *compl = data;
- complete(compl);
+ return REG_GET(DISPC_OVL_ATTRIBUTES(plane), 0, 0);
}
+EXPORT_SYMBOL(dispc_ovl_enabled);
-static void _enable_lcd_out(enum omap_channel channel, bool enable)
+void dispc_mgr_enable(enum omap_channel channel, bool enable)
{
mgr_fld_write(channel, DISPC_MGR_FLD_ENABLE, enable);
/* flush posted write */
mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
}
-
-static void dispc_mgr_enable_lcd_out(enum omap_channel channel, bool enable)
-{
- struct completion frame_done_completion;
- bool is_on;
- int r;
- u32 irq;
-
- /* When we disable LCD output, we need to wait until frame is done.
- * Otherwise the DSS is still working, and turning off the clocks
- * prevents DSS from going to OFF mode */
- is_on = mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
-
- irq = mgr_desc[channel].framedone_irq;
-
- if (!enable && is_on) {
- init_completion(&frame_done_completion);
-
- r = omap_dispc_register_isr(dispc_disable_isr,
- &frame_done_completion, irq);
-
- if (r)
- DSSERR("failed to register FRAMEDONE isr\n");
- }
-
- _enable_lcd_out(channel, enable);
-
- if (!enable && is_on) {
- if (!wait_for_completion_timeout(&frame_done_completion,
- msecs_to_jiffies(100)))
- DSSERR("timeout waiting for FRAME DONE\n");
-
- r = omap_dispc_unregister_isr(dispc_disable_isr,
- &frame_done_completion, irq);
-
- if (r)
- DSSERR("failed to unregister FRAMEDONE isr\n");
- }
-}
-
-static void _enable_digit_out(bool enable)
-{
- REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 1, 1);
- /* flush posted write */
- dispc_read_reg(DISPC_CONTROL);
-}
-
-static void dispc_mgr_enable_digit_out(bool enable)
-{
- struct completion frame_done_completion;
- enum dss_hdmi_venc_clk_source_select src;
- int r, i;
- u32 irq_mask;
- int num_irqs;
-
- if (REG_GET(DISPC_CONTROL, 1, 1) == enable)
- return;
-
- src = dss_get_hdmi_venc_clk_source();
-
- if (enable) {
- unsigned long flags;
- /* When we enable digit output, we'll get an extra digit
- * sync lost interrupt, that we need to ignore */
- spin_lock_irqsave(&dispc.irq_lock, flags);
- dispc.irq_error_mask &= ~DISPC_IRQ_SYNC_LOST_DIGIT;
- _omap_dispc_set_irqs();
- spin_unlock_irqrestore(&dispc.irq_lock, flags);
- }
-
- /* When we disable digit output, we need to wait until fields are done.
- * Otherwise the DSS is still working, and turning off the clocks
- * prevents DSS from going to OFF mode. And when enabling, we need to
- * wait for the extra sync losts */
- init_completion(&frame_done_completion);
-
- if (src == DSS_HDMI_M_PCLK && enable == false) {
- irq_mask = DISPC_IRQ_FRAMEDONETV;
- num_irqs = 1;
- } else {
- irq_mask = DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD;
- /* XXX I understand from TRM that we should only wait for the
- * current field to complete. But it seems we have to wait for
- * both fields */
- num_irqs = 2;
- }
-
- r = omap_dispc_register_isr(dispc_disable_isr, &frame_done_completion,
- irq_mask);
- if (r)
- DSSERR("failed to register %x isr\n", irq_mask);
-
- _enable_digit_out(enable);
-
- for (i = 0; i < num_irqs; ++i) {
- if (!wait_for_completion_timeout(&frame_done_completion,
- msecs_to_jiffies(100)))
- DSSERR("timeout waiting for digit out to %s\n",
- enable ? "start" : "stop");
- }
-
- r = omap_dispc_unregister_isr(dispc_disable_isr, &frame_done_completion,
- irq_mask);
- if (r)
- DSSERR("failed to unregister %x isr\n", irq_mask);
-
- if (enable) {
- unsigned long flags;
- spin_lock_irqsave(&dispc.irq_lock, flags);
- dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST_DIGIT;
- dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT);
- _omap_dispc_set_irqs();
- spin_unlock_irqrestore(&dispc.irq_lock, flags);
- }
-}
+EXPORT_SYMBOL(dispc_mgr_enable);
bool dispc_mgr_is_enabled(enum omap_channel channel)
{
return !!mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
}
-
-void dispc_mgr_enable(enum omap_channel channel, bool enable)
-{
- if (dss_mgr_is_lcd(channel))
- dispc_mgr_enable_lcd_out(channel, enable);
- else if (channel == OMAP_DSS_CHANNEL_DIGIT)
- dispc_mgr_enable_digit_out(enable);
- else
- BUG();
-}
+EXPORT_SYMBOL(dispc_mgr_is_enabled);
void dispc_wb_enable(bool enable)
{
- enum omap_plane plane = OMAP_DSS_WB;
- struct completion frame_done_completion;
- bool is_on;
- int r;
- u32 irq;
-
- is_on = REG_GET(DISPC_OVL_ATTRIBUTES(plane), 0, 0);
- irq = DISPC_IRQ_FRAMEDONEWB;
-
- if (!enable && is_on) {
- init_completion(&frame_done_completion);
-
- r = omap_dispc_register_isr(dispc_disable_isr,
- &frame_done_completion, irq);
- if (r)
- DSSERR("failed to register FRAMEDONEWB isr\n");
- }
-
- REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 0, 0);
-
- if (!enable && is_on) {
- if (!wait_for_completion_timeout(&frame_done_completion,
- msecs_to_jiffies(100)))
- DSSERR("timeout waiting for FRAMEDONEWB\n");
-
- r = omap_dispc_unregister_isr(dispc_disable_isr,
- &frame_done_completion, irq);
- if (r)
- DSSERR("failed to unregister FRAMEDONEWB isr\n");
- }
+ dispc_ovl_enable(OMAP_DSS_WB, enable);
}
bool dispc_wb_is_enabled(void)
{
- enum omap_plane plane = OMAP_DSS_WB;
-
- return REG_GET(DISPC_OVL_ATTRIBUTES(plane), 0, 0);
+ return dispc_ovl_enabled(OMAP_DSS_WB);
}
-void dispc_lcd_enable_signal_polarity(bool act_high)
+static void dispc_lcd_enable_signal_polarity(bool act_high)
{
if (!dss_has_feature(FEAT_LCDENABLEPOL))
return;
@@ -2791,13 +2664,13 @@ void dispc_pck_free_enable(bool enable)
REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27);
}
-void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable)
+static void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable)
{
mgr_fld_write(channel, DISPC_MGR_FLD_FIFOHANDCHECK, enable);
}
-void dispc_mgr_set_lcd_type_tft(enum omap_channel channel)
+static void dispc_mgr_set_lcd_type_tft(enum omap_channel channel)
{
mgr_fld_write(channel, DISPC_MGR_FLD_STNTFT, 1);
}
@@ -2840,7 +2713,7 @@ static void dispc_mgr_enable_alpha_fixed_zorder(enum omap_channel ch,
}
void dispc_mgr_setup(enum omap_channel channel,
- struct omap_overlay_manager_info *info)
+ const struct omap_overlay_manager_info *info)
{
dispc_mgr_set_default_color(channel, info->default_color);
dispc_mgr_set_trans_key(channel, info->trans_key_type, info->trans_key);
@@ -2852,8 +2725,9 @@ void dispc_mgr_setup(enum omap_channel channel,
dispc_mgr_set_cpr_coef(channel, &info->cpr_coefs);
}
}
+EXPORT_SYMBOL(dispc_mgr_setup);
-void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines)
+static void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines)
{
int code;
@@ -2878,7 +2752,7 @@ void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines)
mgr_fld_write(channel, DISPC_MGR_FLD_TFTDATALINES, code);
}
-void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode)
+static void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode)
{
u32 l;
int gpout0, gpout1;
@@ -2907,15 +2781,33 @@ void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode)
dispc_write_reg(DISPC_CONTROL, l);
}
-void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable)
+static void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable)
{
mgr_fld_write(channel, DISPC_MGR_FLD_STALLMODE, enable);
}
+void dispc_mgr_set_lcd_config(enum omap_channel channel,
+ const struct dss_lcd_mgr_config *config)
+{
+ dispc_mgr_set_io_pad_mode(config->io_pad_mode);
+
+ dispc_mgr_enable_stallmode(channel, config->stallmode);
+ dispc_mgr_enable_fifohandcheck(channel, config->fifohandcheck);
+
+ dispc_mgr_set_clock_div(channel, &config->clock_info);
+
+ dispc_mgr_set_tft_data_lines(channel, config->video_port_width);
+
+ dispc_lcd_enable_signal_polarity(config->lcden_sig_polarity);
+
+ dispc_mgr_set_lcd_type_tft(channel);
+}
+EXPORT_SYMBOL(dispc_mgr_set_lcd_config);
+
static bool _dispc_mgr_size_ok(u16 width, u16 height)
{
- return width <= dss_feat_get_param_max(FEAT_PARAM_MGR_WIDTH) &&
- height <= dss_feat_get_param_max(FEAT_PARAM_MGR_HEIGHT);
+ return width <= dispc.feat->mgr_width_max &&
+ height <= dispc.feat->mgr_height_max;
}
static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp,
@@ -3010,7 +2902,7 @@ static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw,
/* change name to mode? */
void dispc_mgr_set_timings(enum omap_channel channel,
- struct omap_video_timings *timings)
+ const struct omap_video_timings *timings)
{
unsigned xtot, ytot;
unsigned long ht, vt;
@@ -3049,6 +2941,7 @@ void dispc_mgr_set_timings(enum omap_channel channel,
dispc_mgr_set_size(channel, t.x_res, t.y_res);
}
+EXPORT_SYMBOL(dispc_mgr_set_timings);
static void dispc_mgr_set_lcd_divisor(enum omap_channel channel, u16 lck_div,
u16 pck_div)
@@ -3076,7 +2969,7 @@ unsigned long dispc_fclk_rate(void)
switch (dss_get_dispc_clk_source()) {
case OMAP_DSS_CLK_SRC_FCK:
- r = clk_get_rate(dispc.dss_clk);
+ r = dss_get_dispc_clk_rate();
break;
case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC:
dsidev = dsi_get_dsidev_from_id(0);
@@ -3101,28 +2994,32 @@ unsigned long dispc_mgr_lclk_rate(enum omap_channel channel)
unsigned long r;
u32 l;
- l = dispc_read_reg(DISPC_DIVISORo(channel));
+ if (dss_mgr_is_lcd(channel)) {
+ l = dispc_read_reg(DISPC_DIVISORo(channel));
- lcd = FLD_GET(l, 23, 16);
+ lcd = FLD_GET(l, 23, 16);
- switch (dss_get_lcd_clk_source(channel)) {
- case OMAP_DSS_CLK_SRC_FCK:
- r = clk_get_rate(dispc.dss_clk);
- break;
- case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC:
- dsidev = dsi_get_dsidev_from_id(0);
- r = dsi_get_pll_hsdiv_dispc_rate(dsidev);
- break;
- case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC:
- dsidev = dsi_get_dsidev_from_id(1);
- r = dsi_get_pll_hsdiv_dispc_rate(dsidev);
- break;
- default:
- BUG();
- return 0;
- }
+ switch (dss_get_lcd_clk_source(channel)) {
+ case OMAP_DSS_CLK_SRC_FCK:
+ r = dss_get_dispc_clk_rate();
+ break;
+ case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC:
+ dsidev = dsi_get_dsidev_from_id(0);
+ r = dsi_get_pll_hsdiv_dispc_rate(dsidev);
+ break;
+ case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC:
+ dsidev = dsi_get_dsidev_from_id(1);
+ r = dsi_get_pll_hsdiv_dispc_rate(dsidev);
+ break;
+ default:
+ BUG();
+ return 0;
+ }
- return r / lcd;
+ return r / lcd;
+ } else {
+ return dispc_fclk_rate();
+ }
}
unsigned long dispc_mgr_pclk_rate(enum omap_channel channel)
@@ -3172,21 +3069,28 @@ unsigned long dispc_core_clk_rate(void)
static unsigned long dispc_plane_pclk_rate(enum omap_plane plane)
{
- enum omap_channel channel = dispc_ovl_get_channel_out(plane);
+ enum omap_channel channel;
+
+ if (plane == OMAP_DSS_WB)
+ return 0;
+
+ channel = dispc_ovl_get_channel_out(plane);
return dispc_mgr_pclk_rate(channel);
}
static unsigned long dispc_plane_lclk_rate(enum omap_plane plane)
{
- enum omap_channel channel = dispc_ovl_get_channel_out(plane);
+ enum omap_channel channel;
- if (dss_mgr_is_lcd(channel))
- return dispc_mgr_lclk_rate(channel);
- else
- return dispc_fclk_rate();
+ if (plane == OMAP_DSS_WB)
+ return 0;
+
+ channel = dispc_ovl_get_channel_out(plane);
+ return dispc_mgr_lclk_rate(channel);
}
+
static void dispc_dump_clocks_channel(struct seq_file *s, enum omap_channel channel)
{
int lcd, pcd;
@@ -3244,64 +3148,6 @@ void dispc_dump_clocks(struct seq_file *s)
dispc_runtime_put();
}
-#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
-void dispc_dump_irqs(struct seq_file *s)
-{
- unsigned long flags;
- struct dispc_irq_stats stats;
-
- spin_lock_irqsave(&dispc.irq_stats_lock, flags);
-
- stats = dispc.irq_stats;
- memset(&dispc.irq_stats, 0, sizeof(dispc.irq_stats));
- dispc.irq_stats.last_reset = jiffies;
-
- spin_unlock_irqrestore(&dispc.irq_stats_lock, flags);
-
- seq_printf(s, "period %u ms\n",
- jiffies_to_msecs(jiffies - stats.last_reset));
-
- seq_printf(s, "irqs %d\n", stats.irq_count);
-#define PIS(x) \
- seq_printf(s, "%-20s %10d\n", #x, stats.irqs[ffs(DISPC_IRQ_##x)-1]);
-
- PIS(FRAMEDONE);
- PIS(VSYNC);
- PIS(EVSYNC_EVEN);
- PIS(EVSYNC_ODD);
- PIS(ACBIAS_COUNT_STAT);
- PIS(PROG_LINE_NUM);
- PIS(GFX_FIFO_UNDERFLOW);
- PIS(GFX_END_WIN);
- PIS(PAL_GAMMA_MASK);
- PIS(OCP_ERR);
- PIS(VID1_FIFO_UNDERFLOW);
- PIS(VID1_END_WIN);
- PIS(VID2_FIFO_UNDERFLOW);
- PIS(VID2_END_WIN);
- if (dss_feat_get_num_ovls() > 3) {
- PIS(VID3_FIFO_UNDERFLOW);
- PIS(VID3_END_WIN);
- }
- PIS(SYNC_LOST);
- PIS(SYNC_LOST_DIGIT);
- PIS(WAKEUP);
- if (dss_has_feature(FEAT_MGR_LCD2)) {
- PIS(FRAMEDONE2);
- PIS(VSYNC2);
- PIS(ACBIAS_COUNT_STAT2);
- PIS(SYNC_LOST2);
- }
- if (dss_has_feature(FEAT_MGR_LCD3)) {
- PIS(FRAMEDONE3);
- PIS(VSYNC3);
- PIS(ACBIAS_COUNT_STAT3);
- PIS(SYNC_LOST3);
- }
-#undef PIS
-}
-#endif
-
static void dispc_dump_regs(struct seq_file *s)
{
int i, j;
@@ -3351,7 +3197,7 @@ static void dispc_dump_regs(struct seq_file *s)
#define DISPC_REG(i, name) name(i)
#define DUMPREG(i, r) seq_printf(s, "%s(%s)%*s %08x\n", #r, p_names[i], \
- 48 - strlen(#r) - strlen(p_names[i]), " ", \
+ (int)(48 - strlen(#r) - strlen(p_names[i])), " ", \
dispc_read_reg(DISPC_REG(i, r)))
p_names = mgr_names;
@@ -3428,7 +3274,7 @@ static void dispc_dump_regs(struct seq_file *s)
#define DISPC_REG(plane, name, i) name(plane, i)
#define DUMPREG(plane, name, i) \
seq_printf(s, "%s_%d(%s)%*s %08x\n", #name, i, p_names[plane], \
- 46 - strlen(#name) - strlen(p_names[plane]), " ", \
+ (int)(46 - strlen(#name) - strlen(p_names[plane])), " ", \
dispc_read_reg(DISPC_REG(plane, name, i)))
/* Video pipeline coefficient registers */
@@ -3531,7 +3377,7 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
}
void dispc_mgr_set_clock_div(enum omap_channel channel,
- struct dispc_clock_info *cinfo)
+ const struct dispc_clock_info *cinfo)
{
DSSDBG("lck = %lu (%u)\n", cinfo->lck, cinfo->lck_div);
DSSDBG("pck = %lu (%u)\n", cinfo->pck, cinfo->pck_div);
@@ -3555,403 +3401,34 @@ int dispc_mgr_get_clock_div(enum omap_channel channel,
return 0;
}
-/* dispc.irq_lock has to be locked by the caller */
-static void _omap_dispc_set_irqs(void)
+u32 dispc_read_irqstatus(void)
{
- u32 mask;
- u32 old_mask;
- int i;
- struct omap_dispc_isr_data *isr_data;
-
- mask = dispc.irq_error_mask;
-
- for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
- isr_data = &dispc.registered_isr[i];
-
- if (isr_data->isr == NULL)
- continue;
-
- mask |= isr_data->mask;
- }
-
- old_mask = dispc_read_reg(DISPC_IRQENABLE);
- /* clear the irqstatus for newly enabled irqs */
- dispc_write_reg(DISPC_IRQSTATUS, (mask ^ old_mask) & mask);
-
- dispc_write_reg(DISPC_IRQENABLE, mask);
-}
-
-int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
-{
- int i;
- int ret;
- unsigned long flags;
- struct omap_dispc_isr_data *isr_data;
-
- if (isr == NULL)
- return -EINVAL;
-
- spin_lock_irqsave(&dispc.irq_lock, flags);
-
- /* check for duplicate entry */
- for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
- isr_data = &dispc.registered_isr[i];
- if (isr_data->isr == isr && isr_data->arg == arg &&
- isr_data->mask == mask) {
- ret = -EINVAL;
- goto err;
- }
- }
-
- isr_data = NULL;
- ret = -EBUSY;
-
- for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
- isr_data = &dispc.registered_isr[i];
-
- if (isr_data->isr != NULL)
- continue;
-
- isr_data->isr = isr;
- isr_data->arg = arg;
- isr_data->mask = mask;
- ret = 0;
-
- break;
- }
-
- if (ret)
- goto err;
-
- _omap_dispc_set_irqs();
-
- spin_unlock_irqrestore(&dispc.irq_lock, flags);
-
- return 0;
-err:
- spin_unlock_irqrestore(&dispc.irq_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(omap_dispc_register_isr);
-
-int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
-{
- int i;
- unsigned long flags;
- int ret = -EINVAL;
- struct omap_dispc_isr_data *isr_data;
-
- spin_lock_irqsave(&dispc.irq_lock, flags);
-
- for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
- isr_data = &dispc.registered_isr[i];
- if (isr_data->isr != isr || isr_data->arg != arg ||
- isr_data->mask != mask)
- continue;
-
- /* found the correct isr */
-
- isr_data->isr = NULL;
- isr_data->arg = NULL;
- isr_data->mask = 0;
-
- ret = 0;
- break;
- }
-
- if (ret == 0)
- _omap_dispc_set_irqs();
-
- spin_unlock_irqrestore(&dispc.irq_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(omap_dispc_unregister_isr);
-
-#ifdef DEBUG
-static void print_irq_status(u32 status)
-{
- if ((status & dispc.irq_error_mask) == 0)
- return;
-
- printk(KERN_DEBUG "DISPC IRQ: 0x%x: ", status);
-
-#define PIS(x) \
- if (status & DISPC_IRQ_##x) \
- printk(#x " ");
- PIS(GFX_FIFO_UNDERFLOW);
- PIS(OCP_ERR);
- PIS(VID1_FIFO_UNDERFLOW);
- PIS(VID2_FIFO_UNDERFLOW);
- if (dss_feat_get_num_ovls() > 3)
- PIS(VID3_FIFO_UNDERFLOW);
- PIS(SYNC_LOST);
- PIS(SYNC_LOST_DIGIT);
- if (dss_has_feature(FEAT_MGR_LCD2))
- PIS(SYNC_LOST2);
- if (dss_has_feature(FEAT_MGR_LCD3))
- PIS(SYNC_LOST3);
-#undef PIS
-
- printk("\n");
-}
-#endif
-
-/* Called from dss.c. Note that we don't touch clocks here,
- * but we presume they are on because we got an IRQ. However,
- * an irq handler may turn the clocks off, so we may not have
- * clock later in the function. */
-static irqreturn_t omap_dispc_irq_handler(int irq, void *arg)
-{
- int i;
- u32 irqstatus, irqenable;
- u32 handledirqs = 0;
- u32 unhandled_errors;
- struct omap_dispc_isr_data *isr_data;
- struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS];
-
- spin_lock(&dispc.irq_lock);
-
- irqstatus = dispc_read_reg(DISPC_IRQSTATUS);
- irqenable = dispc_read_reg(DISPC_IRQENABLE);
-
- /* IRQ is not for us */
- if (!(irqstatus & irqenable)) {
- spin_unlock(&dispc.irq_lock);
- return IRQ_NONE;
- }
-
-#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
- spin_lock(&dispc.irq_stats_lock);
- dispc.irq_stats.irq_count++;
- dss_collect_irq_stats(irqstatus, dispc.irq_stats.irqs);
- spin_unlock(&dispc.irq_stats_lock);
-#endif
-
-#ifdef DEBUG
- if (dss_debug)
- print_irq_status(irqstatus);
-#endif
- /* Ack the interrupt. Do it here before clocks are possibly turned
- * off */
- dispc_write_reg(DISPC_IRQSTATUS, irqstatus);
- /* flush posted write */
- dispc_read_reg(DISPC_IRQSTATUS);
-
- /* make a copy and unlock, so that isrs can unregister
- * themselves */
- memcpy(registered_isr, dispc.registered_isr,
- sizeof(registered_isr));
-
- spin_unlock(&dispc.irq_lock);
-
- for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
- isr_data = &registered_isr[i];
-
- if (!isr_data->isr)
- continue;
-
- if (isr_data->mask & irqstatus) {
- isr_data->isr(isr_data->arg, irqstatus);
- handledirqs |= isr_data->mask;
- }
- }
-
- spin_lock(&dispc.irq_lock);
-
- unhandled_errors = irqstatus & ~handledirqs & dispc.irq_error_mask;
-
- if (unhandled_errors) {
- dispc.error_irqs |= unhandled_errors;
-
- dispc.irq_error_mask &= ~unhandled_errors;
- _omap_dispc_set_irqs();
-
- schedule_work(&dispc.error_work);
- }
-
- spin_unlock(&dispc.irq_lock);
-
- return IRQ_HANDLED;
-}
-
-static void dispc_error_worker(struct work_struct *work)
-{
- int i;
- u32 errors;
- unsigned long flags;
- static const unsigned fifo_underflow_bits[] = {
- DISPC_IRQ_GFX_FIFO_UNDERFLOW,
- DISPC_IRQ_VID1_FIFO_UNDERFLOW,
- DISPC_IRQ_VID2_FIFO_UNDERFLOW,
- DISPC_IRQ_VID3_FIFO_UNDERFLOW,
- };
-
- spin_lock_irqsave(&dispc.irq_lock, flags);
- errors = dispc.error_irqs;
- dispc.error_irqs = 0;
- spin_unlock_irqrestore(&dispc.irq_lock, flags);
-
- dispc_runtime_get();
-
- for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
- struct omap_overlay *ovl;
- unsigned bit;
-
- ovl = omap_dss_get_overlay(i);
- bit = fifo_underflow_bits[i];
-
- if (bit & errors) {
- DSSERR("FIFO UNDERFLOW on %s, disabling the overlay\n",
- ovl->name);
- dispc_ovl_enable(ovl->id, false);
- dispc_mgr_go(ovl->manager->id);
- msleep(50);
- }
- }
-
- for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
- struct omap_overlay_manager *mgr;
- unsigned bit;
-
- mgr = omap_dss_get_overlay_manager(i);
- bit = mgr_desc[i].sync_lost_irq;
-
- if (bit & errors) {
- struct omap_dss_device *dssdev = mgr->get_device(mgr);
- bool enable;
-
- DSSERR("SYNC_LOST on channel %s, restarting the output "
- "with video overlays disabled\n",
- mgr->name);
-
- enable = dssdev->state == OMAP_DSS_DISPLAY_ACTIVE;
- dssdev->driver->disable(dssdev);
-
- for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
- struct omap_overlay *ovl;
- ovl = omap_dss_get_overlay(i);
-
- if (ovl->id != OMAP_DSS_GFX &&
- ovl->manager == mgr)
- dispc_ovl_enable(ovl->id, false);
- }
-
- dispc_mgr_go(mgr->id);
- msleep(50);
-
- if (enable)
- dssdev->driver->enable(dssdev);
- }
- }
-
- if (errors & DISPC_IRQ_OCP_ERR) {
- DSSERR("OCP_ERR\n");
- for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
- struct omap_overlay_manager *mgr;
- struct omap_dss_device *dssdev;
-
- mgr = omap_dss_get_overlay_manager(i);
- dssdev = mgr->get_device(mgr);
-
- if (dssdev && dssdev->driver)
- dssdev->driver->disable(dssdev);
- }
- }
-
- spin_lock_irqsave(&dispc.irq_lock, flags);
- dispc.irq_error_mask |= errors;
- _omap_dispc_set_irqs();
- spin_unlock_irqrestore(&dispc.irq_lock, flags);
-
- dispc_runtime_put();
+ return dispc_read_reg(DISPC_IRQSTATUS);
}
+EXPORT_SYMBOL(dispc_read_irqstatus);
-int omap_dispc_wait_for_irq_timeout(u32 irqmask, unsigned long timeout)
+void dispc_clear_irqstatus(u32 mask)
{
- void dispc_irq_wait_handler(void *data, u32 mask)
- {
- complete((struct completion *)data);
- }
-
- int r;
- DECLARE_COMPLETION_ONSTACK(completion);
-
- r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion,
- irqmask);
-
- if (r)
- return r;
-
- timeout = wait_for_completion_timeout(&completion, timeout);
-
- omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask);
-
- if (timeout == 0)
- return -ETIMEDOUT;
-
- if (timeout == -ERESTARTSYS)
- return -ERESTARTSYS;
-
- return 0;
+ dispc_write_reg(DISPC_IRQSTATUS, mask);
}
+EXPORT_SYMBOL(dispc_clear_irqstatus);
-int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
- unsigned long timeout)
+u32 dispc_read_irqenable(void)
{
- void dispc_irq_wait_handler(void *data, u32 mask)
- {
- complete((struct completion *)data);
- }
-
- int r;
- DECLARE_COMPLETION_ONSTACK(completion);
-
- r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion,
- irqmask);
-
- if (r)
- return r;
-
- timeout = wait_for_completion_interruptible_timeout(&completion,
- timeout);
-
- omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask);
-
- if (timeout == 0)
- return -ETIMEDOUT;
-
- if (timeout == -ERESTARTSYS)
- return -ERESTARTSYS;
-
- return 0;
+ return dispc_read_reg(DISPC_IRQENABLE);
}
+EXPORT_SYMBOL(dispc_read_irqenable);
-static void _omap_dispc_initialize_irq(void)
+void dispc_write_irqenable(u32 mask)
{
- unsigned long flags;
-
- spin_lock_irqsave(&dispc.irq_lock, flags);
-
- memset(dispc.registered_isr, 0, sizeof(dispc.registered_isr));
-
- dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR;
- if (dss_has_feature(FEAT_MGR_LCD2))
- dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST2;
- if (dss_has_feature(FEAT_MGR_LCD3))
- dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST3;
- if (dss_feat_get_num_ovls() > 3)
- dispc.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW;
-
- /* there's SYNC_LOST_DIGIT waiting after enabling the DSS,
- * so clear it */
- dispc_write_reg(DISPC_IRQSTATUS, dispc_read_reg(DISPC_IRQSTATUS));
+ u32 old_mask = dispc_read_reg(DISPC_IRQENABLE);
- _omap_dispc_set_irqs();
+ /* clear the irqstatus for newly enabled irqs */
+ dispc_clear_irqstatus((mask ^ old_mask) & mask);
- spin_unlock_irqrestore(&dispc.irq_lock, flags);
+ dispc_write_reg(DISPC_IRQENABLE, mask);
}
+EXPORT_SYMBOL(dispc_write_irqenable);
void dispc_enable_sidle(void)
{
@@ -3998,9 +3475,14 @@ static const struct dispc_features omap24xx_dispc_feats __initconst = {
.sw_max = 64,
.vp_max = 255,
.hp_max = 256,
+ .mgr_width_start = 10,
+ .mgr_height_start = 26,
+ .mgr_width_max = 2048,
+ .mgr_height_max = 2048,
.calc_scaling = dispc_ovl_calc_scaling_24xx,
.calc_core_clk = calc_core_clk_24xx,
.num_fifos = 3,
+ .no_framedone_tv = true,
};
static const struct dispc_features omap34xx_rev1_0_dispc_feats __initconst = {
@@ -4010,9 +3492,14 @@ static const struct dispc_features omap34xx_rev1_0_dispc_feats __initconst = {
.sw_max = 64,
.vp_max = 255,
.hp_max = 256,
+ .mgr_width_start = 10,
+ .mgr_height_start = 26,
+ .mgr_width_max = 2048,
+ .mgr_height_max = 2048,
.calc_scaling = dispc_ovl_calc_scaling_34xx,
.calc_core_clk = calc_core_clk_34xx,
.num_fifos = 3,
+ .no_framedone_tv = true,
};
static const struct dispc_features omap34xx_rev3_0_dispc_feats __initconst = {
@@ -4022,9 +3509,14 @@ static const struct dispc_features omap34xx_rev3_0_dispc_feats __initconst = {
.sw_max = 256,
.vp_max = 4095,
.hp_max = 4096,
+ .mgr_width_start = 10,
+ .mgr_height_start = 26,
+ .mgr_width_max = 2048,
+ .mgr_height_max = 2048,
.calc_scaling = dispc_ovl_calc_scaling_34xx,
.calc_core_clk = calc_core_clk_34xx,
.num_fifos = 3,
+ .no_framedone_tv = true,
};
static const struct dispc_features omap44xx_dispc_feats __initconst = {
@@ -4034,6 +3526,27 @@ static const struct dispc_features omap44xx_dispc_feats __initconst = {
.sw_max = 256,
.vp_max = 4095,
.hp_max = 4096,
+ .mgr_width_start = 10,
+ .mgr_height_start = 26,
+ .mgr_width_max = 2048,
+ .mgr_height_max = 2048,
+ .calc_scaling = dispc_ovl_calc_scaling_44xx,
+ .calc_core_clk = calc_core_clk_44xx,
+ .num_fifos = 5,
+ .gfx_fifo_workaround = true,
+};
+
+static const struct dispc_features omap54xx_dispc_feats __initconst = {
+ .sw_start = 7,
+ .fp_start = 19,
+ .bp_start = 31,
+ .sw_max = 256,
+ .vp_max = 4095,
+ .hp_max = 4096,
+ .mgr_width_start = 11,
+ .mgr_height_start = 27,
+ .mgr_width_max = 4096,
+ .mgr_height_max = 4096,
.calc_scaling = dispc_ovl_calc_scaling_44xx,
.calc_core_clk = calc_core_clk_44xx,
.num_fifos = 5,
@@ -4042,7 +3555,6 @@ static const struct dispc_features omap44xx_dispc_feats __initconst = {
static int __init dispc_init_features(struct platform_device *pdev)
{
- struct omap_dss_board_info *pdata = pdev->dev.platform_data;
const struct dispc_features *src;
struct dispc_features *dst;
@@ -4052,7 +3564,7 @@ static int __init dispc_init_features(struct platform_device *pdev)
return -ENOMEM;
}
- switch (pdata->version) {
+ switch (omapdss_get_version()) {
case OMAPDSS_VER_OMAP24xx:
src = &omap24xx_dispc_feats;
break;
@@ -4074,7 +3586,7 @@ static int __init dispc_init_features(struct platform_device *pdev)
break;
case OMAPDSS_VER_OMAP5:
- src = &omap44xx_dispc_feats;
+ src = &omap54xx_dispc_feats;
break;
default:
@@ -4087,13 +3599,25 @@ static int __init dispc_init_features(struct platform_device *pdev)
return 0;
}
+int dispc_request_irq(irq_handler_t handler, void *dev_id)
+{
+ return devm_request_irq(&dispc.pdev->dev, dispc.irq, handler,
+ IRQF_SHARED, "OMAP DISPC", dev_id);
+}
+EXPORT_SYMBOL(dispc_request_irq);
+
+void dispc_free_irq(void *dev_id)
+{
+ devm_free_irq(&dispc.pdev->dev, dispc.irq, dev_id);
+}
+EXPORT_SYMBOL(dispc_free_irq);
+
/* DISPC HW IP initialisation */
static int __init omap_dispchw_probe(struct platform_device *pdev)
{
u32 rev;
int r = 0;
struct resource *dispc_mem;
- struct clk *clk;
dispc.pdev = pdev;
@@ -4101,15 +3625,6 @@ static int __init omap_dispchw_probe(struct platform_device *pdev)
if (r)
return r;
- spin_lock_init(&dispc.irq_lock);
-
-#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
- spin_lock_init(&dispc.irq_stats_lock);
- dispc.irq_stats.last_reset = jiffies;
-#endif
-
- INIT_WORK(&dispc.error_work, dispc_error_worker);
-
dispc_mem = platform_get_resource(dispc.pdev, IORESOURCE_MEM, 0);
if (!dispc_mem) {
DSSERR("can't get IORESOURCE_MEM DISPC\n");
@@ -4129,22 +3644,6 @@ static int __init omap_dispchw_probe(struct platform_device *pdev)
return -ENODEV;
}
- r = devm_request_irq(&pdev->dev, dispc.irq, omap_dispc_irq_handler,
- IRQF_SHARED, "OMAP DISPC", dispc.pdev);
- if (r < 0) {
- DSSERR("request_irq failed\n");
- return r;
- }
-
- clk = clk_get(&pdev->dev, "fck");
- if (IS_ERR(clk)) {
- DSSERR("can't get fck\n");
- r = PTR_ERR(clk);
- return r;
- }
-
- dispc.dss_clk = clk;
-
pm_runtime_enable(&pdev->dev);
r = dispc_runtime_get();
@@ -4153,8 +3652,6 @@ static int __init omap_dispchw_probe(struct platform_device *pdev)
_omap_dispc_initial_config();
- _omap_dispc_initialize_irq();
-
rev = dispc_read_reg(DISPC_REVISION);
dev_dbg(&pdev->dev, "OMAP DISPC rev %d.%d\n",
FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
@@ -4163,14 +3660,10 @@ static int __init omap_dispchw_probe(struct platform_device *pdev)
dss_debugfs_create_file("dispc", dispc_dump_regs);
-#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
- dss_debugfs_create_file("dispc_irq", dispc_dump_irqs);
-#endif
return 0;
err_runtime_get:
pm_runtime_disable(&pdev->dev);
- clk_put(dispc.dss_clk);
return r;
}
@@ -4178,8 +3671,6 @@ static int __exit omap_dispchw_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
- clk_put(dispc.dss_clk);
-
return 0;
}
diff --git a/drivers/video/omap2/dss/display-sysfs.c b/drivers/video/omap2/dss/display-sysfs.c
new file mode 100644
index 00000000000..18211a9ab35
--- /dev/null
+++ b/drivers/video/omap2/dss/display-sysfs.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Some code and ideas taken from drivers/video/omap/ driver
+ * by Imre Deak.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define DSS_SUBSYS_NAME "DISPLAY"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+
+#include <video/omapdss.h>
+#include "dss.h"
+#include "dss_features.h"
+
+static ssize_t display_enabled_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ bool enabled = dssdev->state != OMAP_DSS_DISPLAY_DISABLED;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", enabled);
+}
+
+static ssize_t display_enabled_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ int r;
+ bool enabled;
+
+ r = strtobool(buf, &enabled);
+ if (r)
+ return r;
+
+ if (enabled != (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) {
+ if (enabled) {
+ r = dssdev->driver->enable(dssdev);
+ if (r)
+ return r;
+ } else {
+ dssdev->driver->disable(dssdev);
+ }
+ }
+
+ return size;
+}
+
+static ssize_t display_tear_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ dssdev->driver->get_te ?
+ dssdev->driver->get_te(dssdev) : 0);
+}
+
+static ssize_t display_tear_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ int r;
+ bool te;
+
+ if (!dssdev->driver->enable_te || !dssdev->driver->get_te)
+ return -ENOENT;
+
+ r = strtobool(buf, &te);
+ if (r)
+ return r;
+
+ r = dssdev->driver->enable_te(dssdev, te);
+ if (r)
+ return r;
+
+ return size;
+}
+
+static ssize_t display_timings_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ struct omap_video_timings t;
+
+ if (!dssdev->driver->get_timings)
+ return -ENOENT;
+
+ dssdev->driver->get_timings(dssdev, &t);
+
+ return snprintf(buf, PAGE_SIZE, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n",
+ t.pixel_clock,
+ t.x_res, t.hfp, t.hbp, t.hsw,
+ t.y_res, t.vfp, t.vbp, t.vsw);
+}
+
+static ssize_t display_timings_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ struct omap_video_timings t = dssdev->panel.timings;
+ int r, found;
+
+ if (!dssdev->driver->set_timings || !dssdev->driver->check_timings)
+ return -ENOENT;
+
+ found = 0;
+#ifdef CONFIG_OMAP2_DSS_VENC
+ if (strncmp("pal", buf, 3) == 0) {
+ t = omap_dss_pal_timings;
+ found = 1;
+ } else if (strncmp("ntsc", buf, 4) == 0) {
+ t = omap_dss_ntsc_timings;
+ found = 1;
+ }
+#endif
+ if (!found && sscanf(buf, "%u,%hu/%hu/%hu/%hu,%hu/%hu/%hu/%hu",
+ &t.pixel_clock,
+ &t.x_res, &t.hfp, &t.hbp, &t.hsw,
+ &t.y_res, &t.vfp, &t.vbp, &t.vsw) != 9)
+ return -EINVAL;
+
+ r = dssdev->driver->check_timings(dssdev, &t);
+ if (r)
+ return r;
+
+ dssdev->driver->disable(dssdev);
+ dssdev->driver->set_timings(dssdev, &t);
+ r = dssdev->driver->enable(dssdev);
+ if (r)
+ return r;
+
+ return size;
+}
+
+static ssize_t display_rotate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ int rotate;
+ if (!dssdev->driver->get_rotate)
+ return -ENOENT;
+ rotate = dssdev->driver->get_rotate(dssdev);
+ return snprintf(buf, PAGE_SIZE, "%u\n", rotate);
+}
+
+static ssize_t display_rotate_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ int rot, r;
+
+ if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate)
+ return -ENOENT;
+
+ r = kstrtoint(buf, 0, &rot);
+ if (r)
+ return r;
+
+ r = dssdev->driver->set_rotate(dssdev, rot);
+ if (r)
+ return r;
+
+ return size;
+}
+
+static ssize_t display_mirror_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ int mirror;
+ if (!dssdev->driver->get_mirror)
+ return -ENOENT;
+ mirror = dssdev->driver->get_mirror(dssdev);
+ return snprintf(buf, PAGE_SIZE, "%u\n", mirror);
+}
+
+static ssize_t display_mirror_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ int r;
+ bool mirror;
+
+ if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror)
+ return -ENOENT;
+
+ r = strtobool(buf, &mirror);
+ if (r)
+ return r;
+
+ r = dssdev->driver->set_mirror(dssdev, mirror);
+ if (r)
+ return r;
+
+ return size;
+}
+
+static ssize_t display_wss_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ unsigned int wss;
+
+ if (!dssdev->driver->get_wss)
+ return -ENOENT;
+
+ wss = dssdev->driver->get_wss(dssdev);
+
+ return snprintf(buf, PAGE_SIZE, "0x%05x\n", wss);
+}
+
+static ssize_t display_wss_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ u32 wss;
+ int r;
+
+ if (!dssdev->driver->get_wss || !dssdev->driver->set_wss)
+ return -ENOENT;
+
+ r = kstrtou32(buf, 0, &wss);
+ if (r)
+ return r;
+
+ if (wss > 0xfffff)
+ return -EINVAL;
+
+ r = dssdev->driver->set_wss(dssdev, wss);
+ if (r)
+ return r;
+
+ return size;
+}
+
+static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR,
+ display_enabled_show, display_enabled_store);
+static DEVICE_ATTR(tear_elim, S_IRUGO|S_IWUSR,
+ display_tear_show, display_tear_store);
+static DEVICE_ATTR(timings, S_IRUGO|S_IWUSR,
+ display_timings_show, display_timings_store);
+static DEVICE_ATTR(rotate, S_IRUGO|S_IWUSR,
+ display_rotate_show, display_rotate_store);
+static DEVICE_ATTR(mirror, S_IRUGO|S_IWUSR,
+ display_mirror_show, display_mirror_store);
+static DEVICE_ATTR(wss, S_IRUGO|S_IWUSR,
+ display_wss_show, display_wss_store);
+
+static struct device_attribute *display_sysfs_attrs[] = {
+ &dev_attr_enabled,
+ &dev_attr_tear_elim,
+ &dev_attr_timings,
+ &dev_attr_rotate,
+ &dev_attr_mirror,
+ &dev_attr_wss,
+ NULL
+};
+
+int display_init_sysfs(struct platform_device *pdev,
+ struct omap_dss_device *dssdev)
+{
+ struct device_attribute *attr;
+ int i, r;
+
+ /* create device sysfs files */
+ i = 0;
+ while ((attr = display_sysfs_attrs[i++]) != NULL) {
+ r = device_create_file(&dssdev->dev, attr);
+ if (r) {
+ for (i = i - 2; i >= 0; i--) {
+ attr = display_sysfs_attrs[i];
+ device_remove_file(&dssdev->dev, attr);
+ }
+
+ DSSERR("failed to create sysfs file\n");
+ return r;
+ }
+ }
+
+ /* create display? sysfs links */
+ r = sysfs_create_link(&pdev->dev.kobj, &dssdev->dev.kobj,
+ dev_name(&dssdev->dev));
+ if (r) {
+ while ((attr = display_sysfs_attrs[i++]) != NULL)
+ device_remove_file(&dssdev->dev, attr);
+
+ DSSERR("failed to create sysfs display link\n");
+ return r;
+ }
+
+ return 0;
+}
+
+void display_uninit_sysfs(struct platform_device *pdev,
+ struct omap_dss_device *dssdev)
+{
+ struct device_attribute *attr;
+ int i = 0;
+
+ sysfs_remove_link(&pdev->dev.kobj, dev_name(&dssdev->dev));
+
+ while ((attr = display_sysfs_attrs[i++]) != NULL)
+ device_remove_file(&dssdev->dev, attr);
+}
diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c
index ccf8550fafd..0aa8ad8f966 100644
--- a/drivers/video/omap2/dss/display.c
+++ b/drivers/video/omap2/dss/display.c
@@ -31,250 +31,6 @@
#include "dss.h"
#include "dss_features.h"
-static ssize_t display_enabled_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct omap_dss_device *dssdev = to_dss_device(dev);
- bool enabled = dssdev->state != OMAP_DSS_DISPLAY_DISABLED;
-
- return snprintf(buf, PAGE_SIZE, "%d\n", enabled);
-}
-
-static ssize_t display_enabled_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
-{
- struct omap_dss_device *dssdev = to_dss_device(dev);
- int r;
- bool enabled;
-
- r = strtobool(buf, &enabled);
- if (r)
- return r;
-
- if (enabled != (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) {
- if (enabled) {
- r = dssdev->driver->enable(dssdev);
- if (r)
- return r;
- } else {
- dssdev->driver->disable(dssdev);
- }
- }
-
- return size;
-}
-
-static ssize_t display_tear_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct omap_dss_device *dssdev = to_dss_device(dev);
- return snprintf(buf, PAGE_SIZE, "%d\n",
- dssdev->driver->get_te ?
- dssdev->driver->get_te(dssdev) : 0);
-}
-
-static ssize_t display_tear_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
-{
- struct omap_dss_device *dssdev = to_dss_device(dev);
- int r;
- bool te;
-
- if (!dssdev->driver->enable_te || !dssdev->driver->get_te)
- return -ENOENT;
-
- r = strtobool(buf, &te);
- if (r)
- return r;
-
- r = dssdev->driver->enable_te(dssdev, te);
- if (r)
- return r;
-
- return size;
-}
-
-static ssize_t display_timings_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct omap_dss_device *dssdev = to_dss_device(dev);
- struct omap_video_timings t;
-
- if (!dssdev->driver->get_timings)
- return -ENOENT;
-
- dssdev->driver->get_timings(dssdev, &t);
-
- return snprintf(buf, PAGE_SIZE, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n",
- t.pixel_clock,
- t.x_res, t.hfp, t.hbp, t.hsw,
- t.y_res, t.vfp, t.vbp, t.vsw);
-}
-
-static ssize_t display_timings_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
-{
- struct omap_dss_device *dssdev = to_dss_device(dev);
- struct omap_video_timings t = dssdev->panel.timings;
- int r, found;
-
- if (!dssdev->driver->set_timings || !dssdev->driver->check_timings)
- return -ENOENT;
-
- found = 0;
-#ifdef CONFIG_OMAP2_DSS_VENC
- if (strncmp("pal", buf, 3) == 0) {
- t = omap_dss_pal_timings;
- found = 1;
- } else if (strncmp("ntsc", buf, 4) == 0) {
- t = omap_dss_ntsc_timings;
- found = 1;
- }
-#endif
- if (!found && sscanf(buf, "%u,%hu/%hu/%hu/%hu,%hu/%hu/%hu/%hu",
- &t.pixel_clock,
- &t.x_res, &t.hfp, &t.hbp, &t.hsw,
- &t.y_res, &t.vfp, &t.vbp, &t.vsw) != 9)
- return -EINVAL;
-
- r = dssdev->driver->check_timings(dssdev, &t);
- if (r)
- return r;
-
- dssdev->driver->disable(dssdev);
- dssdev->driver->set_timings(dssdev, &t);
- r = dssdev->driver->enable(dssdev);
- if (r)
- return r;
-
- return size;
-}
-
-static ssize_t display_rotate_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct omap_dss_device *dssdev = to_dss_device(dev);
- int rotate;
- if (!dssdev->driver->get_rotate)
- return -ENOENT;
- rotate = dssdev->driver->get_rotate(dssdev);
- return snprintf(buf, PAGE_SIZE, "%u\n", rotate);
-}
-
-static ssize_t display_rotate_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
-{
- struct omap_dss_device *dssdev = to_dss_device(dev);
- int rot, r;
-
- if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate)
- return -ENOENT;
-
- r = kstrtoint(buf, 0, &rot);
- if (r)
- return r;
-
- r = dssdev->driver->set_rotate(dssdev, rot);
- if (r)
- return r;
-
- return size;
-}
-
-static ssize_t display_mirror_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct omap_dss_device *dssdev = to_dss_device(dev);
- int mirror;
- if (!dssdev->driver->get_mirror)
- return -ENOENT;
- mirror = dssdev->driver->get_mirror(dssdev);
- return snprintf(buf, PAGE_SIZE, "%u\n", mirror);
-}
-
-static ssize_t display_mirror_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
-{
- struct omap_dss_device *dssdev = to_dss_device(dev);
- int r;
- bool mirror;
-
- if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror)
- return -ENOENT;
-
- r = strtobool(buf, &mirror);
- if (r)
- return r;
-
- r = dssdev->driver->set_mirror(dssdev, mirror);
- if (r)
- return r;
-
- return size;
-}
-
-static ssize_t display_wss_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct omap_dss_device *dssdev = to_dss_device(dev);
- unsigned int wss;
-
- if (!dssdev->driver->get_wss)
- return -ENOENT;
-
- wss = dssdev->driver->get_wss(dssdev);
-
- return snprintf(buf, PAGE_SIZE, "0x%05x\n", wss);
-}
-
-static ssize_t display_wss_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
-{
- struct omap_dss_device *dssdev = to_dss_device(dev);
- u32 wss;
- int r;
-
- if (!dssdev->driver->get_wss || !dssdev->driver->set_wss)
- return -ENOENT;
-
- r = kstrtou32(buf, 0, &wss);
- if (r)
- return r;
-
- if (wss > 0xfffff)
- return -EINVAL;
-
- r = dssdev->driver->set_wss(dssdev, wss);
- if (r)
- return r;
-
- return size;
-}
-
-static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR,
- display_enabled_show, display_enabled_store);
-static DEVICE_ATTR(tear_elim, S_IRUGO|S_IWUSR,
- display_tear_show, display_tear_store);
-static DEVICE_ATTR(timings, S_IRUGO|S_IWUSR,
- display_timings_show, display_timings_store);
-static DEVICE_ATTR(rotate, S_IRUGO|S_IWUSR,
- display_rotate_show, display_rotate_store);
-static DEVICE_ATTR(mirror, S_IRUGO|S_IWUSR,
- display_mirror_show, display_mirror_store);
-static DEVICE_ATTR(wss, S_IRUGO|S_IWUSR,
- display_wss_show, display_wss_store);
-
-static struct device_attribute *display_sysfs_attrs[] = {
- &dev_attr_enabled,
- &dev_attr_tear_elim,
- &dev_attr_timings,
- &dev_attr_rotate,
- &dev_attr_mirror,
- &dev_attr_wss,
- NULL
-};
-
void omapdss_default_get_resolution(struct omap_dss_device *dssdev,
u16 *xres, u16 *yres)
{
@@ -320,136 +76,8 @@ void omapdss_default_get_timings(struct omap_dss_device *dssdev,
}
EXPORT_SYMBOL(omapdss_default_get_timings);
-/*
- * Connect dssdev to a manager if the manager is free or if force is specified.
- * Connect all overlays to that manager if they are free or if force is
- * specified.
- */
-static int dss_init_connections(struct omap_dss_device *dssdev, bool force)
-{
- struct omap_dss_output *out;
- struct omap_overlay_manager *mgr;
- int i, r;
-
- out = omapdss_get_output_from_dssdev(dssdev);
-
- WARN_ON(dssdev->output);
- WARN_ON(out->device);
-
- r = omapdss_output_set_device(out, dssdev);
- if (r) {
- DSSERR("failed to connect output to new device\n");
- return r;
- }
-
- mgr = omap_dss_get_overlay_manager(dssdev->channel);
-
- if (mgr->output && !force)
- return 0;
-
- if (mgr->output)
- mgr->unset_output(mgr);
-
- r = mgr->set_output(mgr, out);
- if (r) {
- DSSERR("failed to connect manager to output of new device\n");
-
- /* remove the output-device connection we just made */
- omapdss_output_unset_device(out);
- return r;
- }
-
- for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
- struct omap_overlay *ovl = omap_dss_get_overlay(i);
-
- if (!ovl->manager || force) {
- if (ovl->manager)
- ovl->unset_manager(ovl);
-
- r = ovl->set_manager(ovl, mgr);
- if (r) {
- DSSERR("failed to set initial overlay\n");
- return r;
- }
- }
- }
-
- return 0;
-}
-
-static void dss_uninit_connections(struct omap_dss_device *dssdev)
-{
- if (dssdev->output) {
- struct omap_overlay_manager *mgr = dssdev->output->manager;
-
- if (mgr)
- mgr->unset_output(mgr);
-
- omapdss_output_unset_device(dssdev->output);
- }
-}
-
-int dss_init_device(struct platform_device *pdev,
- struct omap_dss_device *dssdev)
-{
- struct device_attribute *attr;
- int i, r;
- const char *def_disp_name = dss_get_default_display_name();
- bool force;
-
- force = def_disp_name && strcmp(def_disp_name, dssdev->name) == 0;
- dss_init_connections(dssdev, force);
-
- /* create device sysfs files */
- i = 0;
- while ((attr = display_sysfs_attrs[i++]) != NULL) {
- r = device_create_file(&dssdev->dev, attr);
- if (r) {
- for (i = i - 2; i >= 0; i--) {
- attr = display_sysfs_attrs[i];
- device_remove_file(&dssdev->dev, attr);
- }
-
- dss_uninit_connections(dssdev);
-
- DSSERR("failed to create sysfs file\n");
- return r;
- }
- }
-
- /* create display? sysfs links */
- r = sysfs_create_link(&pdev->dev.kobj, &dssdev->dev.kobj,
- dev_name(&dssdev->dev));
- if (r) {
- while ((attr = display_sysfs_attrs[i++]) != NULL)
- device_remove_file(&dssdev->dev, attr);
-
- dss_uninit_connections(dssdev);
-
- DSSERR("failed to create sysfs display link\n");
- return r;
- }
-
- return 0;
-}
-
-void dss_uninit_device(struct platform_device *pdev,
- struct omap_dss_device *dssdev)
-{
- struct device_attribute *attr;
- int i = 0;
-
- sysfs_remove_link(&pdev->dev.kobj, dev_name(&dssdev->dev));
-
- while ((attr = display_sysfs_attrs[i++]) != NULL)
- device_remove_file(&dssdev->dev, attr);
-
- dss_uninit_connections(dssdev);
-}
-
static int dss_suspend_device(struct device *dev, void *data)
{
- int r;
struct omap_dss_device *dssdev = to_dss_device(dev);
if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
@@ -457,15 +85,7 @@ static int dss_suspend_device(struct device *dev, void *data)
return 0;
}
- if (!dssdev->driver->suspend) {
- DSSERR("display '%s' doesn't implement suspend\n",
- dssdev->name);
- return -ENOSYS;
- }
-
- r = dssdev->driver->suspend(dssdev);
- if (r)
- return r;
+ dssdev->driver->disable(dssdev);
dssdev->activate_after_resume = true;
@@ -492,8 +112,8 @@ static int dss_resume_device(struct device *dev, void *data)
int r;
struct omap_dss_device *dssdev = to_dss_device(dev);
- if (dssdev->activate_after_resume && dssdev->driver->resume) {
- r = dssdev->driver->resume(dssdev);
+ if (dssdev->activate_after_resume) {
+ r = dssdev->driver->enable(dssdev);
if (r)
return r;
}
diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c
index 56748cf8760..4af136a04e5 100644
--- a/drivers/video/omap2/dss/dpi.c
+++ b/drivers/video/omap2/dss/dpi.c
@@ -49,34 +49,53 @@ static struct {
struct omap_dss_output output;
} dpi;
-static struct platform_device *dpi_get_dsidev(enum omap_dss_clk_source clk)
+static struct platform_device *dpi_get_dsidev(enum omap_channel channel)
{
- int dsi_module;
-
- dsi_module = clk == OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC ? 0 : 1;
+ /*
+ * XXX we can't currently use DSI PLL for DPI with OMAP3, as the DSI PLL
+ * would also be used for DISPC fclk. Meaning, when the DPI output is
+ * disabled, DISPC clock will be disabled, and TV out will stop.
+ */
+ switch (omapdss_get_version()) {
+ case OMAPDSS_VER_OMAP24xx:
+ case OMAPDSS_VER_OMAP34xx_ES1:
+ case OMAPDSS_VER_OMAP34xx_ES3:
+ case OMAPDSS_VER_OMAP3630:
+ case OMAPDSS_VER_AM35xx:
+ return NULL;
+ default:
+ break;
+ }
- return dsi_get_dsidev_from_id(dsi_module);
+ switch (channel) {
+ case OMAP_DSS_CHANNEL_LCD:
+ return dsi_get_dsidev_from_id(0);
+ case OMAP_DSS_CHANNEL_LCD2:
+ return dsi_get_dsidev_from_id(1);
+ default:
+ return NULL;
+ }
}
-static bool dpi_use_dsi_pll(struct omap_dss_device *dssdev)
+static enum omap_dss_clk_source dpi_get_alt_clk_src(enum omap_channel channel)
{
- if (dssdev->clocks.dispc.dispc_fclk_src ==
- OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC ||
- dssdev->clocks.dispc.dispc_fclk_src ==
- OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC ||
- dssdev->clocks.dispc.channel.lcd_clk_src ==
- OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC ||
- dssdev->clocks.dispc.channel.lcd_clk_src ==
- OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC)
- return true;
- else
- return false;
+ switch (channel) {
+ case OMAP_DSS_CHANNEL_LCD:
+ return OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC;
+ case OMAP_DSS_CHANNEL_LCD2:
+ return OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC;
+ default:
+ /* this shouldn't happen */
+ WARN_ON(1);
+ return OMAP_DSS_CLK_SRC_FCK;
+ }
}
static int dpi_set_dsi_clk(struct omap_dss_device *dssdev,
unsigned long pck_req, unsigned long *fck, int *lck_div,
int *pck_div)
{
+ struct omap_overlay_manager *mgr = dssdev->output->manager;
struct dsi_clock_info dsi_cinfo;
struct dispc_clock_info dispc_cinfo;
int r;
@@ -90,7 +109,8 @@ static int dpi_set_dsi_clk(struct omap_dss_device *dssdev,
if (r)
return r;
- dss_select_dispc_clk_source(dssdev->clocks.dispc.dispc_fclk_src);
+ dss_select_lcd_clk_source(mgr->id,
+ dpi_get_alt_clk_src(mgr->id));
dpi.mgr_config.clock_info = dispc_cinfo;
@@ -135,7 +155,7 @@ static int dpi_set_mode(struct omap_dss_device *dssdev)
unsigned long pck;
int r = 0;
- if (dpi_use_dsi_pll(dssdev))
+ if (dpi.dsidev)
r = dpi_set_dsi_clk(dssdev, t->pixel_clock * 1000, &fck,
&lck_div, &pck_div);
else
@@ -214,7 +234,7 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
if (r)
goto err_src_sel;
- if (dpi_use_dsi_pll(dssdev)) {
+ if (dpi.dsidev) {
r = dsi_runtime_get(dpi.dsidev);
if (r)
goto err_get_dsi;
@@ -242,10 +262,10 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
err_mgr_enable:
err_set_mode:
- if (dpi_use_dsi_pll(dssdev))
+ if (dpi.dsidev)
dsi_pll_uninit(dpi.dsidev, true);
err_dsi_pll_init:
- if (dpi_use_dsi_pll(dssdev))
+ if (dpi.dsidev)
dsi_runtime_put(dpi.dsidev);
err_get_dsi:
err_src_sel:
@@ -271,8 +291,8 @@ void omapdss_dpi_display_disable(struct omap_dss_device *dssdev)
dss_mgr_disable(mgr);
- if (dpi_use_dsi_pll(dssdev)) {
- dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK);
+ if (dpi.dsidev) {
+ dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);
dsi_pll_uninit(dpi.dsidev, true);
dsi_runtime_put(dpi.dsidev);
}
@@ -311,13 +331,13 @@ int dpi_check_timings(struct omap_dss_device *dssdev,
unsigned long pck;
struct dispc_clock_info dispc_cinfo;
- if (dss_mgr_check_timings(mgr, timings))
+ if (mgr && !dispc_mgr_timings_ok(mgr->id, timings))
return -EINVAL;
if (timings->pixel_clock == 0)
return -EINVAL;
- if (dpi_use_dsi_pll(dssdev)) {
+ if (dpi.dsidev) {
struct dsi_clock_info dsi_cinfo;
r = dsi_pll_calc_clock_div_pck(dpi.dsidev,
timings->pixel_clock * 1000,
@@ -359,8 +379,32 @@ void omapdss_dpi_set_data_lines(struct omap_dss_device *dssdev, int data_lines)
}
EXPORT_SYMBOL(omapdss_dpi_set_data_lines);
+static int __init dpi_verify_dsi_pll(struct platform_device *dsidev)
+{
+ int r;
+
+ /* do initial setup with the PLL to see if it is operational */
+
+ r = dsi_runtime_get(dsidev);
+ if (r)
+ return r;
+
+ r = dsi_pll_init(dsidev, 0, 1);
+ if (r) {
+ dsi_runtime_put(dsidev);
+ return r;
+ }
+
+ dsi_pll_uninit(dsidev, true);
+ dsi_runtime_put(dsidev);
+
+ return 0;
+}
+
static int __init dpi_init_display(struct omap_dss_device *dssdev)
{
+ struct platform_device *dsidev;
+
DSSDBG("init_display\n");
if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) &&
@@ -377,19 +421,30 @@ static int __init dpi_init_display(struct omap_dss_device *dssdev)
dpi.vdds_dsi_reg = vdds_dsi;
}
- if (dpi_use_dsi_pll(dssdev)) {
- enum omap_dss_clk_source dispc_fclk_src =
- dssdev->clocks.dispc.dispc_fclk_src;
- dpi.dsidev = dpi_get_dsidev(dispc_fclk_src);
+ /*
+ * XXX We shouldn't need dssdev->channel for this. The dsi pll clock
+ * source for DPI is SoC integration detail, not something that should
+ * be configured in the dssdev
+ */
+ dsidev = dpi_get_dsidev(dssdev->channel);
+
+ if (dsidev && dpi_verify_dsi_pll(dsidev)) {
+ dsidev = NULL;
+ DSSWARN("DSI PLL not operational\n");
}
+ if (dsidev)
+ DSSDBG("using DSI PLL for DPI clock\n");
+
+ dpi.dsidev = dsidev;
+
return 0;
}
static struct omap_dss_device * __init dpi_find_dssdev(struct platform_device *pdev)
{
struct omap_dss_board_info *pdata = pdev->dev.platform_data;
- const char *def_disp_name = dss_get_default_display_name();
+ const char *def_disp_name = omapdss_get_default_display_name();
struct omap_dss_device *def_dssdev;
int i;
@@ -438,9 +493,18 @@ static void __init dpi_probe_pdata(struct platform_device *dpidev)
return;
}
+ r = omapdss_output_set_device(&dpi.output, dssdev);
+ if (r) {
+ DSSERR("failed to connect output to new device: %s\n",
+ dssdev->name);
+ dss_put_device(dssdev);
+ return;
+ }
+
r = dss_add_device(dssdev);
if (r) {
DSSERR("device %s register failed: %d\n", dssdev->name, r);
+ omapdss_output_unset_device(&dpi.output);
dss_put_device(dssdev);
return;
}
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
index bee92846cfa..28d41d16b7b 100644
--- a/drivers/video/omap2/dss/dsi.c
+++ b/drivers/video/omap2/dss/dsi.c
@@ -45,7 +45,6 @@
#include "dss.h"
#include "dss_features.h"
-/*#define VERBOSE_IRQ*/
#define DSI_CATCH_MISSING_TE
struct dsi_reg { u16 idx; };
@@ -535,42 +534,38 @@ static inline void dsi_perf_show(struct platform_device *dsidev,
}
#endif
+static int verbose_irq;
+
static void print_irq_status(u32 status)
{
if (status == 0)
return;
-#ifndef VERBOSE_IRQ
- if ((status & ~DSI_IRQ_CHANNEL_MASK) == 0)
+ if (!verbose_irq && (status & ~DSI_IRQ_CHANNEL_MASK) == 0)
return;
-#endif
- printk(KERN_DEBUG "DSI IRQ: 0x%x: ", status);
-#define PIS(x) \
- if (status & DSI_IRQ_##x) \
- printk(#x " ");
-#ifdef VERBOSE_IRQ
- PIS(VC0);
- PIS(VC1);
- PIS(VC2);
- PIS(VC3);
-#endif
- PIS(WAKEUP);
- PIS(RESYNC);
- PIS(PLL_LOCK);
- PIS(PLL_UNLOCK);
- PIS(PLL_RECALL);
- PIS(COMPLEXIO_ERR);
- PIS(HS_TX_TIMEOUT);
- PIS(LP_RX_TIMEOUT);
- PIS(TE_TRIGGER);
- PIS(ACK_TRIGGER);
- PIS(SYNC_LOST);
- PIS(LDO_POWER_GOOD);
- PIS(TA_TIMEOUT);
+#define PIS(x) (status & DSI_IRQ_##x) ? (#x " ") : ""
+
+ pr_debug("DSI IRQ: 0x%x: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+ status,
+ verbose_irq ? PIS(VC0) : "",
+ verbose_irq ? PIS(VC1) : "",
+ verbose_irq ? PIS(VC2) : "",
+ verbose_irq ? PIS(VC3) : "",
+ PIS(WAKEUP),
+ PIS(RESYNC),
+ PIS(PLL_LOCK),
+ PIS(PLL_UNLOCK),
+ PIS(PLL_RECALL),
+ PIS(COMPLEXIO_ERR),
+ PIS(HS_TX_TIMEOUT),
+ PIS(LP_RX_TIMEOUT),
+ PIS(TE_TRIGGER),
+ PIS(ACK_TRIGGER),
+ PIS(SYNC_LOST),
+ PIS(LDO_POWER_GOOD),
+ PIS(TA_TIMEOUT));
#undef PIS
-
- printk("\n");
}
static void print_irq_status_vc(int channel, u32 status)
@@ -578,28 +573,24 @@ static void print_irq_status_vc(int channel, u32 status)
if (status == 0)
return;
-#ifndef VERBOSE_IRQ
- if ((status & ~DSI_VC_IRQ_PACKET_SENT) == 0)
+ if (!verbose_irq && (status & ~DSI_VC_IRQ_PACKET_SENT) == 0)
return;
-#endif
- printk(KERN_DEBUG "DSI VC(%d) IRQ 0x%x: ", channel, status);
-#define PIS(x) \
- if (status & DSI_VC_IRQ_##x) \
- printk(#x " ");
- PIS(CS);
- PIS(ECC_CORR);
-#ifdef VERBOSE_IRQ
- PIS(PACKET_SENT);
-#endif
- PIS(FIFO_TX_OVF);
- PIS(FIFO_RX_OVF);
- PIS(BTA);
- PIS(ECC_NO_CORR);
- PIS(FIFO_TX_UDF);
- PIS(PP_BUSY_CHANGE);
+#define PIS(x) (status & DSI_VC_IRQ_##x) ? (#x " ") : ""
+
+ pr_debug("DSI VC(%d) IRQ 0x%x: %s%s%s%s%s%s%s%s%s\n",
+ channel,
+ status,
+ PIS(CS),
+ PIS(ECC_CORR),
+ PIS(ECC_NO_CORR),
+ verbose_irq ? PIS(PACKET_SENT) : "",
+ PIS(BTA),
+ PIS(FIFO_TX_OVF),
+ PIS(FIFO_RX_OVF),
+ PIS(FIFO_TX_UDF),
+ PIS(PP_BUSY_CHANGE));
#undef PIS
- printk("\n");
}
static void print_irq_status_cio(u32 status)
@@ -607,34 +598,31 @@ static void print_irq_status_cio(u32 status)
if (status == 0)
return;
- printk(KERN_DEBUG "DSI CIO IRQ 0x%x: ", status);
-
-#define PIS(x) \
- if (status & DSI_CIO_IRQ_##x) \
- printk(#x " ");
- PIS(ERRSYNCESC1);
- PIS(ERRSYNCESC2);
- PIS(ERRSYNCESC3);
- PIS(ERRESC1);
- PIS(ERRESC2);
- PIS(ERRESC3);
- PIS(ERRCONTROL1);
- PIS(ERRCONTROL2);
- PIS(ERRCONTROL3);
- PIS(STATEULPS1);
- PIS(STATEULPS2);
- PIS(STATEULPS3);
- PIS(ERRCONTENTIONLP0_1);
- PIS(ERRCONTENTIONLP1_1);
- PIS(ERRCONTENTIONLP0_2);
- PIS(ERRCONTENTIONLP1_2);
- PIS(ERRCONTENTIONLP0_3);
- PIS(ERRCONTENTIONLP1_3);
- PIS(ULPSACTIVENOT_ALL0);
- PIS(ULPSACTIVENOT_ALL1);
+#define PIS(x) (status & DSI_CIO_IRQ_##x) ? (#x " ") : ""
+
+ pr_debug("DSI CIO IRQ 0x%x: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+ status,
+ PIS(ERRSYNCESC1),
+ PIS(ERRSYNCESC2),
+ PIS(ERRSYNCESC3),
+ PIS(ERRESC1),
+ PIS(ERRESC2),
+ PIS(ERRESC3),
+ PIS(ERRCONTROL1),
+ PIS(ERRCONTROL2),
+ PIS(ERRCONTROL3),
+ PIS(STATEULPS1),
+ PIS(STATEULPS2),
+ PIS(STATEULPS3),
+ PIS(ERRCONTENTIONLP0_1),
+ PIS(ERRCONTENTIONLP1_1),
+ PIS(ERRCONTENTIONLP0_2),
+ PIS(ERRCONTENTIONLP1_2),
+ PIS(ERRCONTENTIONLP0_3),
+ PIS(ERRCONTENTIONLP1_3),
+ PIS(ULPSACTIVENOT_ALL0),
+ PIS(ULPSACTIVENOT_ALL1));
#undef PIS
-
- printk("\n");
}
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
@@ -1116,28 +1104,16 @@ static inline void dsi_enable_pll_clock(struct platform_device *dsidev,
}
}
-#ifdef DEBUG
static void _dsi_print_reset_status(struct platform_device *dsidev)
{
u32 l;
int b0, b1, b2;
- if (!dss_debug)
- return;
-
/* A dummy read using the SCP interface to any DSIPHY register is
* required after DSIPHY reset to complete the reset of the DSI complex
* I/O. */
l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5);
- printk(KERN_DEBUG "DSI resets: ");
-
- l = dsi_read_reg(dsidev, DSI_PLL_STATUS);
- printk("PLL (%d) ", FLD_GET(l, 0, 0));
-
- l = dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1);
- printk("CIO (%d) ", FLD_GET(l, 29, 29));
-
if (dss_has_feature(FEAT_DSI_REVERSE_TXCLKESC)) {
b0 = 28;
b1 = 27;
@@ -1148,18 +1124,21 @@ static void _dsi_print_reset_status(struct platform_device *dsidev)
b2 = 26;
}
- l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5);
- printk("PHY (%x%x%x, %d, %d, %d)\n",
- FLD_GET(l, b0, b0),
- FLD_GET(l, b1, b1),
- FLD_GET(l, b2, b2),
- FLD_GET(l, 29, 29),
- FLD_GET(l, 30, 30),
- FLD_GET(l, 31, 31));
+#define DSI_FLD_GET(fld, start, end)\
+ FLD_GET(dsi_read_reg(dsidev, DSI_##fld), start, end)
+
+ pr_debug("DSI resets: PLL (%d) CIO (%d) PHY (%x%x%x, %d, %d, %d)\n",
+ DSI_FLD_GET(PLL_STATUS, 0, 0),
+ DSI_FLD_GET(COMPLEXIO_CFG1, 29, 29),
+ DSI_FLD_GET(DSIPHY_CFG5, b0, b0),
+ DSI_FLD_GET(DSIPHY_CFG5, b1, b1),
+ DSI_FLD_GET(DSIPHY_CFG5, b2, b2),
+ DSI_FLD_GET(DSIPHY_CFG5, 29, 29),
+ DSI_FLD_GET(DSIPHY_CFG5, 30, 30),
+ DSI_FLD_GET(DSIPHY_CFG5, 31, 31));
+
+#undef DSI_FLD_GET
}
-#else
-#define _dsi_print_reset_status(x)
-#endif
static inline int dsi_if_enable(struct platform_device *dsidev, bool enable)
{
@@ -1407,6 +1386,11 @@ retry:
cur.dsi_pll_hsdiv_dispc_clk =
cur.clkin4ddr / cur.regm_dispc;
+ if (cur.regm_dispc > 1 &&
+ cur.regm_dispc % 2 != 0 &&
+ req_pck >= 1000000)
+ continue;
+
/* this will narrow down the search a bit,
* but still give pixclocks below what was
* requested */
@@ -1621,7 +1605,7 @@ int dsi_pll_set_clock_div(struct platform_device *dsidev,
u8 regn_start, regn_end, regm_start, regm_end;
u8 regm_dispc_start, regm_dispc_end, regm_dsi_start, regm_dsi_end;
- DSSDBGF();
+ DSSDBG("DSI PLL clock config starts");
dsi->current_cinfo.clkin = cinfo->clkin;
dsi->current_cinfo.fint = cinfo->fint;
@@ -1757,11 +1741,21 @@ int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk,
DSSDBG("PLL init\n");
+ /*
+ * It seems that on many OMAPs we need to enable both to have a
+ * functional HSDivider.
+ */
+ enable_hsclk = enable_hsdiv = true;
+
if (dsi->vdds_dsi_reg == NULL) {
struct regulator *vdds_dsi;
vdds_dsi = regulator_get(&dsi->pdev->dev, "vdds_dsi");
+ /* DT HACK: try VCXIO to make omapdss work for o4 sdp/panda */
+ if (IS_ERR(vdds_dsi))
+ vdds_dsi = regulator_get(&dsi->pdev->dev, "VCXIO");
+
if (IS_ERR(vdds_dsi)) {
DSSERR("can't get VDDS_DSI regulator\n");
return PTR_ERR(vdds_dsi);
@@ -2440,7 +2434,7 @@ static int dsi_cio_init(struct platform_device *dsidev)
int r;
u32 l;
- DSSDBGF();
+ DSSDBG("DSI CIO init starts");
r = dss_dsi_enable_pads(dsi->module_id, dsi_get_lane_mask(dsidev));
if (r)
@@ -2791,7 +2785,7 @@ static void dsi_vc_initial_config(struct platform_device *dsidev, int channel)
{
u32 r;
- DSSDBGF("%d", channel);
+ DSSDBG("Initial config of virtual channel %d", channel);
r = dsi_read_reg(dsidev, DSI_VC_CTRL(channel));
@@ -2823,7 +2817,7 @@ static int dsi_vc_config_source(struct platform_device *dsidev, int channel,
if (dsi->vc[channel].source == source)
return 0;
- DSSDBGF("%d", channel);
+ DSSDBG("Source config of virtual channel %d", channel);
dsi_sync_vc(dsidev, channel);
@@ -3581,7 +3575,7 @@ static int dsi_enter_ulps(struct platform_device *dsidev)
int r, i;
unsigned mask;
- DSSDBGF();
+ DSSDBG("Entering ULPS");
WARN_ON(!dsi_bus_is_locked(dsidev));
@@ -4285,7 +4279,7 @@ int omapdss_dsi_set_clocks(struct omap_dss_device *dssdev,
unsigned long pck;
int r;
- DSSDBGF("ddr_clk %lu, lp_clk %lu", ddr_clk, lp_clk);
+ DSSDBG("Setting DSI clocks: ddr_clk %lu, lp_clk %lu", ddr_clk, lp_clk);
mutex_lock(&dsi->lock);
@@ -4541,7 +4535,7 @@ static void dsi_framedone_timeout_work_callback(struct work_struct *work)
dsi_handle_framedone(dsi->pdev, -ETIMEDOUT);
}
-static void dsi_framedone_irq_callback(void *data, u32 mask)
+static void dsi_framedone_irq_callback(void *data)
{
struct platform_device *dsidev = (struct platform_device *) data;
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
@@ -4615,7 +4609,6 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev)
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
struct omap_overlay_manager *mgr = dssdev->output->manager;
int r;
- u32 irq = 0;
if (dsi->mode == OMAP_DSS_DSI_CMD_MODE) {
dsi->timings.hsw = 1;
@@ -4625,12 +4618,10 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev)
dsi->timings.vfp = 0;
dsi->timings.vbp = 0;
- irq = dispc_mgr_get_framedone_irq(mgr->id);
-
- r = omap_dispc_register_isr(dsi_framedone_irq_callback,
- (void *) dsidev, irq);
+ r = dss_mgr_register_framedone_handler(mgr,
+ dsi_framedone_irq_callback, dsidev);
if (r) {
- DSSERR("can't get FRAMEDONE irq\n");
+ DSSERR("can't register FRAMEDONE handler\n");
goto err;
}
@@ -4668,8 +4659,8 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev)
return 0;
err1:
if (dsi->mode == OMAP_DSS_DSI_CMD_MODE)
- omap_dispc_unregister_isr(dsi_framedone_irq_callback,
- (void *) dsidev, irq);
+ dss_mgr_unregister_framedone_handler(mgr,
+ dsi_framedone_irq_callback, dsidev);
err:
return r;
}
@@ -4680,14 +4671,9 @@ static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev)
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
struct omap_overlay_manager *mgr = dssdev->output->manager;
- if (dsi->mode == OMAP_DSS_DSI_CMD_MODE) {
- u32 irq;
-
- irq = dispc_mgr_get_framedone_irq(mgr->id);
-
- omap_dispc_unregister_isr(dsi_framedone_irq_callback,
- (void *) dsidev, irq);
- }
+ if (dsi->mode == OMAP_DSS_DSI_CMD_MODE)
+ dss_mgr_unregister_framedone_handler(mgr,
+ dsi_framedone_irq_callback, dsidev);
}
static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev)
@@ -4730,7 +4716,6 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
if (r)
goto err1;
- dss_select_dispc_clk_source(dssdev->clocks.dispc.dispc_fclk_src);
dss_select_dsi_clk_source(dsi->module_id, dssdev->clocks.dsi.dsi_fclk_src);
dss_select_lcd_clk_source(mgr->id,
dssdev->clocks.dispc.channel.lcd_clk_src);
@@ -4765,7 +4750,6 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
err3:
dsi_cio_uninit(dsidev);
err2:
- dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK);
dss_select_dsi_clk_source(dsi->module_id, OMAP_DSS_CLK_SRC_FCK);
dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);
@@ -4792,7 +4776,6 @@ static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev,
dsi_vc_enable(dsidev, 2, 0);
dsi_vc_enable(dsidev, 3, 0);
- dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK);
dss_select_dsi_clk_source(dsi->module_id, OMAP_DSS_CLK_SRC_FCK);
dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);
dsi_cio_uninit(dsidev);
@@ -4981,6 +4964,10 @@ static int __init dsi_init_display(struct omap_dss_device *dssdev)
vdds_dsi = regulator_get(&dsi->pdev->dev, "vdds_dsi");
+ /* DT HACK: try VCXIO to make omapdss work for o4 sdp/panda */
+ if (IS_ERR(vdds_dsi))
+ vdds_dsi = regulator_get(&dsi->pdev->dev, "VCXIO");
+
if (IS_ERR(vdds_dsi)) {
DSSERR("can't get VDDS_DSI regulator\n");
return PTR_ERR(vdds_dsi);
@@ -5121,7 +5108,7 @@ static struct omap_dss_device * __init dsi_find_dssdev(struct platform_device *p
{
struct omap_dss_board_info *pdata = pdev->dev.platform_data;
struct dsi_data *dsi = dsi_get_dsidrv_data(pdev);
- const char *def_disp_name = dss_get_default_display_name();
+ const char *def_disp_name = omapdss_get_default_display_name();
struct omap_dss_device *def_dssdev;
int i;
@@ -5151,6 +5138,7 @@ static struct omap_dss_device * __init dsi_find_dssdev(struct platform_device *p
static void __init dsi_probe_pdata(struct platform_device *dsidev)
{
+ struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
struct omap_dss_device *plat_dssdev;
struct omap_dss_device *dssdev;
int r;
@@ -5173,9 +5161,18 @@ static void __init dsi_probe_pdata(struct platform_device *dsidev)
return;
}
+ r = omapdss_output_set_device(&dsi->output, dssdev);
+ if (r) {
+ DSSERR("failed to connect output to new device: %s\n",
+ dssdev->name);
+ dss_put_device(dssdev);
+ return;
+ }
+
r = dss_add_device(dssdev);
if (r) {
DSSERR("device %s register failed: %d\n", dssdev->name, r);
+ omapdss_output_unset_device(&dsi->output);
dss_put_device(dssdev);
return;
}
diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c
index 602102cebcb..054c2a22b3f 100644
--- a/drivers/video/omap2/dss/dss.c
+++ b/drivers/video/omap2/dss/dss.c
@@ -32,6 +32,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/gfp.h>
+#include <linux/sizes.h>
#include <video/omapdss.h>
@@ -76,6 +77,7 @@ static struct {
struct clk *dpll4_m4_ck;
struct clk *dss_clk;
+ unsigned long dss_clk_rate;
unsigned long cache_req_pck;
unsigned long cache_prate;
@@ -96,6 +98,8 @@ static const char * const dss_generic_clk_source_names[] = {
[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "DSI_PLL_HSDIV_DISPC",
[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "DSI_PLL_HSDIV_DSI",
[OMAP_DSS_CLK_SRC_FCK] = "DSS_FCK",
+ [OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC] = "DSI_PLL2_HSDIV_DISPC",
+ [OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI] = "DSI_PLL2_HSDIV_DSI",
};
static inline void dss_write_reg(const struct dss_reg idx, u32 val)
@@ -151,6 +155,21 @@ static void dss_restore_context(void)
#undef SR
#undef RR
+int dss_get_ctx_loss_count(void)
+{
+ struct omap_dss_board_info *board_data = dss.pdev->dev.platform_data;
+ int cnt;
+
+ if (!board_data->get_context_loss_count)
+ return -ENOENT;
+
+ cnt = board_data->get_context_loss_count(&dss.pdev->dev);
+
+ WARN_ONCE(cnt < 0, "get_context_loss_count failed: %d\n", cnt);
+
+ return cnt;
+}
+
void dss_sdi_init(int datapairs)
{
u32 l;
@@ -301,7 +320,7 @@ static void dss_dump_regs(struct seq_file *s)
#undef DUMPREG
}
-void dss_select_dispc_clk_source(enum omap_dss_clk_source clk_src)
+static void dss_select_dispc_clk_source(enum omap_dss_clk_source clk_src)
{
struct platform_device *dsidev;
int b;
@@ -372,8 +391,10 @@ void dss_select_lcd_clk_source(enum omap_channel channel,
struct platform_device *dsidev;
int b, ix, pos;
- if (!dss_has_feature(FEAT_LCD_CLK_SRC))
+ if (!dss_has_feature(FEAT_LCD_CLK_SRC)) {
+ dss_select_dispc_clk_source(clk_src);
return;
+ }
switch (clk_src) {
case OMAP_DSS_CLK_SRC_FCK:
@@ -429,6 +450,29 @@ enum omap_dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel)
}
}
+/* calculate clock rates using dividers in cinfo */
+int dss_calc_clock_rates(struct dss_clock_info *cinfo)
+{
+ if (dss.dpll4_m4_ck) {
+ unsigned long prate;
+
+ if (cinfo->fck_div > dss.feat->fck_div_max ||
+ cinfo->fck_div == 0)
+ return -EINVAL;
+
+ prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
+
+ cinfo->fck = prate / cinfo->fck_div *
+ dss.feat->dss_fck_multiplier;
+ } else {
+ if (cinfo->fck_div != 0)
+ return -EINVAL;
+ cinfo->fck = clk_get_rate(dss.dss_clk);
+ }
+
+ return 0;
+}
+
int dss_set_clock_div(struct dss_clock_info *cinfo)
{
if (dss.dpll4_m4_ck) {
@@ -446,6 +490,10 @@ int dss_set_clock_div(struct dss_clock_info *cinfo)
return -EINVAL;
}
+ dss.dss_clk_rate = clk_get_rate(dss.dss_clk);
+
+ WARN_ONCE(dss.dss_clk_rate != cinfo->fck, "clk rate mismatch");
+
DSSDBG("fck = %ld (%d)\n", cinfo->fck, cinfo->fck_div);
return 0;
@@ -459,6 +507,41 @@ unsigned long dss_get_dpll4_rate(void)
return 0;
}
+unsigned long dss_get_dispc_clk_rate(void)
+{
+ return dss.dss_clk_rate;
+}
+
+static int dss_setup_default_clock(void)
+{
+ unsigned long max_dss_fck, prate;
+ unsigned fck_div;
+ struct dss_clock_info dss_cinfo = { 0 };
+ int r;
+
+ if (dss.dpll4_m4_ck == NULL)
+ return 0;
+
+ max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK);
+
+ prate = dss_get_dpll4_rate();
+
+ fck_div = DIV_ROUND_UP(prate * dss.feat->dss_fck_multiplier,
+ max_dss_fck);
+
+ dss_cinfo.fck_div = fck_div;
+
+ r = dss_calc_clock_rates(&dss_cinfo);
+ if (r)
+ return r;
+
+ r = dss_set_clock_div(&dss_cinfo);
+ if (r)
+ return r;
+
+ return 0;
+}
+
int dss_calc_clock_div(unsigned long req_pck, struct dss_clock_info *dss_cinfo,
struct dispc_clock_info *dispc_cinfo)
{
@@ -748,7 +831,7 @@ static void dss_runtime_put(void)
}
/* DEBUGFS */
-#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
+#if defined(CONFIG_OMAP2_DSS_DEBUGFS)
void dss_debug_dump_clocks(struct seq_file *s)
{
dss_dump_clocks(s);
@@ -796,7 +879,6 @@ static const struct dss_features omap54xx_dss_feats __initconst = {
static int __init dss_init_features(struct platform_device *pdev)
{
- struct omap_dss_board_info *pdata = pdev->dev.platform_data;
const struct dss_features *src;
struct dss_features *dst;
@@ -806,7 +888,7 @@ static int __init dss_init_features(struct platform_device *pdev)
return -ENOMEM;
}
- switch (pdata->version) {
+ switch (omapdss_get_version()) {
case OMAPDSS_VER_OMAP24xx:
src = &omap24xx_dss_feats;
break;
@@ -871,15 +953,23 @@ static int __init omap_dsshw_probe(struct platform_device *pdev)
if (r)
return r;
+ r = dss_setup_default_clock();
+ if (r)
+ goto err_setup_clocks;
+
pm_runtime_enable(&pdev->dev);
r = dss_runtime_get();
if (r)
goto err_runtime_get;
+ dss.dss_clk_rate = clk_get_rate(dss.dss_clk);
+
/* Select DPLL */
REG_FLD_MOD(DSS_CONTROL, 0, 0, 0);
+ dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK);
+
#ifdef CONFIG_OMAP2_DSS_VENC
REG_FLD_MOD(DSS_CONTROL, 1, 4, 4); /* venc dac demen */
REG_FLD_MOD(DSS_CONTROL, 1, 3, 3); /* venc clock 4x enable */
@@ -903,6 +993,7 @@ static int __init omap_dsshw_probe(struct platform_device *pdev)
err_runtime_get:
pm_runtime_disable(&pdev->dev);
+err_setup_clocks:
dss_put_clocks();
return r;
}
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index 6728892f9da..610c8e563da 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -23,44 +23,20 @@
#ifndef __OMAP2_DSS_H
#define __OMAP2_DSS_H
-#ifdef CONFIG_OMAP2_DSS_DEBUG_SUPPORT
-#define DEBUG
-#endif
+#include <linux/interrupt.h>
-#ifdef DEBUG
-extern bool dss_debug;
-#ifdef DSS_SUBSYS_NAME
-#define DSSDBG(format, ...) \
- if (dss_debug) \
- printk(KERN_DEBUG "omapdss " DSS_SUBSYS_NAME ": " format, \
- ## __VA_ARGS__)
-#else
-#define DSSDBG(format, ...) \
- if (dss_debug) \
- printk(KERN_DEBUG "omapdss: " format, ## __VA_ARGS__)
+#ifdef pr_fmt
+#undef pr_fmt
#endif
#ifdef DSS_SUBSYS_NAME
-#define DSSDBGF(format, ...) \
- if (dss_debug) \
- printk(KERN_DEBUG "omapdss " DSS_SUBSYS_NAME \
- ": %s(" format ")\n", \
- __func__, \
- ## __VA_ARGS__)
+#define pr_fmt(fmt) DSS_SUBSYS_NAME ": " fmt
#else
-#define DSSDBGF(format, ...) \
- if (dss_debug) \
- printk(KERN_DEBUG "omapdss: " \
- ": %s(" format ")\n", \
- __func__, \
- ## __VA_ARGS__)
-#endif
-
-#else /* DEBUG */
-#define DSSDBG(format, ...)
-#define DSSDBGF(format, ...)
+#define pr_fmt(fmt) fmt
#endif
+#define DSSDBG(format, ...) \
+ pr_debug(format, ## __VA_ARGS__)
#ifdef DSS_SUBSYS_NAME
#define DSSERR(format, ...) \
@@ -186,11 +162,10 @@ struct seq_file;
struct platform_device;
/* core */
-const char *dss_get_default_display_name(void);
+struct platform_device *dss_get_core_pdev(void);
struct bus_type *dss_get_bus(void);
struct regulator *dss_get_vdds_dsi(void);
struct regulator *dss_get_vdds_sdi(void);
-int dss_get_ctx_loss_count(struct device *dev);
int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask);
void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask);
int dss_set_min_bus_tput(struct device *dev, unsigned long tput);
@@ -204,55 +179,18 @@ void dss_put_device(struct omap_dss_device *dssdev);
void dss_copy_device_pdata(struct omap_dss_device *dst,
const struct omap_dss_device *src);
-/* apply */
-void dss_apply_init(void);
-int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr);
-int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl);
-void dss_mgr_start_update(struct omap_overlay_manager *mgr);
-int omap_dss_mgr_apply(struct omap_overlay_manager *mgr);
-
-int dss_mgr_enable(struct omap_overlay_manager *mgr);
-void dss_mgr_disable(struct omap_overlay_manager *mgr);
-int dss_mgr_set_info(struct omap_overlay_manager *mgr,
- struct omap_overlay_manager_info *info);
-void dss_mgr_get_info(struct omap_overlay_manager *mgr,
- struct omap_overlay_manager_info *info);
-int dss_mgr_set_device(struct omap_overlay_manager *mgr,
- struct omap_dss_device *dssdev);
-int dss_mgr_unset_device(struct omap_overlay_manager *mgr);
-int dss_mgr_set_output(struct omap_overlay_manager *mgr,
- struct omap_dss_output *output);
-int dss_mgr_unset_output(struct omap_overlay_manager *mgr);
-void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
- const struct omap_video_timings *timings);
-void dss_mgr_set_lcd_config(struct omap_overlay_manager *mgr,
- const struct dss_lcd_mgr_config *config);
-const struct omap_video_timings *dss_mgr_get_timings(struct omap_overlay_manager *mgr);
-
-bool dss_ovl_is_enabled(struct omap_overlay *ovl);
-int dss_ovl_enable(struct omap_overlay *ovl);
-int dss_ovl_disable(struct omap_overlay *ovl);
-int dss_ovl_set_info(struct omap_overlay *ovl,
- struct omap_overlay_info *info);
-void dss_ovl_get_info(struct omap_overlay *ovl,
- struct omap_overlay_info *info);
-int dss_ovl_set_manager(struct omap_overlay *ovl,
- struct omap_overlay_manager *mgr);
-int dss_ovl_unset_manager(struct omap_overlay *ovl);
-
/* output */
void dss_register_output(struct omap_dss_output *out);
void dss_unregister_output(struct omap_dss_output *out);
-struct omap_dss_output *omapdss_get_output_from_dssdev(struct omap_dss_device *dssdev);
/* display */
int dss_suspend_all_devices(void);
int dss_resume_all_devices(void);
void dss_disable_all_devices(void);
-int dss_init_device(struct platform_device *pdev,
+int display_init_sysfs(struct platform_device *pdev,
struct omap_dss_device *dssdev);
-void dss_uninit_device(struct platform_device *pdev,
+void display_uninit_sysfs(struct platform_device *pdev,
struct omap_dss_device *dssdev);
/* manager */
@@ -299,21 +237,23 @@ void dss_overlay_kobj_uninit(struct omap_overlay *ovl);
int dss_init_platform_driver(void) __init;
void dss_uninit_platform_driver(void);
+unsigned long dss_get_dispc_clk_rate(void);
int dss_dpi_select_source(enum omap_channel channel);
void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select);
enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void);
const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src);
void dss_dump_clocks(struct seq_file *s);
-#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
+#if defined(CONFIG_OMAP2_DSS_DEBUGFS)
void dss_debug_dump_clocks(struct seq_file *s);
#endif
+int dss_get_ctx_loss_count(void);
+
void dss_sdi_init(int datapairs);
int dss_sdi_enable(void);
void dss_sdi_disable(void);
-void dss_select_dispc_clk_source(enum omap_dss_clk_source clk_src);
void dss_select_dsi_clk_source(int dsi_module,
enum omap_dss_clk_source clk_src);
void dss_select_lcd_clk_source(enum omap_channel channel,
@@ -326,6 +266,7 @@ void dss_set_venc_output(enum omap_dss_venc_type type);
void dss_set_dac_pwrdn_bgz(bool enable);
unsigned long dss_get_dpll4_rate(void);
+int dss_calc_clock_rates(struct dss_clock_info *cinfo);
int dss_set_clock_div(struct dss_clock_info *cinfo);
int dss_calc_clock_div(unsigned long req_pck, struct dss_clock_info *dss_cinfo,
struct dispc_clock_info *dispc_cinfo);
@@ -413,8 +354,6 @@ static inline void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev)
}
static inline struct platform_device *dsi_get_dsidev_from_id(int module)
{
- WARN("%s: DSI not compiled in, returning platform device as NULL\n",
- __func__);
return NULL;
}
#endif
@@ -427,15 +366,10 @@ void dpi_uninit_platform_driver(void) __exit;
int dispc_init_platform_driver(void) __init;
void dispc_uninit_platform_driver(void) __exit;
void dispc_dump_clocks(struct seq_file *s);
-void dispc_irq_handler(void);
-
-int dispc_runtime_get(void);
-void dispc_runtime_put(void);
void dispc_enable_sidle(void);
void dispc_disable_sidle(void);
-void dispc_lcd_enable_signal_polarity(bool act_high);
void dispc_lcd_enable_signal(bool enable);
void dispc_pck_free_enable(bool enable);
void dispc_enable_fifomerge(bool enable);
@@ -455,36 +389,14 @@ void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high);
void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane,
u32 *fifo_low, u32 *fifo_high, bool use_fifomerge,
bool manual_update);
-int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi,
- bool replication, const struct omap_video_timings *mgr_timings,
- bool mem_to_mem);
-int dispc_ovl_enable(enum omap_plane plane, bool enable);
-void dispc_ovl_set_channel_out(enum omap_plane plane,
- enum omap_channel channel);
-
-void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable);
-u32 dispc_mgr_get_vsync_irq(enum omap_channel channel);
-u32 dispc_mgr_get_framedone_irq(enum omap_channel channel);
-bool dispc_mgr_go_busy(enum omap_channel channel);
-void dispc_mgr_go(enum omap_channel channel);
-bool dispc_mgr_is_enabled(enum omap_channel channel);
-void dispc_mgr_enable(enum omap_channel channel, bool enable);
-bool dispc_mgr_is_channel_enabled(enum omap_channel channel);
-void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode);
-void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable);
-void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines);
-void dispc_mgr_set_lcd_type_tft(enum omap_channel channel);
-void dispc_mgr_set_timings(enum omap_channel channel,
- struct omap_video_timings *timings);
+
unsigned long dispc_mgr_lclk_rate(enum omap_channel channel);
unsigned long dispc_mgr_pclk_rate(enum omap_channel channel);
unsigned long dispc_core_clk_rate(void);
void dispc_mgr_set_clock_div(enum omap_channel channel,
- struct dispc_clock_info *cinfo);
+ const struct dispc_clock_info *cinfo);
int dispc_mgr_get_clock_div(enum omap_channel channel,
struct dispc_clock_info *cinfo);
-void dispc_mgr_setup(enum omap_channel channel,
- struct omap_overlay_manager_info *info);
u32 dispc_wb_get_framedone_irq(void);
bool dispc_wb_go_busy(void);
@@ -536,6 +448,8 @@ static inline unsigned long hdmi_get_pixel_clock(void)
#endif
int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev);
void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev);
+int omapdss_hdmi_core_enable(struct omap_dss_device *dssdev);
+void omapdss_hdmi_core_disable(struct omap_dss_device *dssdev);
void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev,
struct omap_video_timings *timings);
int omapdss_hdmi_display_check_timing(struct omap_dss_device *dssdev,
diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c
index 3e8287c8709..18688c12e30 100644
--- a/drivers/video/omap2/dss/dss_features.c
+++ b/drivers/video/omap2/dss/dss_features.c
@@ -18,6 +18,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/types.h>
#include <linux/err.h>
#include <linux/slab.h>
@@ -429,8 +430,6 @@ static const struct dss_param_range omap2_dss_param_range[] = {
* scaler cannot scale a image with width more than 768.
*/
[FEAT_PARAM_LINEWIDTH] = { 1, 768 },
- [FEAT_PARAM_MGR_WIDTH] = { 1, 2048 },
- [FEAT_PARAM_MGR_HEIGHT] = { 1, 2048 },
};
static const struct dss_param_range omap3_dss_param_range[] = {
@@ -445,8 +444,6 @@ static const struct dss_param_range omap3_dss_param_range[] = {
[FEAT_PARAM_DSI_FCK] = { 0, 173000000 },
[FEAT_PARAM_DOWNSCALE] = { 1, 4 },
[FEAT_PARAM_LINEWIDTH] = { 1, 1024 },
- [FEAT_PARAM_MGR_WIDTH] = { 1, 2048 },
- [FEAT_PARAM_MGR_HEIGHT] = { 1, 2048 },
};
static const struct dss_param_range omap4_dss_param_range[] = {
@@ -461,8 +458,6 @@ static const struct dss_param_range omap4_dss_param_range[] = {
[FEAT_PARAM_DSI_FCK] = { 0, 170000000 },
[FEAT_PARAM_DOWNSCALE] = { 1, 4 },
[FEAT_PARAM_LINEWIDTH] = { 1, 2048 },
- [FEAT_PARAM_MGR_WIDTH] = { 1, 2048 },
- [FEAT_PARAM_MGR_HEIGHT] = { 1, 2048 },
};
static const struct dss_param_range omap5_dss_param_range[] = {
@@ -477,8 +472,6 @@ static const struct dss_param_range omap5_dss_param_range[] = {
[FEAT_PARAM_DSI_FCK] = { 0, 170000000 },
[FEAT_PARAM_DOWNSCALE] = { 1, 4 },
[FEAT_PARAM_LINEWIDTH] = { 1, 2048 },
- [FEAT_PARAM_MGR_WIDTH] = { 1, 2048 },
- [FEAT_PARAM_MGR_HEIGHT] = { 1, 2048 },
};
static const enum dss_feat_id omap2_dss_feat_list[] = {
@@ -820,6 +813,7 @@ static const struct ti_hdmi_ip_ops omap4_hdmi_functions = {
.audio_start = ti_hdmi_4xxx_audio_start,
.audio_stop = ti_hdmi_4xxx_audio_stop,
.audio_config = ti_hdmi_4xxx_audio_config,
+ .audio_get_dma_port = ti_hdmi_4xxx_audio_get_dma_port,
#endif
};
@@ -846,11 +840,13 @@ int dss_feat_get_num_mgrs(void)
{
return omap_current_dss_features->num_mgrs;
}
+EXPORT_SYMBOL(dss_feat_get_num_mgrs);
int dss_feat_get_num_ovls(void)
{
return omap_current_dss_features->num_ovls;
}
+EXPORT_SYMBOL(dss_feat_get_num_ovls);
int dss_feat_get_num_wbs(void)
{
@@ -871,16 +867,19 @@ enum omap_display_type dss_feat_get_supported_displays(enum omap_channel channel
{
return omap_current_dss_features->supported_displays[channel];
}
+EXPORT_SYMBOL(dss_feat_get_supported_displays);
enum omap_dss_output_id dss_feat_get_supported_outputs(enum omap_channel channel)
{
return omap_current_dss_features->supported_outputs[channel];
}
+EXPORT_SYMBOL(dss_feat_get_supported_outputs);
enum omap_color_mode dss_feat_get_supported_color_modes(enum omap_plane plane)
{
return omap_current_dss_features->supported_color_modes[plane];
}
+EXPORT_SYMBOL(dss_feat_get_supported_color_modes);
enum omap_overlay_caps dss_feat_get_overlay_caps(enum omap_plane plane)
{
diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h
index fc492ef72a5..489b9bec4a6 100644
--- a/drivers/video/omap2/dss/dss_features.h
+++ b/drivers/video/omap2/dss/dss_features.h
@@ -98,19 +98,12 @@ enum dss_range_param {
FEAT_PARAM_DSI_FCK,
FEAT_PARAM_DOWNSCALE,
FEAT_PARAM_LINEWIDTH,
- FEAT_PARAM_MGR_WIDTH,
- FEAT_PARAM_MGR_HEIGHT,
};
/* DSS Feature Functions */
-int dss_feat_get_num_mgrs(void);
-int dss_feat_get_num_ovls(void);
int dss_feat_get_num_wbs(void);
unsigned long dss_feat_get_param_min(enum dss_range_param param);
unsigned long dss_feat_get_param_max(enum dss_range_param param);
-enum omap_display_type dss_feat_get_supported_displays(enum omap_channel channel);
-enum omap_dss_output_id dss_feat_get_supported_outputs(enum omap_channel channel);
-enum omap_color_mode dss_feat_get_supported_color_modes(enum omap_plane plane);
enum omap_overlay_caps dss_feat_get_overlay_caps(enum omap_plane plane);
bool dss_feat_color_mode_supported(enum omap_plane plane,
enum omap_color_mode color_mode);
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
index 0d6d7213a85..769d0828581 100644
--- a/drivers/video/omap2/dss/hdmi.c
+++ b/drivers/video/omap2/dss/hdmi.c
@@ -60,6 +60,7 @@
static struct {
struct mutex lock;
struct platform_device *pdev;
+
struct hdmi_ip_data ip_data;
struct clk *sys_clk;
@@ -295,6 +296,12 @@ static const struct hdmi_config vesa_timings[] = {
false, },
{ 0x55, HDMI_DVI },
},
+ {
+ { 1920, 1200, 154000, 32, 48, 80, 6, 3, 26,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x44, HDMI_DVI },
+ },
};
static int hdmi_runtime_get(void)
@@ -323,7 +330,6 @@ static void hdmi_runtime_put(void)
static int __init hdmi_init_display(struct omap_dss_device *dssdev)
{
- struct omap_dss_board_info *pdata = hdmi.pdev->dev.platform_data;
int r;
struct gpio gpios[] = {
@@ -334,13 +340,17 @@ static int __init hdmi_init_display(struct omap_dss_device *dssdev)
DSSDBG("init_display\n");
- dss_init_hdmi_ip_ops(&hdmi.ip_data, pdata->version);
+ dss_init_hdmi_ip_ops(&hdmi.ip_data, omapdss_get_version());
if (hdmi.vdda_hdmi_dac_reg == NULL) {
struct regulator *reg;
reg = devm_regulator_get(&hdmi.pdev->dev, "vdda_hdmi_dac");
+ /* DT HACK: try VDAC to make omapdss work for o4 sdp/panda */
+ if (IS_ERR(reg))
+ reg = devm_regulator_get(&hdmi.pdev->dev, "VDAC");
+
if (IS_ERR(reg)) {
DSSERR("can't get VDDA_HDMI_DAC regulator\n");
return PTR_ERR(reg);
@@ -356,7 +366,7 @@ static int __init hdmi_init_display(struct omap_dss_device *dssdev)
return 0;
}
-static void __exit hdmi_uninit_display(struct omap_dss_device *dssdev)
+static void hdmi_uninit_display(struct omap_dss_device *dssdev)
{
DSSDBG("uninit_display\n");
@@ -399,7 +409,8 @@ static bool hdmi_timings_compare(struct omap_video_timings *timing1,
{
int timing1_vsync, timing1_hsync, timing2_vsync, timing2_hsync;
- if ((timing2->pixel_clock == timing1->pixel_clock) &&
+ if ((DIV_ROUND_CLOSEST(timing2->pixel_clock, 1000) ==
+ DIV_ROUND_CLOSEST(timing1->pixel_clock, 1000)) &&
(timing2->x_res == timing1->x_res) &&
(timing2->y_res == timing1->y_res)) {
@@ -501,12 +512,9 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,
DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd);
}
-static int hdmi_power_on(struct omap_dss_device *dssdev)
+static int hdmi_power_on_core(struct omap_dss_device *dssdev)
{
int r;
- struct omap_video_timings *p;
- struct omap_overlay_manager *mgr = dssdev->output->manager;
- unsigned long phy;
gpio_set_value(hdmi.ct_cp_hpd_gpio, 1);
gpio_set_value(hdmi.ls_oe_gpio, 1);
@@ -522,6 +530,38 @@ static int hdmi_power_on(struct omap_dss_device *dssdev)
if (r)
goto err_runtime_get;
+ /* Make selection of HDMI in DSS */
+ dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK);
+
+ return 0;
+
+err_runtime_get:
+ regulator_disable(hdmi.vdda_hdmi_dac_reg);
+err_vdac_enable:
+ gpio_set_value(hdmi.ct_cp_hpd_gpio, 0);
+ gpio_set_value(hdmi.ls_oe_gpio, 0);
+ return r;
+}
+
+static void hdmi_power_off_core(struct omap_dss_device *dssdev)
+{
+ hdmi_runtime_put();
+ regulator_disable(hdmi.vdda_hdmi_dac_reg);
+ gpio_set_value(hdmi.ct_cp_hpd_gpio, 0);
+ gpio_set_value(hdmi.ls_oe_gpio, 0);
+}
+
+static int hdmi_power_on_full(struct omap_dss_device *dssdev)
+{
+ int r;
+ struct omap_video_timings *p;
+ struct omap_overlay_manager *mgr = dssdev->output->manager;
+ unsigned long phy;
+
+ r = hdmi_power_on_core(dssdev);
+ if (r)
+ return r;
+
dss_mgr_disable(mgr);
p = &hdmi.ip_data.cfg.timings;
@@ -549,17 +589,6 @@ static int hdmi_power_on(struct omap_dss_device *dssdev)
hdmi.ip_data.ops->video_configure(&hdmi.ip_data);
- /* Make selection of HDMI in DSS */
- dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK);
-
- /* Select the dispc clock source as PRCM clock, to ensure that it is not
- * DSI PLL source as the clock selected by DSI PLL might not be
- * sufficient for the resolution selected / that can be changed
- * dynamically by user. This can be moved to single location , say
- * Boardfile.
- */
- dss_select_dispc_clk_source(dssdev->clocks.dispc.dispc_fclk_src);
-
/* bypass TV gamma table */
dispc_enable_gamma_table(0);
@@ -583,16 +612,11 @@ err_vid_enable:
err_phy_enable:
hdmi.ip_data.ops->pll_disable(&hdmi.ip_data);
err_pll_enable:
- hdmi_runtime_put();
-err_runtime_get:
- regulator_disable(hdmi.vdda_hdmi_dac_reg);
-err_vdac_enable:
- gpio_set_value(hdmi.ct_cp_hpd_gpio, 0);
- gpio_set_value(hdmi.ls_oe_gpio, 0);
+ hdmi_power_off_core(dssdev);
return -EIO;
}
-static void hdmi_power_off(struct omap_dss_device *dssdev)
+static void hdmi_power_off_full(struct omap_dss_device *dssdev)
{
struct omap_overlay_manager *mgr = dssdev->output->manager;
@@ -601,12 +625,8 @@ static void hdmi_power_off(struct omap_dss_device *dssdev)
hdmi.ip_data.ops->video_disable(&hdmi.ip_data);
hdmi.ip_data.ops->phy_disable(&hdmi.ip_data);
hdmi.ip_data.ops->pll_disable(&hdmi.ip_data);
- hdmi_runtime_put();
-
- regulator_disable(hdmi.vdda_hdmi_dac_reg);
- gpio_set_value(hdmi.ct_cp_hpd_gpio, 0);
- gpio_set_value(hdmi.ls_oe_gpio, 0);
+ hdmi_power_off_core(dssdev);
}
int omapdss_hdmi_display_check_timing(struct omap_dss_device *dssdev,
@@ -716,7 +736,7 @@ int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev)
goto err0;
}
- r = hdmi_power_on(dssdev);
+ r = hdmi_power_on_full(dssdev);
if (r) {
DSSERR("failed to power on device\n");
goto err1;
@@ -738,13 +758,48 @@ void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev)
mutex_lock(&hdmi.lock);
- hdmi_power_off(dssdev);
+ hdmi_power_off_full(dssdev);
omap_dss_stop_device(dssdev);
mutex_unlock(&hdmi.lock);
}
+int omapdss_hdmi_core_enable(struct omap_dss_device *dssdev)
+{
+ int r = 0;
+
+ DSSDBG("ENTER omapdss_hdmi_core_enable\n");
+
+ mutex_lock(&hdmi.lock);
+
+ hdmi.ip_data.hpd_gpio = hdmi.hpd_gpio;
+
+ r = hdmi_power_on_core(dssdev);
+ if (r) {
+ DSSERR("failed to power on device\n");
+ goto err0;
+ }
+
+ mutex_unlock(&hdmi.lock);
+ return 0;
+
+err0:
+ mutex_unlock(&hdmi.lock);
+ return r;
+}
+
+void omapdss_hdmi_core_disable(struct omap_dss_device *dssdev)
+{
+ DSSDBG("Enter omapdss_hdmi_core_disable\n");
+
+ mutex_lock(&hdmi.lock);
+
+ hdmi_power_off_core(dssdev);
+
+ mutex_unlock(&hdmi.lock);
+}
+
static int hdmi_get_clocks(struct platform_device *pdev)
{
struct clk *clk;
@@ -913,7 +968,7 @@ int hdmi_audio_config(struct omap_dss_audio *audio)
static struct omap_dss_device * __init hdmi_find_dssdev(struct platform_device *pdev)
{
struct omap_dss_board_info *pdata = pdev->dev.platform_data;
- const char *def_disp_name = dss_get_default_display_name();
+ const char *def_disp_name = omapdss_get_default_display_name();
struct omap_dss_device *def_dssdev;
int i;
@@ -971,9 +1026,19 @@ static void __init hdmi_probe_pdata(struct platform_device *pdev)
return;
}
+ r = omapdss_output_set_device(&hdmi.output, dssdev);
+ if (r) {
+ DSSERR("failed to connect output to new device: %s\n",
+ dssdev->name);
+ dss_put_device(dssdev);
+ return;
+ }
+
r = dss_add_device(dssdev);
if (r) {
DSSERR("device %s register failed: %d\n", dssdev->name, r);
+ omapdss_output_unset_device(&hdmi.output);
+ hdmi_uninit_display(dssdev);
dss_put_device(dssdev);
return;
}
@@ -1000,22 +1065,22 @@ static void __exit hdmi_uninit_output(struct platform_device *pdev)
/* HDMI HW IP initialisation */
static int __init omapdss_hdmihw_probe(struct platform_device *pdev)
{
- struct resource *hdmi_mem;
+ struct resource *res;
int r;
hdmi.pdev = pdev;
mutex_init(&hdmi.lock);
+ mutex_init(&hdmi.ip_data.lock);
- hdmi_mem = platform_get_resource(hdmi.pdev, IORESOURCE_MEM, 0);
- if (!hdmi_mem) {
+ res = platform_get_resource(hdmi.pdev, IORESOURCE_MEM, 0);
+ if (!res) {
DSSERR("can't get IORESOURCE_MEM HDMI\n");
return -EINVAL;
}
/* Base address taken from platform */
- hdmi.ip_data.base_wp = ioremap(hdmi_mem->start,
- resource_size(hdmi_mem));
+ hdmi.ip_data.base_wp = devm_request_and_ioremap(&pdev->dev, res);
if (!hdmi.ip_data.base_wp) {
DSSERR("can't ioremap WP\n");
return -ENOMEM;
@@ -1023,7 +1088,7 @@ static int __init omapdss_hdmihw_probe(struct platform_device *pdev)
r = hdmi_get_clocks(pdev);
if (r) {
- iounmap(hdmi.ip_data.base_wp);
+ DSSERR("can't get clocks\n");
return r;
}
@@ -1034,9 +1099,11 @@ static int __init omapdss_hdmihw_probe(struct platform_device *pdev)
hdmi.ip_data.pll_offset = HDMI_PLLCTRL;
hdmi.ip_data.phy_offset = HDMI_PHY;
- mutex_init(&hdmi.ip_data.lock);
-
- hdmi_panel_init();
+ r = hdmi_panel_init();
+ if (r) {
+ DSSERR("can't init panel\n");
+ goto err_panel_init;
+ }
dss_debugfs_create_file("hdmi", hdmi_dump_regs);
@@ -1045,6 +1112,10 @@ static int __init omapdss_hdmihw_probe(struct platform_device *pdev)
hdmi_probe_pdata(pdev);
return 0;
+
+err_panel_init:
+ hdmi_put_clocks();
+ return r;
}
static int __exit hdmi_remove_child(struct device *dev, void *data)
@@ -1068,8 +1139,6 @@ static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
hdmi_put_clocks();
- iounmap(hdmi.ip_data.base_wp);
-
return 0;
}
diff --git a/drivers/video/omap2/dss/hdmi_panel.c b/drivers/video/omap2/dss/hdmi_panel.c
index 69fb115bab3..dfb8eda81b6 100644
--- a/drivers/video/omap2/dss/hdmi_panel.c
+++ b/drivers/video/omap2/dss/hdmi_panel.c
@@ -280,58 +280,6 @@ static void hdmi_panel_disable(struct omap_dss_device *dssdev)
mutex_unlock(&hdmi.lock);
}
-static int hdmi_panel_suspend(struct omap_dss_device *dssdev)
-{
- int r = 0;
-
- mutex_lock(&hdmi.lock);
-
- if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
- r = -EINVAL;
- goto err;
- }
-
- /*
- * TODO: notify audio users that the display was suspended. For now,
- * disable audio locally to not break our audio state machine.
- */
- hdmi_panel_audio_disable(dssdev);
-
- dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
- omapdss_hdmi_display_disable(dssdev);
-
-err:
- mutex_unlock(&hdmi.lock);
-
- return r;
-}
-
-static int hdmi_panel_resume(struct omap_dss_device *dssdev)
-{
- int r = 0;
-
- mutex_lock(&hdmi.lock);
-
- if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
- r = -EINVAL;
- goto err;
- }
-
- r = omapdss_hdmi_display_enable(dssdev);
- if (r) {
- DSSERR("failed to power on\n");
- goto err;
- }
- /* TODO: notify audio users that the panel resumed. */
-
- dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
-err:
- mutex_unlock(&hdmi.lock);
-
- return r;
-}
-
static void hdmi_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
@@ -379,20 +327,22 @@ static int hdmi_check_timings(struct omap_dss_device *dssdev,
static int hdmi_read_edid(struct omap_dss_device *dssdev, u8 *buf, int len)
{
int r;
+ bool need_enable;
mutex_lock(&hdmi.lock);
- if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
- r = omapdss_hdmi_display_enable(dssdev);
+ need_enable = dssdev->state == OMAP_DSS_DISPLAY_DISABLED;
+
+ if (need_enable) {
+ r = omapdss_hdmi_core_enable(dssdev);
if (r)
goto err;
}
r = omapdss_hdmi_read_edid(buf, len);
- if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
- dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
- omapdss_hdmi_display_disable(dssdev);
+ if (need_enable)
+ omapdss_hdmi_core_disable(dssdev);
err:
mutex_unlock(&hdmi.lock);
@@ -402,20 +352,22 @@ err:
static bool hdmi_detect(struct omap_dss_device *dssdev)
{
int r;
+ bool need_enable;
mutex_lock(&hdmi.lock);
- if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
- r = omapdss_hdmi_display_enable(dssdev);
+ need_enable = dssdev->state == OMAP_DSS_DISPLAY_DISABLED;
+
+ if (need_enable) {
+ r = omapdss_hdmi_core_enable(dssdev);
if (r)
goto err;
}
r = omapdss_hdmi_detect();
- if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
- dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
- omapdss_hdmi_display_disable(dssdev);
+ if (need_enable)
+ omapdss_hdmi_core_disable(dssdev);
err:
mutex_unlock(&hdmi.lock);
@@ -427,8 +379,6 @@ static struct omap_dss_driver hdmi_driver = {
.remove = hdmi_panel_remove,
.enable = hdmi_panel_enable,
.disable = hdmi_panel_disable,
- .suspend = hdmi_panel_suspend,
- .resume = hdmi_panel_resume,
.get_timings = hdmi_get_timings,
.set_timings = hdmi_set_timings,
.check_timings = hdmi_check_timings,
@@ -454,9 +404,7 @@ int hdmi_panel_init(void)
spin_lock_init(&hdmi.audio_lock);
#endif
- omap_dss_register_driver(&hdmi_driver);
-
- return 0;
+ return omap_dss_register_driver(&hdmi_driver);
}
void hdmi_panel_exit(void)
diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c
index c54d2f620ce..2551eaa14c4 100644
--- a/drivers/video/omap2/dss/manager.c
+++ b/drivers/video/omap2/dss/manager.c
@@ -36,36 +36,6 @@
static int num_managers;
static struct omap_overlay_manager *managers;
-static inline struct omap_dss_device *dss_mgr_get_device(struct omap_overlay_manager *mgr)
-{
- return mgr->output ? mgr->output->device : NULL;
-}
-
-static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr)
-{
- unsigned long timeout = msecs_to_jiffies(500);
- struct omap_dss_device *dssdev = mgr->get_device(mgr);
- u32 irq;
- int r;
-
- r = dispc_runtime_get();
- if (r)
- return r;
-
- if (dssdev->type == OMAP_DISPLAY_TYPE_VENC)
- irq = DISPC_IRQ_EVSYNC_ODD;
- else if (dssdev->type == OMAP_DISPLAY_TYPE_HDMI)
- irq = DISPC_IRQ_EVSYNC_EVEN;
- else
- irq = dispc_mgr_get_vsync_irq(mgr->id);
-
- r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
-
- dispc_runtime_put();
-
- return r;
-}
-
int dss_init_overlay_managers(struct platform_device *pdev)
{
int i, r;
@@ -99,15 +69,6 @@ int dss_init_overlay_managers(struct platform_device *pdev)
break;
}
- mgr->set_output = &dss_mgr_set_output;
- mgr->unset_output = &dss_mgr_unset_output;
- mgr->apply = &omap_dss_mgr_apply;
- mgr->set_manager_info = &dss_mgr_set_info;
- mgr->get_manager_info = &dss_mgr_get_info;
- mgr->wait_for_go = &dss_mgr_wait_for_go;
- mgr->wait_for_vsync = &dss_mgr_wait_for_vsync;
- mgr->get_device = &dss_mgr_get_device;
-
mgr->caps = 0;
mgr->supported_displays =
dss_feat_get_supported_displays(mgr->id);
diff --git a/drivers/video/omap2/dss/output.c b/drivers/video/omap2/dss/output.c
index 813f26682b7..79dea1a1a73 100644
--- a/drivers/video/omap2/dss/output.c
+++ b/drivers/video/omap2/dss/output.c
@@ -114,35 +114,67 @@ struct omap_dss_output *omap_dss_get_output(enum omap_dss_output_id id)
return NULL;
}
-struct omap_dss_output *omapdss_get_output_from_dssdev(struct omap_dss_device *dssdev)
+static const struct dss_mgr_ops *dss_mgr_ops;
+
+int dss_install_mgr_ops(const struct dss_mgr_ops *mgr_ops)
{
- struct omap_dss_output *out = NULL;
- enum omap_dss_output_id id;
-
- switch (dssdev->type) {
- case OMAP_DISPLAY_TYPE_DPI:
- out = omap_dss_get_output(OMAP_DSS_OUTPUT_DPI);
- break;
- case OMAP_DISPLAY_TYPE_DBI:
- out = omap_dss_get_output(OMAP_DSS_OUTPUT_DBI);
- break;
- case OMAP_DISPLAY_TYPE_SDI:
- out = omap_dss_get_output(OMAP_DSS_OUTPUT_SDI);
- break;
- case OMAP_DISPLAY_TYPE_VENC:
- out = omap_dss_get_output(OMAP_DSS_OUTPUT_VENC);
- break;
- case OMAP_DISPLAY_TYPE_HDMI:
- out = omap_dss_get_output(OMAP_DSS_OUTPUT_HDMI);
- break;
- case OMAP_DISPLAY_TYPE_DSI:
- id = dssdev->phy.dsi.module == 0 ? OMAP_DSS_OUTPUT_DSI1 :
- OMAP_DSS_OUTPUT_DSI2;
- out = omap_dss_get_output(id);
- break;
- default:
- break;
- }
+ if (dss_mgr_ops)
+ return -EBUSY;
+
+ dss_mgr_ops = mgr_ops;
+
+ return 0;
+}
+EXPORT_SYMBOL(dss_install_mgr_ops);
+
+void dss_uninstall_mgr_ops(void)
+{
+ dss_mgr_ops = NULL;
+}
+EXPORT_SYMBOL(dss_uninstall_mgr_ops);
+
+void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
+ const struct omap_video_timings *timings)
+{
+ dss_mgr_ops->set_timings(mgr, timings);
+}
+EXPORT_SYMBOL(dss_mgr_set_timings);
+
+void dss_mgr_set_lcd_config(struct omap_overlay_manager *mgr,
+ const struct dss_lcd_mgr_config *config)
+{
+ dss_mgr_ops->set_lcd_config(mgr, config);
+}
+EXPORT_SYMBOL(dss_mgr_set_lcd_config);
+
+int dss_mgr_enable(struct omap_overlay_manager *mgr)
+{
+ return dss_mgr_ops->enable(mgr);
+}
+EXPORT_SYMBOL(dss_mgr_enable);
+
+void dss_mgr_disable(struct omap_overlay_manager *mgr)
+{
+ dss_mgr_ops->disable(mgr);
+}
+EXPORT_SYMBOL(dss_mgr_disable);
- return out;
+void dss_mgr_start_update(struct omap_overlay_manager *mgr)
+{
+ dss_mgr_ops->start_update(mgr);
+}
+EXPORT_SYMBOL(dss_mgr_start_update);
+
+int dss_mgr_register_framedone_handler(struct omap_overlay_manager *mgr,
+ void (*handler)(void *), void *data)
+{
+ return dss_mgr_ops->register_framedone_handler(mgr, handler, data);
+}
+EXPORT_SYMBOL(dss_mgr_register_framedone_handler);
+
+void dss_mgr_unregister_framedone_handler(struct omap_overlay_manager *mgr,
+ void (*handler)(void *), void *data)
+{
+ dss_mgr_ops->unregister_framedone_handler(mgr, handler, data);
}
+EXPORT_SYMBOL(dss_mgr_unregister_framedone_handler);
diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c
index 45f4994bc6b..eccde322c28 100644
--- a/drivers/video/omap2/dss/overlay.c
+++ b/drivers/video/omap2/dss/overlay.c
@@ -38,13 +38,6 @@
static int num_overlays;
static struct omap_overlay *overlays;
-static inline struct omap_dss_device *dss_ovl_get_device(struct omap_overlay *ovl)
-{
- return ovl->manager ?
- (ovl->manager->output ? ovl->manager->output->device : NULL) :
- NULL;
-}
-
int omap_dss_get_num_overlays(void)
{
return num_overlays;
@@ -93,16 +86,6 @@ void dss_init_overlays(struct platform_device *pdev)
break;
}
- ovl->is_enabled = &dss_ovl_is_enabled;
- ovl->enable = &dss_ovl_enable;
- ovl->disable = &dss_ovl_disable;
- ovl->set_manager = &dss_ovl_set_manager;
- ovl->unset_manager = &dss_ovl_unset_manager;
- ovl->set_overlay_info = &dss_ovl_set_info;
- ovl->get_overlay_info = &dss_ovl_get_info;
- ovl->wait_for_go = &dss_mgr_wait_for_go_ovl;
- ovl->get_device = &dss_ovl_get_device;
-
ovl->caps = dss_feat_get_overlay_caps(ovl->id);
ovl->supported_modes =
dss_feat_get_supported_color_modes(ovl->id);
diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c
index 7282e5af3e1..e903dd3f54d 100644
--- a/drivers/video/omap2/dss/rfbi.c
+++ b/drivers/video/omap2/dss/rfbi.c
@@ -342,7 +342,7 @@ static int rfbi_transfer_area(struct omap_dss_device *dssdev,
return 0;
}
-static void framedone_callback(void *data, u32 mask)
+static void framedone_callback(void *data)
{
void (*callback)(void *data);
@@ -908,8 +908,8 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev)
goto err0;
}
- r = omap_dispc_register_isr(framedone_callback, NULL,
- DISPC_IRQ_FRAMEDONE);
+ r = dss_mgr_register_framedone_handler(out->manager,
+ framedone_callback, NULL);
if (r) {
DSSERR("can't get FRAMEDONE irq\n");
goto err1;
@@ -933,8 +933,10 @@ EXPORT_SYMBOL(omapdss_rfbi_display_enable);
void omapdss_rfbi_display_disable(struct omap_dss_device *dssdev)
{
- omap_dispc_unregister_isr(framedone_callback, NULL,
- DISPC_IRQ_FRAMEDONE);
+ struct omap_dss_output *out = dssdev->output;
+
+ dss_mgr_unregister_framedone_handler(out->manager,
+ framedone_callback, NULL);
omap_dss_stop_device(dssdev);
rfbi_runtime_put();
@@ -950,7 +952,7 @@ static int __init rfbi_init_display(struct omap_dss_device *dssdev)
static struct omap_dss_device * __init rfbi_find_dssdev(struct platform_device *pdev)
{
struct omap_dss_board_info *pdata = pdev->dev.platform_data;
- const char *def_disp_name = dss_get_default_display_name();
+ const char *def_disp_name = omapdss_get_default_display_name();
struct omap_dss_device *def_dssdev;
int i;
@@ -999,9 +1001,18 @@ static void __init rfbi_probe_pdata(struct platform_device *rfbidev)
return;
}
+ r = omapdss_output_set_device(&rfbi.output, dssdev);
+ if (r) {
+ DSSERR("failed to connect output to new device: %s\n",
+ dssdev->name);
+ dss_put_device(dssdev);
+ return;
+ }
+
r = dss_add_device(dssdev);
if (r) {
DSSERR("device %s register failed: %d\n", dssdev->name, r);
+ omapdss_output_unset_device(&rfbi.output);
dss_put_device(dssdev);
return;
}
diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c
index 7760851f6e5..62b5374ce43 100644
--- a/drivers/video/omap2/dss/sdi.c
+++ b/drivers/video/omap2/dss/sdi.c
@@ -205,7 +205,7 @@ static int __init sdi_init_display(struct omap_dss_device *dssdev)
static struct omap_dss_device * __init sdi_find_dssdev(struct platform_device *pdev)
{
struct omap_dss_board_info *pdata = pdev->dev.platform_data;
- const char *def_disp_name = dss_get_default_display_name();
+ const char *def_disp_name = omapdss_get_default_display_name();
struct omap_dss_device *def_dssdev;
int i;
@@ -254,9 +254,18 @@ static void __init sdi_probe_pdata(struct platform_device *sdidev)
return;
}
+ r = omapdss_output_set_device(&sdi.output, dssdev);
+ if (r) {
+ DSSERR("failed to connect output to new device: %s\n",
+ dssdev->name);
+ dss_put_device(dssdev);
+ return;
+ }
+
r = dss_add_device(dssdev);
if (r) {
DSSERR("device %s register failed: %d\n", dssdev->name, r);
+ omapdss_output_unset_device(&sdi.output);
dss_put_device(dssdev);
return;
}
diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h
index b046c208cb9..216aa704f9d 100644
--- a/drivers/video/omap2/dss/ti_hdmi.h
+++ b/drivers/video/omap2/dss/ti_hdmi.h
@@ -102,6 +102,8 @@ struct ti_hdmi_ip_ops {
int (*audio_config)(struct hdmi_ip_data *ip_data,
struct omap_dss_audio *audio);
+
+ int (*audio_get_dma_port)(u32 *offset, u32 *size);
#endif
};
@@ -183,5 +185,6 @@ int ti_hdmi_4xxx_audio_start(struct hdmi_ip_data *ip_data);
void ti_hdmi_4xxx_audio_stop(struct hdmi_ip_data *ip_data);
int ti_hdmi_4xxx_audio_config(struct hdmi_ip_data *ip_data,
struct omap_dss_audio *audio);
+int ti_hdmi_4xxx_audio_get_dma_port(u32 *offset, u32 *size);
#endif
#endif
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
index c23b85a20cd..e18b222ed73 100644
--- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
+++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
@@ -899,7 +899,7 @@ void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
#define DUMPCOREAV(r) seq_printf(s, "%-35s %08x\n", #r,\
hdmi_read_reg(hdmi_av_base(ip_data), r))
#define DUMPCOREAV2(i, r) seq_printf(s, "%s[%d]%*s %08x\n", #r, i, \
- (i < 10) ? 32 - strlen(#r) : 31 - strlen(#r), " ", \
+ (i < 10) ? 32 - (int)strlen(#r) : 31 - (int)strlen(#r), " ", \
hdmi_read_reg(hdmi_av_base(ip_data), CORE_REG(i, r)))
DUMPCORE(HDMI_CORE_SYS_VND_IDL);
@@ -1418,4 +1418,13 @@ void ti_hdmi_4xxx_audio_stop(struct hdmi_ip_data *ip_data)
REG_FLD_MOD(hdmi_wp_base(ip_data),
HDMI_WP_AUDIO_CTRL, false, 30, 30);
}
+
+int ti_hdmi_4xxx_audio_get_dma_port(u32 *offset, u32 *size)
+{
+ if (!offset || !size)
+ return -EINVAL;
+ *offset = HDMI_WP_AUDIO_DATA;
+ *size = 4;
+ return 0;
+}
#endif
diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c
index 56efa3bb465..006caf3cb50 100644
--- a/drivers/video/omap2/dss/venc.c
+++ b/drivers/video/omap2/dss/venc.c
@@ -744,7 +744,7 @@ static void venc_put_clocks(void)
static struct omap_dss_device * __init venc_find_dssdev(struct platform_device *pdev)
{
struct omap_dss_board_info *pdata = pdev->dev.platform_data;
- const char *def_disp_name = dss_get_default_display_name();
+ const char *def_disp_name = omapdss_get_default_display_name();
struct omap_dss_device *def_dssdev;
int i;
@@ -795,9 +795,18 @@ static void __init venc_probe_pdata(struct platform_device *vencdev)
return;
}
+ r = omapdss_output_set_device(&venc.output, dssdev);
+ if (r) {
+ DSSERR("failed to connect output to new device: %s\n",
+ dssdev->name);
+ dss_put_device(dssdev);
+ return;
+ }
+
r = dss_add_device(dssdev);
if (r) {
DSSERR("device %s register failed: %d\n", dssdev->name, r);
+ omapdss_output_unset_device(&venc.output);
dss_put_device(dssdev);
return;
}
diff --git a/drivers/video/omap2/dss/venc_panel.c b/drivers/video/omap2/dss/venc_panel.c
index d55b8784ecf..0d2b1a0834a 100644
--- a/drivers/video/omap2/dss/venc_panel.c
+++ b/drivers/video/omap2/dss/venc_panel.c
@@ -157,12 +157,6 @@ static void venc_panel_disable(struct omap_dss_device *dssdev)
if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED)
goto end;
- if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) {
- /* suspended is the same as disabled with venc */
- dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
- goto end;
- }
-
omapdss_venc_display_disable(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
@@ -170,17 +164,6 @@ end:
mutex_unlock(&venc_panel.lock);
}
-static int venc_panel_suspend(struct omap_dss_device *dssdev)
-{
- venc_panel_disable(dssdev);
- return 0;
-}
-
-static int venc_panel_resume(struct omap_dss_device *dssdev)
-{
- return venc_panel_enable(dssdev);
-}
-
static void venc_panel_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
@@ -222,8 +205,6 @@ static struct omap_dss_driver venc_driver = {
.enable = venc_panel_enable,
.disable = venc_panel_disable,
- .suspend = venc_panel_suspend,
- .resume = venc_panel_resume,
.get_resolution = omapdss_default_get_resolution,
.get_recommended_bpp = omapdss_default_get_recommended_bpp,
diff --git a/drivers/video/omap2/omapfb/Kconfig b/drivers/video/omap2/omapfb/Kconfig
index 4ea17dc3258..4cb12ce6885 100644
--- a/drivers/video/omap2/omapfb/Kconfig
+++ b/drivers/video/omap2/omapfb/Kconfig
@@ -2,7 +2,6 @@ menuconfig FB_OMAP2
tristate "OMAP2+ frame buffer support"
depends on FB && OMAP2_DSS && !DRM_OMAP
- select OMAP2_VRAM
select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c
index 532a31b3d96..d30b45d7264 100644
--- a/drivers/video/omap2/omapfb/omapfb-ioctl.c
+++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c
@@ -28,10 +28,10 @@
#include <linux/omapfb.h>
#include <linux/vmalloc.h>
#include <linux/export.h>
+#include <linux/sizes.h>
#include <video/omapdss.h>
#include <video/omapvrfb.h>
-#include <plat/vram.h>
#include "omapfb.h"
@@ -211,6 +211,7 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
{
struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_device *fbdev = ofbi->fbdev;
+ struct omap_dss_device *display = fb2display(fbi);
struct omapfb2_mem_region *rg;
int r = 0, i;
size_t size;
@@ -220,6 +221,9 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
size = PAGE_ALIGN(mi->size);
+ if (display && display->driver->sync)
+ display->driver->sync(display);
+
rg = ofbi->region;
down_write_nested(&rg->lock, rg->id);
@@ -279,7 +283,7 @@ static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
return 0;
}
-static int omapfb_update_window_nolock(struct fb_info *fbi,
+static int omapfb_update_window(struct fb_info *fbi,
u32 x, u32 y, u32 w, u32 h)
{
struct omap_dss_device *display = fb2display(fbi);
@@ -299,27 +303,6 @@ static int omapfb_update_window_nolock(struct fb_info *fbi,
return display->driver->update(display, x, y, w, h);
}
-/* This function is exported for SGX driver use */
-int omapfb_update_window(struct fb_info *fbi,
- u32 x, u32 y, u32 w, u32 h)
-{
- struct omapfb_info *ofbi = FB2OFB(fbi);
- struct omapfb2_device *fbdev = ofbi->fbdev;
- int r;
-
- if (!lock_fb_info(fbi))
- return -ENODEV;
- omapfb_lock(fbdev);
-
- r = omapfb_update_window_nolock(fbi, x, y, w, h);
-
- omapfb_unlock(fbdev);
- unlock_fb_info(fbi);
-
- return r;
-}
-EXPORT_SYMBOL(omapfb_update_window);
-
int omapfb_set_update_mode(struct fb_info *fbi,
enum omapfb_update_mode mode)
{
@@ -646,7 +629,7 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
break;
}
- r = omapfb_update_window_nolock(fbi, p.uwnd_o.x, p.uwnd_o.y,
+ r = omapfb_update_window(fbi, p.uwnd_o.x, p.uwnd_o.y,
p.uwnd_o.width, p.uwnd_o.height);
break;
@@ -663,7 +646,7 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
break;
}
- r = omapfb_update_window_nolock(fbi, p.uwnd.x, p.uwnd.y,
+ r = omapfb_update_window(fbi, p.uwnd.x, p.uwnd.y,
p.uwnd.width, p.uwnd.height);
break;
@@ -853,14 +836,15 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
break;
case OMAPFB_GET_VRAM_INFO: {
- unsigned long vram, free, largest;
-
DBG("ioctl GET_VRAM_INFO\n");
- omap_vram_get_info(&vram, &free, &largest);
- p.vram_info.total = vram;
- p.vram_info.free = free;
- p.vram_info.largest_free_block = largest;
+ /*
+ * We don't have the ability to get this vram info anymore.
+ * Fill in something that should keep the applications working.
+ */
+ p.vram_info.total = SZ_1M * 64;
+ p.vram_info.free = SZ_1M * 64;
+ p.vram_info.largest_free_block = SZ_1M * 64;
if (copy_to_user((void __user *)arg, &p.vram_info,
sizeof(p.vram_info)))
diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
index bc225e46fdd..ca585ef37f2 100644
--- a/drivers/video/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -31,7 +31,6 @@
#include <linux/omapfb.h>
#include <video/omapdss.h>
-#include <plat/vram.h>
#include <video/omapvrfb.h>
#include "omapfb.h"
@@ -1258,11 +1257,10 @@ static int omapfb_blank(int blank, struct fb_info *fbi)
switch (blank) {
case FB_BLANK_UNBLANK:
- if (display->state != OMAP_DSS_DISPLAY_SUSPENDED)
+ if (display->state == OMAP_DSS_DISPLAY_ACTIVE)
goto exit;
- if (display->driver->resume)
- r = display->driver->resume(display);
+ r = display->driver->enable(display);
if ((display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) &&
d->update_mode == OMAPFB_AUTO_UPDATE &&
@@ -1283,8 +1281,7 @@ static int omapfb_blank(int blank, struct fb_info *fbi)
if (d->auto_update_work_enabled)
omapfb_stop_auto_update(fbdev, display);
- if (display->driver->suspend)
- r = display->driver->suspend(display);
+ display->driver->disable(display);
break;
@@ -1335,24 +1332,25 @@ static void omapfb_free_fbmem(struct fb_info *fbi)
rg = ofbi->region;
- WARN_ON(atomic_read(&rg->map_count));
-
- if (rg->paddr)
- if (omap_vram_free(rg->paddr, rg->size))
- dev_err(fbdev->dev, "VRAM FREE failed\n");
+ if (rg->token == NULL)
+ return;
- if (rg->vaddr)
- iounmap(rg->vaddr);
+ WARN_ON(atomic_read(&rg->map_count));
if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
/* unmap the 0 angle rotation */
if (rg->vrfb.vaddr[0]) {
iounmap(rg->vrfb.vaddr[0]);
- omap_vrfb_release_ctx(&rg->vrfb);
rg->vrfb.vaddr[0] = NULL;
}
+
+ omap_vrfb_release_ctx(&rg->vrfb);
}
+ dma_free_attrs(fbdev->dev, rg->size, rg->token, rg->dma_handle,
+ &rg->attrs);
+
+ rg->token = NULL;
rg->vaddr = NULL;
rg->paddr = 0;
rg->alloc = 0;
@@ -1387,7 +1385,9 @@ static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size,
struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_device *fbdev = ofbi->fbdev;
struct omapfb2_mem_region *rg;
- void __iomem *vaddr;
+ void *token;
+ DEFINE_DMA_ATTRS(attrs);
+ dma_addr_t dma_handle;
int r;
rg = ofbi->region;
@@ -1402,42 +1402,40 @@ static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size,
size = PAGE_ALIGN(size);
- if (!paddr) {
- DBG("allocating %lu bytes for fb %d\n", size, ofbi->id);
- r = omap_vram_alloc(size, &paddr);
- } else {
- DBG("reserving %lu bytes at %lx for fb %d\n", size, paddr,
- ofbi->id);
- r = omap_vram_reserve(paddr, size);
- }
+ dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
- if (r) {
+ if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
+ dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs);
+
+ DBG("allocating %lu bytes for fb %d\n", size, ofbi->id);
+
+ token = dma_alloc_attrs(fbdev->dev, size, &dma_handle,
+ GFP_KERNEL, &attrs);
+
+ if (token == NULL) {
dev_err(fbdev->dev, "failed to allocate framebuffer\n");
return -ENOMEM;
}
- if (ofbi->rotation_type != OMAP_DSS_ROT_VRFB) {
- vaddr = ioremap_wc(paddr, size);
-
- if (!vaddr) {
- dev_err(fbdev->dev, "failed to ioremap framebuffer\n");
- omap_vram_free(paddr, size);
- return -ENOMEM;
- }
+ DBG("allocated VRAM paddr %lx, vaddr %p\n",
+ (unsigned long)dma_handle, token);
- DBG("allocated VRAM paddr %lx, vaddr %p\n", paddr, vaddr);
- } else {
+ if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
r = omap_vrfb_request_ctx(&rg->vrfb);
if (r) {
+ dma_free_attrs(fbdev->dev, size, token, dma_handle,
+ &attrs);
dev_err(fbdev->dev, "vrfb create ctx failed\n");
return r;
}
-
- vaddr = NULL;
}
- rg->paddr = paddr;
- rg->vaddr = vaddr;
+ rg->attrs = attrs;
+ rg->token = token;
+ rg->dma_handle = dma_handle;
+
+ rg->paddr = (unsigned long)dma_handle;
+ rg->vaddr = (void __iomem *)token;
rg->size = size;
rg->alloc = 1;
@@ -1531,6 +1529,9 @@ static int omapfb_parse_vram_param(const char *param, int max_entries,
}
+ WARN_ONCE(paddr,
+ "reserving memory at predefined address not supported\n");
+
paddrs[fbnum] = paddr;
sizes[fbnum] = size;
@@ -1610,7 +1611,6 @@ int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type)
{
struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_device *fbdev = ofbi->fbdev;
- struct omap_dss_device *display = fb2display(fbi);
struct omapfb2_mem_region *rg = ofbi->region;
unsigned long old_size = rg->size;
unsigned long old_paddr = rg->paddr;
@@ -1625,9 +1625,6 @@ int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type)
if (old_size == size && old_type == type)
return 0;
- if (display && display->driver->sync)
- display->driver->sync(display);
-
omapfb_free_fbmem(fbi);
if (size == 0) {
@@ -1882,7 +1879,6 @@ static void omapfb_free_resources(struct omapfb2_device *fbdev)
}
dev_set_drvdata(fbdev->dev, NULL);
- kfree(fbdev);
}
static int omapfb_create_framebuffers(struct omapfb2_device *fbdev)
@@ -2258,26 +2254,28 @@ static int omapfb_find_best_mode(struct omap_dss_device *display,
{
struct fb_monspecs *specs;
u8 *edid;
- int r, i, best_xres, best_idx, len;
+ int r, i, best_idx, len;
if (!display->driver->read_edid)
return -ENODEV;
len = 0x80 * 2;
edid = kmalloc(len, GFP_KERNEL);
+ if (edid == NULL)
+ return -ENOMEM;
r = display->driver->read_edid(display, edid, len);
if (r < 0)
goto err1;
specs = kzalloc(sizeof(*specs), GFP_KERNEL);
+ if (specs == NULL) {
+ r = -ENOMEM;
+ goto err1;
+ }
fb_edid_to_monspecs(edid, specs);
- if (edid[126] > 0)
- fb_edid_add_monspecs(edid + 0x80, specs);
-
- best_xres = 0;
best_idx = -1;
for (i = 0; i < specs->modedb_len; ++i) {
@@ -2293,16 +2291,20 @@ static int omapfb_find_best_mode(struct omap_dss_device *display,
if (m->xres == 2880 || m->xres == 1440)
continue;
+ if (m->vmode & FB_VMODE_INTERLACED ||
+ m->vmode & FB_VMODE_DOUBLE)
+ continue;
+
fb_videomode_to_omap_timings(m, display, &t);
r = display->driver->check_timings(display, &t);
- if (r == 0 && best_xres < m->xres) {
- best_xres = m->xres;
+ if (r == 0) {
best_idx = i;
+ break;
}
}
- if (best_xres == 0) {
+ if (best_idx == -1) {
r = -ENOENT;
goto err2;
}
@@ -2371,15 +2373,62 @@ static int omapfb_init_display(struct omapfb2_device *fbdev,
return 0;
}
+static int omapfb_init_connections(struct omapfb2_device *fbdev,
+ struct omap_dss_device *def_dssdev)
+{
+ int i, r;
+ struct omap_overlay_manager *mgr;
+
+ if (!def_dssdev->output) {
+ dev_err(fbdev->dev, "no output for the default display\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < fbdev->num_displays; ++i) {
+ struct omap_dss_device *dssdev = fbdev->displays[i].dssdev;
+ struct omap_dss_output *out = dssdev->output;
+
+ mgr = omap_dss_get_overlay_manager(dssdev->channel);
+
+ if (!mgr || !out)
+ continue;
+
+ if (mgr->output)
+ mgr->unset_output(mgr);
+
+ mgr->set_output(mgr, out);
+ }
+
+ mgr = def_dssdev->output->manager;
+
+ if (!mgr) {
+ dev_err(fbdev->dev, "no ovl manager for the default display\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < fbdev->num_overlays; i++) {
+ struct omap_overlay *ovl = fbdev->overlays[i];
+
+ if (ovl->manager)
+ ovl->unset_manager(ovl);
+
+ r = ovl->set_manager(ovl, mgr);
+ if (r)
+ dev_warn(fbdev->dev,
+ "failed to connect overlay %s to manager %s\n",
+ ovl->name, mgr->name);
+ }
+
+ return 0;
+}
+
static int __init omapfb_probe(struct platform_device *pdev)
{
struct omapfb2_device *fbdev = NULL;
int r = 0;
int i;
- struct omap_overlay *ovl;
struct omap_dss_device *def_display;
struct omap_dss_device *dssdev;
- struct omap_dss_device *ovl_device;
DBG("omapfb_probe\n");
@@ -2389,7 +2438,8 @@ static int __init omapfb_probe(struct platform_device *pdev)
goto err0;
}
- fbdev = kzalloc(sizeof(struct omapfb2_device), GFP_KERNEL);
+ fbdev = devm_kzalloc(&pdev->dev, sizeof(struct omapfb2_device),
+ GFP_KERNEL);
if (fbdev == NULL) {
r = -ENOMEM;
goto err0;
@@ -2401,13 +2451,15 @@ static int __init omapfb_probe(struct platform_device *pdev)
"ignoring the module parameter vrfb=y\n");
}
+ r = omapdss_compat_init();
+ if (r)
+ goto err0;
mutex_init(&fbdev->mtx);
fbdev->dev = &pdev->dev;
platform_set_drvdata(pdev, fbdev);
- r = 0;
fbdev->num_displays = 0;
dssdev = NULL;
for_each_dss_dev(dssdev) {
@@ -2430,9 +2482,6 @@ static int __init omapfb_probe(struct platform_device *pdev)
d->update_mode = OMAPFB_AUTO_UPDATE;
}
- if (r)
- goto cleanup;
-
if (fbdev->num_displays == 0) {
dev_err(&pdev->dev, "no displays\n");
r = -EINVAL;
@@ -2447,15 +2496,33 @@ static int __init omapfb_probe(struct platform_device *pdev)
for (i = 0; i < fbdev->num_managers; i++)
fbdev->managers[i] = omap_dss_get_overlay_manager(i);
- /* gfx overlay should be the default one. find a display
- * connected to that, and use it as default display */
- ovl = omap_dss_get_overlay(0);
- ovl_device = ovl->get_device(ovl);
- if (ovl_device) {
- def_display = ovl_device;
- } else {
- dev_warn(&pdev->dev, "cannot find default display\n");
- def_display = NULL;
+ def_display = NULL;
+
+ for (i = 0; i < fbdev->num_displays; ++i) {
+ struct omap_dss_device *dssdev;
+ const char *def_name;
+
+ def_name = omapdss_get_default_display_name();
+
+ dssdev = fbdev->displays[i].dssdev;
+
+ if (def_name == NULL ||
+ (dssdev->name && strcmp(def_name, dssdev->name) == 0)) {
+ def_display = dssdev;
+ break;
+ }
+ }
+
+ if (def_display == NULL) {
+ dev_err(fbdev->dev, "failed to find default display\n");
+ r = -EINVAL;
+ goto cleanup;
+ }
+
+ r = omapfb_init_connections(fbdev, def_display);
+ if (r) {
+ dev_err(fbdev->dev, "failed to init overlay connections\n");
+ goto cleanup;
}
if (def_mode && strlen(def_mode) > 0) {
@@ -2506,6 +2573,7 @@ static int __init omapfb_probe(struct platform_device *pdev)
cleanup:
omapfb_free_resources(fbdev);
+ omapdss_compat_uninit();
err0:
dev_err(&pdev->dev, "failed to setup omapfb\n");
return r;
@@ -2521,6 +2589,8 @@ static int __exit omapfb_remove(struct platform_device *pdev)
omapfb_free_resources(fbdev);
+ omapdss_compat_uninit();
+
return 0;
}
diff --git a/drivers/video/omap2/omapfb/omapfb-sysfs.c b/drivers/video/omap2/omapfb/omapfb-sysfs.c
index 17aa174e187..18fa9e1d003 100644
--- a/drivers/video/omap2/omapfb/omapfb-sysfs.c
+++ b/drivers/video/omap2/omapfb/omapfb-sysfs.c
@@ -441,6 +441,7 @@ static ssize_t store_size(struct device *dev, struct device_attribute *attr,
struct fb_info *fbi = dev_get_drvdata(dev);
struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_device *fbdev = ofbi->fbdev;
+ struct omap_dss_device *display = fb2display(fbi);
struct omapfb2_mem_region *rg;
unsigned long size;
int r;
@@ -455,6 +456,9 @@ static ssize_t store_size(struct device *dev, struct device_attribute *attr,
if (!lock_fb_info(fbi))
return -ENODEV;
+ if (display && display->driver->sync)
+ display->driver->sync(display);
+
rg = ofbi->region;
down_write_nested(&rg->lock, rg->id);
diff --git a/drivers/video/omap2/omapfb/omapfb.h b/drivers/video/omap2/omapfb/omapfb.h
index 5ced9b334d3..623cd872a36 100644
--- a/drivers/video/omap2/omapfb/omapfb.h
+++ b/drivers/video/omap2/omapfb/omapfb.h
@@ -28,6 +28,8 @@
#endif
#include <linux/rwsem.h>
+#include <linux/dma-attrs.h>
+#include <linux/dma-mapping.h>
#include <video/omapdss.h>
@@ -49,6 +51,9 @@ extern bool omapfb_debug;
struct omapfb2_mem_region {
int id;
+ struct dma_attrs attrs;
+ void *token;
+ dma_addr_t dma_handle;
u32 paddr;
void __iomem *vaddr;
struct vrfb vrfb;
@@ -124,9 +129,6 @@ void omapfb_remove_sysfs(struct omapfb2_device *fbdev);
int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg);
-int omapfb_update_window(struct fb_info *fbi,
- u32 x, u32 y, u32 w, u32 h);
-
int dss_mode_to_fb_mode(enum omap_color_mode dssmode,
struct fb_var_screeninfo *var);
@@ -144,16 +146,16 @@ int omapfb_set_update_mode(struct fb_info *fbi, enum omapfb_update_mode mode);
static inline struct omap_dss_device *fb2display(struct fb_info *fbi)
{
struct omapfb_info *ofbi = FB2OFB(fbi);
- int i;
+ struct omap_overlay *ovl;
/* XXX: returns the display connected to first attached overlay */
- for (i = 0; i < ofbi->num_overlays; i++) {
- struct omap_overlay *ovl = ofbi->overlays[i];
- return ovl->get_device(ovl);
- }
+ if (ofbi->num_overlays == 0)
+ return NULL;
- return NULL;
+ ovl = ofbi->overlays[0];
+
+ return ovl->get_device(ovl);
}
static inline struct omapfb_display_data *get_display_data(
diff --git a/drivers/video/omap2/vram.c b/drivers/video/omap2/vram.c
deleted file mode 100644
index f2b15c4a75b..00000000000
--- a/drivers/video/omap2/vram.c
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- * VRAM manager for OMAP
- *
- * Copyright (C) 2009 Nokia Corporation
- * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-/*#define DEBUG*/
-
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <linux/seq_file.h>
-#include <linux/memblock.h>
-#include <linux/completion.h>
-#include <linux/debugfs.h>
-#include <linux/jiffies.h>
-#include <linux/module.h>
-
-#include <asm/setup.h>
-
-#include <plat/vram.h>
-
-#ifdef DEBUG
-#define DBG(format, ...) pr_debug("VRAM: " format, ## __VA_ARGS__)
-#else
-#define DBG(format, ...)
-#endif
-
-/* postponed regions are used to temporarily store region information at boot
- * time when we cannot yet allocate the region list */
-#define MAX_POSTPONED_REGIONS 10
-
-static bool vram_initialized;
-static int postponed_cnt;
-static struct {
- unsigned long paddr;
- size_t size;
-} postponed_regions[MAX_POSTPONED_REGIONS];
-
-struct vram_alloc {
- struct list_head list;
- unsigned long paddr;
- unsigned pages;
-};
-
-struct vram_region {
- struct list_head list;
- struct list_head alloc_list;
- unsigned long paddr;
- unsigned pages;
-};
-
-static DEFINE_MUTEX(region_mutex);
-static LIST_HEAD(region_list);
-
-static struct vram_region *omap_vram_create_region(unsigned long paddr,
- unsigned pages)
-{
- struct vram_region *rm;
-
- rm = kzalloc(sizeof(*rm), GFP_KERNEL);
-
- if (rm) {
- INIT_LIST_HEAD(&rm->alloc_list);
- rm->paddr = paddr;
- rm->pages = pages;
- }
-
- return rm;
-}
-
-#if 0
-static void omap_vram_free_region(struct vram_region *vr)
-{
- list_del(&vr->list);
- kfree(vr);
-}
-#endif
-
-static struct vram_alloc *omap_vram_create_allocation(struct vram_region *vr,
- unsigned long paddr, unsigned pages)
-{
- struct vram_alloc *va;
- struct vram_alloc *new;
-
- new = kzalloc(sizeof(*va), GFP_KERNEL);
-
- if (!new)
- return NULL;
-
- new->paddr = paddr;
- new->pages = pages;
-
- list_for_each_entry(va, &vr->alloc_list, list) {
- if (va->paddr > new->paddr)
- break;
- }
-
- list_add_tail(&new->list, &va->list);
-
- return new;
-}
-
-static void omap_vram_free_allocation(struct vram_alloc *va)
-{
- list_del(&va->list);
- kfree(va);
-}
-
-int omap_vram_add_region(unsigned long paddr, size_t size)
-{
- struct vram_region *rm;
- unsigned pages;
-
- if (vram_initialized) {
- DBG("adding region paddr %08lx size %d\n",
- paddr, size);
-
- size &= PAGE_MASK;
- pages = size >> PAGE_SHIFT;
-
- rm = omap_vram_create_region(paddr, pages);
- if (rm == NULL)
- return -ENOMEM;
-
- list_add(&rm->list, &region_list);
- } else {
- if (postponed_cnt == MAX_POSTPONED_REGIONS)
- return -ENOMEM;
-
- postponed_regions[postponed_cnt].paddr = paddr;
- postponed_regions[postponed_cnt].size = size;
-
- ++postponed_cnt;
- }
- return 0;
-}
-
-int omap_vram_free(unsigned long paddr, size_t size)
-{
- struct vram_region *rm;
- struct vram_alloc *alloc;
- unsigned start, end;
-
- DBG("free mem paddr %08lx size %d\n", paddr, size);
-
- size = PAGE_ALIGN(size);
-
- mutex_lock(&region_mutex);
-
- list_for_each_entry(rm, &region_list, list) {
- list_for_each_entry(alloc, &rm->alloc_list, list) {
- start = alloc->paddr;
- end = alloc->paddr + (alloc->pages >> PAGE_SHIFT);
-
- if (start >= paddr && end < paddr + size)
- goto found;
- }
- }
-
- mutex_unlock(&region_mutex);
- return -EINVAL;
-
-found:
- omap_vram_free_allocation(alloc);
-
- mutex_unlock(&region_mutex);
- return 0;
-}
-EXPORT_SYMBOL(omap_vram_free);
-
-static int _omap_vram_reserve(unsigned long paddr, unsigned pages)
-{
- struct vram_region *rm;
- struct vram_alloc *alloc;
- size_t size;
-
- size = pages << PAGE_SHIFT;
-
- list_for_each_entry(rm, &region_list, list) {
- unsigned long start, end;
-
- DBG("checking region %lx %d\n", rm->paddr, rm->pages);
-
- start = rm->paddr;
- end = start + (rm->pages << PAGE_SHIFT) - 1;
- if (start > paddr || end < paddr + size - 1)
- continue;
-
- DBG("block ok, checking allocs\n");
-
- list_for_each_entry(alloc, &rm->alloc_list, list) {
- end = alloc->paddr - 1;
-
- if (start <= paddr && end >= paddr + size - 1)
- goto found;
-
- start = alloc->paddr + (alloc->pages << PAGE_SHIFT);
- }
-
- end = rm->paddr + (rm->pages << PAGE_SHIFT) - 1;
-
- if (!(start <= paddr && end >= paddr + size - 1))
- continue;
-found:
- DBG("found area start %lx, end %lx\n", start, end);
-
- if (omap_vram_create_allocation(rm, paddr, pages) == NULL)
- return -ENOMEM;
-
- return 0;
- }
-
- return -ENOMEM;
-}
-
-int omap_vram_reserve(unsigned long paddr, size_t size)
-{
- unsigned pages;
- int r;
-
- DBG("reserve mem paddr %08lx size %d\n", paddr, size);
-
- size = PAGE_ALIGN(size);
- pages = size >> PAGE_SHIFT;
-
- mutex_lock(&region_mutex);
-
- r = _omap_vram_reserve(paddr, pages);
-
- mutex_unlock(&region_mutex);
-
- return r;
-}
-EXPORT_SYMBOL(omap_vram_reserve);
-
-static int _omap_vram_alloc(unsigned pages, unsigned long *paddr)
-{
- struct vram_region *rm;
- struct vram_alloc *alloc;
-
- list_for_each_entry(rm, &region_list, list) {
- unsigned long start, end;
-
- DBG("checking region %lx %d\n", rm->paddr, rm->pages);
-
- start = rm->paddr;
-
- list_for_each_entry(alloc, &rm->alloc_list, list) {
- end = alloc->paddr;
-
- if (end - start >= pages << PAGE_SHIFT)
- goto found;
-
- start = alloc->paddr + (alloc->pages << PAGE_SHIFT);
- }
-
- end = rm->paddr + (rm->pages << PAGE_SHIFT);
-found:
- if (end - start < pages << PAGE_SHIFT)
- continue;
-
- DBG("found %lx, end %lx\n", start, end);
-
- alloc = omap_vram_create_allocation(rm, start, pages);
- if (alloc == NULL)
- return -ENOMEM;
-
- *paddr = start;
-
- return 0;
- }
-
- return -ENOMEM;
-}
-
-int omap_vram_alloc(size_t size, unsigned long *paddr)
-{
- unsigned pages;
- int r;
-
- BUG_ON(!size);
-
- DBG("alloc mem size %d\n", size);
-
- size = PAGE_ALIGN(size);
- pages = size >> PAGE_SHIFT;
-
- mutex_lock(&region_mutex);
-
- r = _omap_vram_alloc(pages, paddr);
-
- mutex_unlock(&region_mutex);
-
- return r;
-}
-EXPORT_SYMBOL(omap_vram_alloc);
-
-void omap_vram_get_info(unsigned long *vram,
- unsigned long *free_vram,
- unsigned long *largest_free_block)
-{
- struct vram_region *vr;
- struct vram_alloc *va;
-
- *vram = 0;
- *free_vram = 0;
- *largest_free_block = 0;
-
- mutex_lock(&region_mutex);
-
- list_for_each_entry(vr, &region_list, list) {
- unsigned free;
- unsigned long pa;
-
- pa = vr->paddr;
- *vram += vr->pages << PAGE_SHIFT;
-
- list_for_each_entry(va, &vr->alloc_list, list) {
- free = va->paddr - pa;
- *free_vram += free;
- if (free > *largest_free_block)
- *largest_free_block = free;
- pa = va->paddr + (va->pages << PAGE_SHIFT);
- }
-
- free = vr->paddr + (vr->pages << PAGE_SHIFT) - pa;
- *free_vram += free;
- if (free > *largest_free_block)
- *largest_free_block = free;
- }
-
- mutex_unlock(&region_mutex);
-}
-EXPORT_SYMBOL(omap_vram_get_info);
-
-#if defined(CONFIG_DEBUG_FS)
-static int vram_debug_show(struct seq_file *s, void *unused)
-{
- struct vram_region *vr;
- struct vram_alloc *va;
- unsigned size;
-
- mutex_lock(&region_mutex);
-
- list_for_each_entry(vr, &region_list, list) {
- size = vr->pages << PAGE_SHIFT;
- seq_printf(s, "%08lx-%08lx (%d bytes)\n",
- vr->paddr, vr->paddr + size - 1,
- size);
-
- list_for_each_entry(va, &vr->alloc_list, list) {
- size = va->pages << PAGE_SHIFT;
- seq_printf(s, " %08lx-%08lx (%d bytes)\n",
- va->paddr, va->paddr + size - 1,
- size);
- }
- }
-
- mutex_unlock(&region_mutex);
-
- return 0;
-}
-
-static int vram_debug_open(struct inode *inode, struct file *file)
-{
- return single_open(file, vram_debug_show, inode->i_private);
-}
-
-static const struct file_operations vram_debug_fops = {
- .open = vram_debug_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int __init omap_vram_create_debugfs(void)
-{
- struct dentry *d;
-
- d = debugfs_create_file("vram", S_IRUGO, NULL,
- NULL, &vram_debug_fops);
- if (IS_ERR(d))
- return PTR_ERR(d);
-
- return 0;
-}
-#endif
-
-static __init int omap_vram_init(void)
-{
- int i;
-
- vram_initialized = 1;
-
- for (i = 0; i < postponed_cnt; i++)
- omap_vram_add_region(postponed_regions[i].paddr,
- postponed_regions[i].size);
-
-#ifdef CONFIG_DEBUG_FS
- if (omap_vram_create_debugfs())
- pr_err("VRAM: Failed to create debugfs file\n");
-#endif
-
- return 0;
-}
-
-arch_initcall(omap_vram_init);
-
-/* boottime vram alloc stuff */
-
-/* set from board file */
-static u32 omap_vram_sdram_start __initdata;
-static u32 omap_vram_sdram_size __initdata;
-
-/* set from kernel cmdline */
-static u32 omap_vram_def_sdram_size __initdata;
-static u32 omap_vram_def_sdram_start __initdata;
-
-static int __init omap_vram_early_vram(char *p)
-{
- omap_vram_def_sdram_size = memparse(p, &p);
- if (*p == ',')
- omap_vram_def_sdram_start = simple_strtoul(p + 1, &p, 16);
- return 0;
-}
-early_param("vram", omap_vram_early_vram);
-
-/*
- * Called from map_io. We need to call to this early enough so that we
- * can reserve the fixed SDRAM regions before VM could get hold of them.
- */
-void __init omap_vram_reserve_sdram_memblock(void)
-{
- u32 paddr;
- u32 size = 0;
-
- /* cmdline arg overrides the board file definition */
- if (omap_vram_def_sdram_size) {
- size = omap_vram_def_sdram_size;
- paddr = omap_vram_def_sdram_start;
- }
-
- if (!size) {
- size = omap_vram_sdram_size;
- paddr = omap_vram_sdram_start;
- }
-
-#ifdef CONFIG_OMAP2_VRAM_SIZE
- if (!size) {
- size = CONFIG_OMAP2_VRAM_SIZE * 1024 * 1024;
- paddr = 0;
- }
-#endif
-
- if (!size)
- return;
-
- size = ALIGN(size, SZ_2M);
-
- if (paddr) {
- if (paddr & ~PAGE_MASK) {
- pr_err("VRAM start address 0x%08x not page aligned\n",
- paddr);
- return;
- }
-
- if (!memblock_is_region_memory(paddr, size)) {
- pr_err("Illegal SDRAM region 0x%08x..0x%08x for VRAM\n",
- paddr, paddr + size - 1);
- return;
- }
-
- if (memblock_is_region_reserved(paddr, size)) {
- pr_err("FB: failed to reserve VRAM - busy\n");
- return;
- }
-
- if (memblock_reserve(paddr, size) < 0) {
- pr_err("FB: failed to reserve VRAM - no memory\n");
- return;
- }
- } else {
- paddr = memblock_alloc(size, SZ_2M);
- }
-
- memblock_free(paddr, size);
- memblock_remove(paddr, size);
-
- omap_vram_add_region(paddr, size);
-
- pr_info("Reserving %u bytes SDRAM for VRAM\n", size);
-}
-
-void __init omap_vram_set_sdram_vram(u32 size, u32 start)
-{
- omap_vram_sdram_start = start;
- omap_vram_sdram_size = size;
-}
diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c
index 2ed7b633bbd..1a00ad241ed 100644
--- a/drivers/video/s3c-fb.c
+++ b/drivers/video/s3c-fb.c
@@ -189,7 +189,7 @@ struct s3c_fb_vsync {
/**
* struct s3c_fb - overall hardware state of the hardware
- * @slock: The spinlock protection for this data sturucture.
+ * @slock: The spinlock protection for this data structure.
* @dev: The device that we bound to, for printing, etc.
* @bus_clk: The clk (hclk) feeding our interface and possibly pixclk.
* @lcd_clk: The clk (sclk) feeding pixclk.
@@ -268,10 +268,10 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var,
case 8:
if (sfb->variant.palette[win->index] != 0) {
/* non palletised, A:1,R:2,G:3,B:2 mode */
- var->red.offset = 4;
+ var->red.offset = 5;
var->green.offset = 2;
var->blue.offset = 0;
- var->red.length = 5;
+ var->red.length = 2;
var->green.length = 3;
var->blue.length = 2;
var->transp.offset = 7;
@@ -288,6 +288,7 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var,
/* 666 with one bit alpha/transparency */
var->transp.offset = 18;
var->transp.length = 1;
+ /* drop through */
case 18:
var->bits_per_pixel = 32;
@@ -329,6 +330,7 @@ static int s3c_fb_check_var(struct fb_var_screeninfo *var,
default:
dev_err(sfb->dev, "invalid bpp\n");
+ return -EINVAL;
}
dev_dbg(sfb->dev, "%s: verified parameters\n", __func__);
@@ -1544,8 +1546,7 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
static int s3c_fb_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct s3c_fb *sfb = platform_get_drvdata(pdev);
+ struct s3c_fb *sfb = dev_get_drvdata(dev);
struct s3c_fb_win *win;
int win_no;
@@ -1572,8 +1573,7 @@ static int s3c_fb_suspend(struct device *dev)
static int s3c_fb_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct s3c_fb *sfb = platform_get_drvdata(pdev);
+ struct s3c_fb *sfb = dev_get_drvdata(dev);
struct s3c_fb_platdata *pd = sfb->pdata;
struct s3c_fb_win *win;
int win_no;
@@ -1623,7 +1623,7 @@ static int s3c_fb_resume(struct device *dev)
if (!win)
continue;
- dev_dbg(&pdev->dev, "resuming window %d\n", win_no);
+ dev_dbg(dev, "resuming window %d\n", win_no);
s3c_fb_set_par(win->fbinfo);
}
@@ -1636,8 +1636,7 @@ static int s3c_fb_resume(struct device *dev)
#ifdef CONFIG_PM_RUNTIME
static int s3c_fb_runtime_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct s3c_fb *sfb = platform_get_drvdata(pdev);
+ struct s3c_fb *sfb = dev_get_drvdata(dev);
if (!sfb->variant.has_clksel)
clk_disable_unprepare(sfb->lcd_clk);
@@ -1649,8 +1648,7 @@ static int s3c_fb_runtime_suspend(struct device *dev)
static int s3c_fb_runtime_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct s3c_fb *sfb = platform_get_drvdata(pdev);
+ struct s3c_fb *sfb = dev_get_drvdata(dev);
struct s3c_fb_platdata *pd = sfb->pdata;
clk_prepare_enable(sfb->bus_clk);
@@ -1910,7 +1908,7 @@ static struct s3c_fb_driverdata s3c_fb_data_exynos4 = {
static struct s3c_fb_driverdata s3c_fb_data_exynos5 = {
.variant = {
.nr_windows = 5,
- .vidtcon = VIDTCON0,
+ .vidtcon = FIMD_V8_VIDTCON0,
.wincon = WINCON(0),
.winmap = WINxMAP(0),
.keycon = WKEYCON,
diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index 3951fdae5f6..f4962292792 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -127,13 +127,12 @@ static void sh_mipi_shutdown(struct platform_device *pdev)
sh_mipi_dsi_enable(mipi, false);
}
-static int sh_mipi_setup(struct sh_mipi *mipi, struct sh_mipi_dsi_info *pdata)
+static int sh_mipi_setup(struct sh_mipi *mipi, const struct fb_videomode *mode)
{
void __iomem *base = mipi->base;
- struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan;
+ struct sh_mipi_dsi_info *pdata = mipi->pdev->dev.platform_data;
u32 pctype, datatype, pixfmt, linelength, vmctr2;
u32 tmp, top, bottom, delay, div;
- bool yuv;
int bpp;
/*
@@ -146,95 +145,79 @@ static int sh_mipi_setup(struct sh_mipi *mipi, struct sh_mipi_dsi_info *pdata)
pctype = 0;
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24;
pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
- linelength = ch->lcd_modes[0].xres * 3;
- yuv = false;
+ linelength = mode->xres * 3;
break;
case MIPI_RGB565:
pctype = 1;
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16;
pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
- linelength = ch->lcd_modes[0].xres * 2;
- yuv = false;
+ linelength = mode->xres * 2;
break;
case MIPI_RGB666_LP:
pctype = 2;
datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
- linelength = ch->lcd_modes[0].xres * 3;
- yuv = false;
+ linelength = mode->xres * 3;
break;
case MIPI_RGB666:
pctype = 3;
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18;
pixfmt = MIPI_DCS_PIXEL_FMT_18BIT;
- linelength = (ch->lcd_modes[0].xres * 18 + 7) / 8;
- yuv = false;
+ linelength = (mode->xres * 18 + 7) / 8;
break;
case MIPI_BGR888:
pctype = 8;
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24;
pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
- linelength = ch->lcd_modes[0].xres * 3;
- yuv = false;
+ linelength = mode->xres * 3;
break;
case MIPI_BGR565:
pctype = 9;
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16;
pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
- linelength = ch->lcd_modes[0].xres * 2;
- yuv = false;
+ linelength = mode->xres * 2;
break;
case MIPI_BGR666_LP:
pctype = 0xa;
datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
- linelength = ch->lcd_modes[0].xres * 3;
- yuv = false;
+ linelength = mode->xres * 3;
break;
case MIPI_BGR666:
pctype = 0xb;
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18;
pixfmt = MIPI_DCS_PIXEL_FMT_18BIT;
- linelength = (ch->lcd_modes[0].xres * 18 + 7) / 8;
- yuv = false;
+ linelength = (mode->xres * 18 + 7) / 8;
break;
case MIPI_YUYV:
pctype = 4;
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16;
pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
- linelength = ch->lcd_modes[0].xres * 2;
- yuv = true;
+ linelength = mode->xres * 2;
break;
case MIPI_UYVY:
pctype = 5;
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16;
pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
- linelength = ch->lcd_modes[0].xres * 2;
- yuv = true;
+ linelength = mode->xres * 2;
break;
case MIPI_YUV420_L:
pctype = 6;
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12;
pixfmt = MIPI_DCS_PIXEL_FMT_12BIT;
- linelength = (ch->lcd_modes[0].xres * 12 + 7) / 8;
- yuv = true;
+ linelength = (mode->xres * 12 + 7) / 8;
break;
case MIPI_YUV420:
pctype = 7;
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12;
pixfmt = MIPI_DCS_PIXEL_FMT_12BIT;
/* Length of U/V line */
- linelength = (ch->lcd_modes[0].xres + 1) / 2;
- yuv = true;
+ linelength = (mode->xres + 1) / 2;
break;
default:
return -EINVAL;
}
- if ((yuv && ch->interface_type != YUV422) ||
- (!yuv && ch->interface_type != RGB24))
- return -EINVAL;
-
if (!pdata->lane)
return -EINVAL;
@@ -293,7 +276,7 @@ static int sh_mipi_setup(struct sh_mipi *mipi, struct sh_mipi_dsi_info *pdata)
*/
iowrite32(0x00000006, mipi->linkbase + DTCTR);
/* VSYNC width = 2 (<< 17) */
- iowrite32((ch->lcd_modes[0].vsync_len << pdata->vsynw_offset) |
+ iowrite32((mode->vsync_len << pdata->vsynw_offset) |
(pdata->clksrc << 16) | (pctype << 12) | datatype,
mipi->linkbase + VMCTR1);
@@ -327,7 +310,7 @@ static int sh_mipi_setup(struct sh_mipi *mipi, struct sh_mipi_dsi_info *pdata)
top = linelength << 16; /* RGBLEN */
bottom = 0x00000001;
if (pdata->flags & SH_MIPI_DSI_HSABM) /* HSALEN */
- bottom = (pdata->lane * ch->lcd_modes[0].hsync_len) - 10;
+ bottom = (pdata->lane * mode->hsync_len) - 10;
iowrite32(top | bottom , mipi->linkbase + VMLEN1);
/*
@@ -347,18 +330,18 @@ static int sh_mipi_setup(struct sh_mipi *mipi, struct sh_mipi_dsi_info *pdata)
div = 2;
if (pdata->flags & SH_MIPI_DSI_HFPBM) { /* HBPLEN */
- top = ch->lcd_modes[0].hsync_len + ch->lcd_modes[0].left_margin;
+ top = mode->hsync_len + mode->left_margin;
top = ((pdata->lane * top / div) - 10) << 16;
}
if (pdata->flags & SH_MIPI_DSI_HBPBM) { /* HFPLEN */
- bottom = ch->lcd_modes[0].right_margin;
+ bottom = mode->right_margin;
bottom = (pdata->lane * bottom / div) - 12;
}
- bpp = linelength / ch->lcd_modes[0].xres; /* byte / pixel */
+ bpp = linelength / mode->xres; /* byte / pixel */
if ((pdata->lane / div) > bpp) {
- tmp = ch->lcd_modes[0].xres / bpp; /* output cycle */
- tmp = ch->lcd_modes[0].xres - tmp; /* (input - output) cycle */
+ tmp = mode->xres / bpp; /* output cycle */
+ tmp = mode->xres - tmp; /* (input - output) cycle */
delay = (pdata->lane * tmp);
}
@@ -369,7 +352,7 @@ static int sh_mipi_setup(struct sh_mipi *mipi, struct sh_mipi_dsi_info *pdata)
/* setup LCD panel */
/* cf. drivers/video/omap/lcd_mipid.c */
- sh_mipi_dcs(ch->chan, MIPI_DCS_EXIT_SLEEP_MODE);
+ sh_mipi_dcs(pdata->channel, MIPI_DCS_EXIT_SLEEP_MODE);
msleep(120);
/*
* [7] - Page Address Mode
@@ -381,11 +364,11 @@ static int sh_mipi_setup(struct sh_mipi *mipi, struct sh_mipi_dsi_info *pdata)
* [1] - Flip Horizontal
* [0] - Flip Vertical
*/
- sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
+ sh_mipi_dcs_param(pdata->channel, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
/* cf. set_data_lines() */
- sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_PIXEL_FORMAT,
+ sh_mipi_dcs_param(pdata->channel, MIPI_DCS_SET_PIXEL_FORMAT,
pixfmt << 4);
- sh_mipi_dcs(ch->chan, MIPI_DCS_SET_DISPLAY_ON);
+ sh_mipi_dcs(pdata->channel, MIPI_DCS_SET_DISPLAY_ON);
/* Enable timeout counters */
iowrite32(0x00000f00, base + DSICTRL);
@@ -405,7 +388,7 @@ static int mipi_display_on(struct sh_mobile_lcdc_entity *entity)
if (ret < 0)
goto mipi_display_on_fail1;
- ret = sh_mipi_setup(mipi, pdata);
+ ret = sh_mipi_setup(mipi, &entity->def_mode);
if (ret < 0)
goto mipi_display_on_fail2;
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 699487c287b..e78fe4bc152 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -438,7 +438,7 @@ static unsigned long lcdc_sys_read_data(void *handle)
return lcdc_read(ch->lcdc, _LDDRDR) & LDDRDR_DRD_MASK;
}
-struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
+static struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
lcdc_sys_write_index,
lcdc_sys_write_data,
lcdc_sys_read_data,
@@ -586,8 +586,8 @@ static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch,
* Just turn on, if we run a resume here, the
* logo disappears.
*/
- info->var.width = monspec->max_x * 10;
- info->var.height = monspec->max_y * 10;
+ info->var.width = ch->display.width;
+ info->var.height = ch->display.height;
sh_mobile_lcdc_display_on(ch);
} else {
/* New monitor or have to wake up */
@@ -1614,6 +1614,15 @@ static int sh_mobile_lcdc_overlay_blank(int blank, struct fb_info *info)
return 1;
}
+static int
+sh_mobile_lcdc_overlay_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ return dma_mmap_coherent(ovl->channel->lcdc->dev, vma, ovl->fb_mem,
+ ovl->dma_handle, ovl->fb_size);
+}
+
static struct fb_ops sh_mobile_lcdc_overlay_ops = {
.owner = THIS_MODULE,
.fb_read = fb_sys_read,
@@ -1626,6 +1635,7 @@ static struct fb_ops sh_mobile_lcdc_overlay_ops = {
.fb_ioctl = sh_mobile_lcdc_overlay_ioctl,
.fb_check_var = sh_mobile_lcdc_overlay_check_var,
.fb_set_par = sh_mobile_lcdc_overlay_set_par,
+ .fb_mmap = sh_mobile_lcdc_overlay_mmap,
};
static void
@@ -2093,6 +2103,15 @@ static int sh_mobile_lcdc_blank(int blank, struct fb_info *info)
return 0;
}
+static int
+sh_mobile_lcdc_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ struct sh_mobile_lcdc_chan *ch = info->par;
+
+ return dma_mmap_coherent(ch->lcdc->dev, vma, ch->fb_mem,
+ ch->dma_handle, ch->fb_size);
+}
+
static struct fb_ops sh_mobile_lcdc_ops = {
.owner = THIS_MODULE,
.fb_setcolreg = sh_mobile_lcdc_setcolreg,
@@ -2108,6 +2127,7 @@ static struct fb_ops sh_mobile_lcdc_ops = {
.fb_release = sh_mobile_lcdc_release,
.fb_check_var = sh_mobile_lcdc_check_var,
.fb_set_par = sh_mobile_lcdc_set_par,
+ .fb_mmap = sh_mobile_lcdc_mmap,
};
static void
@@ -2167,7 +2187,7 @@ sh_mobile_lcdc_channel_fb_cleanup(struct sh_mobile_lcdc_chan *ch)
static int __devinit
sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch,
- const struct fb_videomode *mode,
+ const struct fb_videomode *modes,
unsigned int num_modes)
{
struct sh_mobile_lcdc_priv *priv = ch->lcdc;
@@ -2193,7 +2213,7 @@ sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch,
info->pseudo_palette = &ch->pseudo_palette;
info->par = ch;
- fb_videomode_to_modelist(mode, num_modes, &info->modelist);
+ fb_videomode_to_modelist(modes, num_modes, &info->modelist);
ret = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0);
if (ret < 0) {
@@ -2227,9 +2247,9 @@ sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch,
* default.
*/
var = &info->var;
- fb_videomode_to_var(var, mode);
- var->width = ch->cfg->panel_cfg.width;
- var->height = ch->cfg->panel_cfg.height;
+ fb_videomode_to_var(var, modes);
+ var->width = ch->display.width;
+ var->height = ch->display.height;
var->xres_virtual = ch->xres_virtual;
var->yres_virtual = ch->yres_virtual;
var->activate = FB_ACTIVATE_NOW;
@@ -2262,6 +2282,7 @@ static int sh_mobile_lcdc_update_bl(struct backlight_device *bdev)
bdev->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
brightness = 0;
+ ch->bl_brightness = brightness;
return ch->cfg->bl_info.set_brightness(brightness);
}
@@ -2269,7 +2290,7 @@ static int sh_mobile_lcdc_get_brightness(struct backlight_device *bdev)
{
struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev);
- return ch->cfg->bl_info.get_brightness();
+ return ch->bl_brightness;
}
static int sh_mobile_lcdc_check_fb(struct backlight_device *bdev,
@@ -2516,10 +2537,10 @@ static int __devinit sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *
}
static int __devinit
-sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_priv *priv,
- struct sh_mobile_lcdc_overlay *ovl)
+sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_overlay *ovl)
{
const struct sh_mobile_lcdc_format_info *format;
+ struct device *dev = ovl->channel->lcdc->dev;
int ret;
if (ovl->cfg->fourcc == 0)
@@ -2528,7 +2549,7 @@ sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_priv *priv,
/* Validate the format. */
format = sh_mobile_format_info(ovl->cfg->fourcc);
if (format == NULL) {
- dev_err(priv->dev, "Invalid FOURCC %08x\n", ovl->cfg->fourcc);
+ dev_err(dev, "Invalid FOURCC %08x\n", ovl->cfg->fourcc);
return -EINVAL;
}
@@ -2556,10 +2577,10 @@ sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_priv *priv,
/* Allocate frame buffer memory. */
ovl->fb_size = ovl->cfg->max_xres * ovl->cfg->max_yres
* format->bpp / 8 * 2;
- ovl->fb_mem = dma_alloc_coherent(priv->dev, ovl->fb_size,
- &ovl->dma_handle, GFP_KERNEL);
+ ovl->fb_mem = dma_alloc_coherent(dev, ovl->fb_size, &ovl->dma_handle,
+ GFP_KERNEL);
if (!ovl->fb_mem) {
- dev_err(priv->dev, "unable to allocate buffer\n");
+ dev_err(dev, "unable to allocate buffer\n");
return -ENOMEM;
}
@@ -2571,11 +2592,11 @@ sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_priv *priv,
}
static int __devinit
-sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
- struct sh_mobile_lcdc_chan *ch)
+sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch)
{
const struct sh_mobile_lcdc_format_info *format;
const struct sh_mobile_lcdc_chan_cfg *cfg = ch->cfg;
+ struct device *dev = ch->lcdc->dev;
const struct fb_videomode *max_mode;
const struct fb_videomode *mode;
unsigned int num_modes;
@@ -2588,7 +2609,7 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
/* Validate the format. */
format = sh_mobile_format_info(cfg->fourcc);
if (format == NULL) {
- dev_err(priv->dev, "Invalid FOURCC %08x.\n", cfg->fourcc);
+ dev_err(dev, "Invalid FOURCC %08x.\n", cfg->fourcc);
return -EINVAL;
}
@@ -2604,7 +2625,7 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
/* NV12/NV21 buffers must have even number of lines */
if ((cfg->fourcc == V4L2_PIX_FMT_NV12 ||
cfg->fourcc == V4L2_PIX_FMT_NV21) && (mode->yres & 0x1)) {
- dev_err(priv->dev, "yres must be multiple of 2 for "
+ dev_err(dev, "yres must be multiple of 2 for "
"YCbCr420 mode.\n");
return -EINVAL;
}
@@ -2618,7 +2639,7 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
if (!max_size)
max_size = MAX_XRES * MAX_YRES;
else
- dev_dbg(priv->dev, "Found largest videomode %ux%u\n",
+ dev_dbg(dev, "Found largest videomode %ux%u\n",
max_mode->xres, max_mode->yres);
if (cfg->lcd_modes == NULL) {
@@ -2652,10 +2673,10 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
/* Allocate frame buffer memory. */
ch->fb_size = max_size * format->bpp / 8 * 2;
- ch->fb_mem = dma_alloc_coherent(priv->dev, ch->fb_size, &ch->dma_handle,
+ ch->fb_mem = dma_alloc_coherent(dev, ch->fb_size, &ch->dma_handle,
GFP_KERNEL);
if (ch->fb_mem == NULL) {
- dev_err(priv->dev, "unable to allocate buffer\n");
+ dev_err(dev, "unable to allocate buffer\n");
return -ENOMEM;
}
@@ -2663,8 +2684,7 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
if (cfg->tx_dev) {
if (!cfg->tx_dev->dev.driver ||
!try_module_get(cfg->tx_dev->dev.driver->owner)) {
- dev_warn(priv->dev,
- "unable to get transmitter device\n");
+ dev_warn(dev, "unable to get transmitter device\n");
return -EINVAL;
}
ch->tx_dev = platform_get_drvdata(cfg->tx_dev);
@@ -2772,9 +2792,9 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
for (i = 0; i < num_channels; i++) {
- struct sh_mobile_lcdc_chan *ch = priv->ch + i;
+ struct sh_mobile_lcdc_chan *ch = &priv->ch[i];
- error = sh_mobile_lcdc_channel_init(priv, ch);
+ error = sh_mobile_lcdc_channel_init(ch);
if (error)
goto err1;
}
@@ -2785,7 +2805,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
ovl->cfg = &pdata->overlays[i];
ovl->channel = &priv->ch[0];
- error = sh_mobile_lcdc_overlay_init(priv, ovl);
+ error = sh_mobile_lcdc_overlay_init(ovl);
if (error)
goto err1;
}
diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h
index 0f92f6544b9..f839adef1d9 100644
--- a/drivers/video/sh_mobile_lcdcfb.h
+++ b/drivers/video/sh_mobile_lcdcfb.h
@@ -94,6 +94,7 @@ struct sh_mobile_lcdc_chan {
/* Backlight */
struct backlight_device *bl;
+ unsigned int bl_brightness;
/* FB */
struct fb_info *info;
diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c
new file mode 100644
index 00000000000..6101f5c2f62
--- /dev/null
+++ b/drivers/video/ssd1307fb.c
@@ -0,0 +1,396 @@
+/*
+ * Driver for the Solomon SSD1307 OLED controler
+ *
+ * Copyright 2012 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include <linux/uaccess.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pwm.h>
+#include <linux/delay.h>
+
+#define SSD1307FB_WIDTH 96
+#define SSD1307FB_HEIGHT 16
+
+#define SSD1307FB_DATA 0x40
+#define SSD1307FB_COMMAND 0x80
+
+#define SSD1307FB_CONTRAST 0x81
+#define SSD1307FB_SEG_REMAP_ON 0xa1
+#define SSD1307FB_DISPLAY_OFF 0xae
+#define SSD1307FB_DISPLAY_ON 0xaf
+#define SSD1307FB_START_PAGE_ADDRESS 0xb0
+
+struct ssd1307fb_par {
+ struct i2c_client *client;
+ struct fb_info *info;
+ struct pwm_device *pwm;
+ u32 pwm_period;
+ int reset;
+};
+
+static struct fb_fix_screeninfo ssd1307fb_fix __devinitdata = {
+ .id = "Solomon SSD1307",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_MONO10,
+ .xpanstep = 0,
+ .ypanstep = 0,
+ .ywrapstep = 0,
+ .line_length = SSD1307FB_WIDTH / 8,
+ .accel = FB_ACCEL_NONE,
+};
+
+static struct fb_var_screeninfo ssd1307fb_var __devinitdata = {
+ .xres = SSD1307FB_WIDTH,
+ .yres = SSD1307FB_HEIGHT,
+ .xres_virtual = SSD1307FB_WIDTH,
+ .yres_virtual = SSD1307FB_HEIGHT,
+ .bits_per_pixel = 1,
+};
+
+static int ssd1307fb_write_array(struct i2c_client *client, u8 type, u8 *cmd, u32 len)
+{
+ u8 *buf;
+ int ret = 0;
+
+ buf = kzalloc(len + 1, GFP_KERNEL);
+ if (!buf) {
+ dev_err(&client->dev, "Couldn't allocate sending buffer.\n");
+ return -ENOMEM;
+ }
+
+ buf[0] = type;
+ memcpy(buf + 1, cmd, len);
+
+ ret = i2c_master_send(client, buf, len + 1);
+ if (ret != len + 1) {
+ dev_err(&client->dev, "Couldn't send I2C command.\n");
+ goto error;
+ }
+
+error:
+ kfree(buf);
+ return ret;
+}
+
+static inline int ssd1307fb_write_cmd_array(struct i2c_client *client, u8 *cmd, u32 len)
+{
+ return ssd1307fb_write_array(client, SSD1307FB_COMMAND, cmd, len);
+}
+
+static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd)
+{
+ return ssd1307fb_write_cmd_array(client, &cmd, 1);
+}
+
+static inline int ssd1307fb_write_data_array(struct i2c_client *client, u8 *cmd, u32 len)
+{
+ return ssd1307fb_write_array(client, SSD1307FB_DATA, cmd, len);
+}
+
+static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data)
+{
+ return ssd1307fb_write_data_array(client, &data, 1);
+}
+
+static void ssd1307fb_update_display(struct ssd1307fb_par *par)
+{
+ u8 *vmem = par->info->screen_base;
+ int i, j, k;
+
+ /*
+ * The screen is divided in pages, each having a height of 8
+ * pixels, and the width of the screen. When sending a byte of
+ * data to the controller, it gives the 8 bits for the current
+ * column. I.e, the first byte are the 8 bits of the first
+ * column, then the 8 bits for the second column, etc.
+ *
+ *
+ * Representation of the screen, assuming it is 5 bits
+ * wide. Each letter-number combination is a bit that controls
+ * one pixel.
+ *
+ * A0 A1 A2 A3 A4
+ * B0 B1 B2 B3 B4
+ * C0 C1 C2 C3 C4
+ * D0 D1 D2 D3 D4
+ * E0 E1 E2 E3 E4
+ * F0 F1 F2 F3 F4
+ * G0 G1 G2 G3 G4
+ * H0 H1 H2 H3 H4
+ *
+ * If you want to update this screen, you need to send 5 bytes:
+ * (1) A0 B0 C0 D0 E0 F0 G0 H0
+ * (2) A1 B1 C1 D1 E1 F1 G1 H1
+ * (3) A2 B2 C2 D2 E2 F2 G2 H2
+ * (4) A3 B3 C3 D3 E3 F3 G3 H3
+ * (5) A4 B4 C4 D4 E4 F4 G4 H4
+ */
+
+ for (i = 0; i < (SSD1307FB_HEIGHT / 8); i++) {
+ ssd1307fb_write_cmd(par->client, SSD1307FB_START_PAGE_ADDRESS + (i + 1));
+ ssd1307fb_write_cmd(par->client, 0x00);
+ ssd1307fb_write_cmd(par->client, 0x10);
+
+ for (j = 0; j < SSD1307FB_WIDTH; j++) {
+ u8 buf = 0;
+ for (k = 0; k < 8; k++) {
+ u32 page_length = SSD1307FB_WIDTH * i;
+ u32 index = page_length + (SSD1307FB_WIDTH * k + j) / 8;
+ u8 byte = *(vmem + index);
+ u8 bit = byte & (1 << (7 - (j % 8)));
+ bit = bit >> (7 - (j % 8));
+ buf |= bit << k;
+ }
+ ssd1307fb_write_data(par->client, buf);
+ }
+ }
+}
+
+
+static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct ssd1307fb_par *par = info->par;
+ unsigned long total_size;
+ unsigned long p = *ppos;
+ u8 __iomem *dst;
+
+ total_size = info->fix.smem_len;
+
+ if (p > total_size)
+ return -EINVAL;
+
+ if (count + p > total_size)
+ count = total_size - p;
+
+ if (!count)
+ return -EINVAL;
+
+ dst = (void __force *) (info->screen_base + p);
+
+ if (copy_from_user(dst, buf, count))
+ return -EFAULT;
+
+ ssd1307fb_update_display(par);
+
+ *ppos += count;
+
+ return count;
+}
+
+static void ssd1307fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+ struct ssd1307fb_par *par = info->par;
+ sys_fillrect(info, rect);
+ ssd1307fb_update_display(par);
+}
+
+static void ssd1307fb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
+{
+ struct ssd1307fb_par *par = info->par;
+ sys_copyarea(info, area);
+ ssd1307fb_update_display(par);
+}
+
+static void ssd1307fb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+ struct ssd1307fb_par *par = info->par;
+ sys_imageblit(info, image);
+ ssd1307fb_update_display(par);
+}
+
+static struct fb_ops ssd1307fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_read = fb_sys_read,
+ .fb_write = ssd1307fb_write,
+ .fb_fillrect = ssd1307fb_fillrect,
+ .fb_copyarea = ssd1307fb_copyarea,
+ .fb_imageblit = ssd1307fb_imageblit,
+};
+
+static void ssd1307fb_deferred_io(struct fb_info *info,
+ struct list_head *pagelist)
+{
+ ssd1307fb_update_display(info->par);
+}
+
+static struct fb_deferred_io ssd1307fb_defio = {
+ .delay = HZ,
+ .deferred_io = ssd1307fb_deferred_io,
+};
+
+static int __devinit ssd1307fb_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct fb_info *info;
+ u32 vmem_size = SSD1307FB_WIDTH * SSD1307FB_HEIGHT / 8;
+ struct ssd1307fb_par *par;
+ u8 *vmem;
+ int ret;
+
+ if (!client->dev.of_node) {
+ dev_err(&client->dev, "No device tree data found!\n");
+ return -EINVAL;
+ }
+
+ info = framebuffer_alloc(sizeof(struct ssd1307fb_par), &client->dev);
+ if (!info) {
+ dev_err(&client->dev, "Couldn't allocate framebuffer.\n");
+ return -ENOMEM;
+ }
+
+ vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
+ if (!vmem) {
+ dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
+ ret = -ENOMEM;
+ goto fb_alloc_error;
+ }
+
+ info->fbops = &ssd1307fb_ops;
+ info->fix = ssd1307fb_fix;
+ info->fbdefio = &ssd1307fb_defio;
+
+ info->var = ssd1307fb_var;
+ info->var.red.length = 1;
+ info->var.red.offset = 0;
+ info->var.green.length = 1;
+ info->var.green.offset = 0;
+ info->var.blue.length = 1;
+ info->var.blue.offset = 0;
+
+ info->screen_base = (u8 __force __iomem *)vmem;
+ info->fix.smem_start = (unsigned long)vmem;
+ info->fix.smem_len = vmem_size;
+
+ fb_deferred_io_init(info);
+
+ par = info->par;
+ par->info = info;
+ par->client = client;
+
+ par->reset = of_get_named_gpio(client->dev.of_node,
+ "reset-gpios", 0);
+ if (!gpio_is_valid(par->reset)) {
+ ret = -EINVAL;
+ goto reset_oled_error;
+ }
+
+ ret = devm_gpio_request_one(&client->dev, par->reset,
+ GPIOF_OUT_INIT_HIGH,
+ "oled-reset");
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to request gpio %d: %d\n",
+ par->reset, ret);
+ goto reset_oled_error;
+ }
+
+ par->pwm = pwm_get(&client->dev, NULL);
+ if (IS_ERR(par->pwm)) {
+ dev_err(&client->dev, "Could not get PWM from device tree!\n");
+ ret = PTR_ERR(par->pwm);
+ goto pwm_error;
+ }
+
+ par->pwm_period = pwm_get_period(par->pwm);
+
+ dev_dbg(&client->dev, "Using PWM%d with a %dns period.\n", par->pwm->pwm, par->pwm_period);
+
+ ret = register_framebuffer(info);
+ if (ret) {
+ dev_err(&client->dev, "Couldn't register the framebuffer\n");
+ goto fbreg_error;
+ }
+
+ i2c_set_clientdata(client, info);
+
+ /* Reset the screen */
+ gpio_set_value(par->reset, 0);
+ udelay(4);
+ gpio_set_value(par->reset, 1);
+ udelay(4);
+
+ /* Enable the PWM */
+ pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
+ pwm_enable(par->pwm);
+
+ /* Map column 127 of the OLED to segment 0 */
+ ret = ssd1307fb_write_cmd(client, SSD1307FB_SEG_REMAP_ON);
+ if (ret < 0) {
+ dev_err(&client->dev, "Couldn't remap the screen.\n");
+ goto remap_error;
+ }
+
+ /* Turn on the display */
+ ret = ssd1307fb_write_cmd(client, SSD1307FB_DISPLAY_ON);
+ if (ret < 0) {
+ dev_err(&client->dev, "Couldn't turn the display on.\n");
+ goto remap_error;
+ }
+
+ dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
+
+ return 0;
+
+remap_error:
+ unregister_framebuffer(info);
+ pwm_disable(par->pwm);
+fbreg_error:
+ pwm_put(par->pwm);
+pwm_error:
+reset_oled_error:
+ fb_deferred_io_cleanup(info);
+fb_alloc_error:
+ framebuffer_release(info);
+ return ret;
+}
+
+static int __devexit ssd1307fb_remove(struct i2c_client *client)
+{
+ struct fb_info *info = i2c_get_clientdata(client);
+ struct ssd1307fb_par *par = info->par;
+
+ unregister_framebuffer(info);
+ pwm_disable(par->pwm);
+ pwm_put(par->pwm);
+ fb_deferred_io_cleanup(info);
+ framebuffer_release(info);
+
+ return 0;
+}
+
+static const struct i2c_device_id ssd1307fb_i2c_id[] = {
+ { "ssd1307fb", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id);
+
+static const struct of_device_id ssd1307fb_of_match[] = {
+ { .compatible = "solomon,ssd1307fb-i2c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
+
+static struct i2c_driver ssd1307fb_driver = {
+ .probe = ssd1307fb_probe,
+ .remove = __devexit_p(ssd1307fb_remove),
+ .id_table = ssd1307fb_i2c_id,
+ .driver = {
+ .name = "ssd1307fb",
+ .of_match_table = of_match_ptr(ssd1307fb_of_match),
+ .owner = THIS_MODULE,
+ },
+};
+
+module_i2c_driver(ssd1307fb_driver);
+
+MODULE_DESCRIPTION("FB driver for the Solomon SSD1307 OLED controler");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig
index 2dcdbc9364d..99ebdde590f 100644
--- a/drivers/virt/Kconfig
+++ b/drivers/virt/Kconfig
@@ -15,6 +15,7 @@ if VIRT_DRIVERS
config FSL_HV_MANAGER
tristate "Freescale hypervisor management driver"
depends on FSL_SOC
+ select EPAPR_PARAVIRT
help
The Freescale hypervisor management driver provides several services
to drivers and applications related to the Freescale hypervisor:
diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c
index 5b06d31ab6a..c0bc92d8e43 100644
--- a/drivers/watchdog/booke_wdt.c
+++ b/drivers/watchdog/booke_wdt.c
@@ -212,7 +212,7 @@ static long booke_wdt_ioctl(struct file *file,
return 0;
}
-/* wdt_is_active stores wether or not the /dev/watchdog device is opened */
+/* wdt_is_active stores whether or not the /dev/watchdog device is opened */
static unsigned long wdt_is_active;
static int booke_wdt_open(struct inode *inode, struct file *file)
diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig
index 126d8ce591c..cabfa97f467 100644
--- a/drivers/xen/Kconfig
+++ b/drivers/xen/Kconfig
@@ -206,4 +206,7 @@ config XEN_MCE_LOG
Allow kernel fetching MCE error from Xen platform and
converting it into Linux mcelog format for mcelog tools
+config XEN_HAVE_PVMMU
+ bool
+
endmenu
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index 74354708c6c..fb213cf81a7 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -1,9 +1,9 @@
ifneq ($(CONFIG_ARM),y)
-obj-y += manage.o balloon.o
+obj-y += manage.o
obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o
endif
obj-$(CONFIG_X86) += fallback.o
-obj-y += grant-table.o features.o events.o
+obj-y += grant-table.o features.o events.o balloon.o
obj-y += xenbus/
nostackp := $(call cc-option, -fno-stack-protector)
@@ -11,7 +11,8 @@ CFLAGS_features.o := $(nostackp)
dom0-$(CONFIG_PCI) += pci.o
dom0-$(CONFIG_USB_SUPPORT) += dbgp.o
-dom0-$(CONFIG_ACPI) += acpi.o
+dom0-$(CONFIG_ACPI) += acpi.o $(xen-pad-y)
+xen-pad-$(CONFIG_X86) += xen-acpi-pad.o
dom0-$(CONFIG_X86) += pcpu.o
obj-$(CONFIG_XEN_DOM0) += $(dom0-y)
obj-$(CONFIG_BLOCK) += biomerge.o
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c
index d6886d90ccf..a56776dbe09 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -359,6 +359,7 @@ static enum bp_state increase_reservation(unsigned long nr_pages)
set_phys_to_machine(pfn, frame_list[i]);
+#ifdef CONFIG_XEN_HAVE_PVMMU
/* Link back into the page tables if not highmem. */
if (xen_pv_domain() && !PageHighMem(page)) {
int ret;
@@ -368,6 +369,7 @@ static enum bp_state increase_reservation(unsigned long nr_pages)
0);
BUG_ON(ret);
}
+#endif
/* Relinquish the page back to the allocator. */
ClearPageReserved(page);
@@ -416,13 +418,14 @@ static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp)
scrub_page(page);
+#ifdef CONFIG_XEN_HAVE_PVMMU
if (xen_pv_domain() && !PageHighMem(page)) {
ret = HYPERVISOR_update_va_mapping(
(unsigned long)__va(pfn << PAGE_SHIFT),
__pte_ma(0), 0);
BUG_ON(ret);
}
-
+#endif
}
/* Ensure that ballooned highmem pages don't have kmaps. */
diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c
index 71f5c459b08..0bbbccbb1f1 100644
--- a/drivers/xen/privcmd.c
+++ b/drivers/xen/privcmd.c
@@ -33,11 +33,14 @@
#include <xen/features.h>
#include <xen/page.h>
#include <xen/xen-ops.h>
+#include <xen/balloon.h>
#include "privcmd.h"
MODULE_LICENSE("GPL");
+#define PRIV_VMA_LOCKED ((void *)1)
+
#ifndef HAVE_ARCH_PRIVCMD_MMAP
static int privcmd_enforce_singleshot_mapping(struct vm_area_struct *vma);
#endif
@@ -178,7 +181,7 @@ static int mmap_mfn_range(void *data, void *state)
msg->va & PAGE_MASK,
msg->mfn, msg->npages,
vma->vm_page_prot,
- st->domain);
+ st->domain, NULL);
if (rc < 0)
return rc;
@@ -199,6 +202,10 @@ static long privcmd_ioctl_mmap(void __user *udata)
if (!xen_initial_domain())
return -EPERM;
+ /* We only support privcmd_ioctl_mmap_batch for auto translated. */
+ if (xen_feature(XENFEAT_auto_translated_physmap))
+ return -ENOSYS;
+
if (copy_from_user(&mmapcmd, udata, sizeof(mmapcmd)))
return -EFAULT;
@@ -246,6 +253,7 @@ struct mmap_batch_state {
domid_t domain;
unsigned long va;
struct vm_area_struct *vma;
+ int index;
/* A tristate:
* 0 for no errors
* 1 if at least one error has happened (and no
@@ -260,14 +268,24 @@ struct mmap_batch_state {
xen_pfn_t __user *user_mfn;
};
+/* auto translated dom0 note: if domU being created is PV, then mfn is
+ * mfn(addr on bus). If it's auto xlated, then mfn is pfn (input to HAP).
+ */
static int mmap_batch_fn(void *data, void *state)
{
xen_pfn_t *mfnp = data;
struct mmap_batch_state *st = state;
+ struct vm_area_struct *vma = st->vma;
+ struct page **pages = vma->vm_private_data;
+ struct page *cur_page = NULL;
int ret;
+ if (xen_feature(XENFEAT_auto_translated_physmap))
+ cur_page = pages[st->index++];
+
ret = xen_remap_domain_mfn_range(st->vma, st->va & PAGE_MASK, *mfnp, 1,
- st->vma->vm_page_prot, st->domain);
+ st->vma->vm_page_prot, st->domain,
+ &cur_page);
/* Store error code for second pass. */
*(st->err++) = ret;
@@ -303,6 +321,32 @@ static int mmap_return_errors_v1(void *data, void *state)
return __put_user(*mfnp, st->user_mfn++);
}
+/* Allocate pfns that are then mapped with gmfns from foreign domid. Update
+ * the vma with the page info to use later.
+ * Returns: 0 if success, otherwise -errno
+ */
+static int alloc_empty_pages(struct vm_area_struct *vma, int numpgs)
+{
+ int rc;
+ struct page **pages;
+
+ pages = kcalloc(numpgs, sizeof(pages[0]), GFP_KERNEL);
+ if (pages == NULL)
+ return -ENOMEM;
+
+ rc = alloc_xenballooned_pages(numpgs, pages, 0);
+ if (rc != 0) {
+ pr_warn("%s Could not alloc %d pfns rc:%d\n", __func__,
+ numpgs, rc);
+ kfree(pages);
+ return -ENOMEM;
+ }
+ BUG_ON(vma->vm_private_data != PRIV_VMA_LOCKED);
+ vma->vm_private_data = pages;
+
+ return 0;
+}
+
static struct vm_operations_struct privcmd_vm_ops;
static long privcmd_ioctl_mmap_batch(void __user *udata, int version)
@@ -370,10 +414,18 @@ static long privcmd_ioctl_mmap_batch(void __user *udata, int version)
ret = -EINVAL;
goto out;
}
+ if (xen_feature(XENFEAT_auto_translated_physmap)) {
+ ret = alloc_empty_pages(vma, m.num);
+ if (ret < 0) {
+ up_write(&mm->mmap_sem);
+ goto out;
+ }
+ }
state.domain = m.dom;
state.vma = vma;
state.va = m.addr;
+ state.index = 0;
state.global_error = 0;
state.err = err_array;
@@ -442,6 +494,19 @@ static long privcmd_ioctl(struct file *file,
return ret;
}
+static void privcmd_close(struct vm_area_struct *vma)
+{
+ struct page **pages = vma->vm_private_data;
+ int numpgs = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+
+ if (!xen_feature(XENFEAT_auto_translated_physmap || !numpgs || !pages))
+ return;
+
+ xen_unmap_domain_mfn_range(vma, numpgs, pages);
+ free_xenballooned_pages(numpgs, pages);
+ kfree(pages);
+}
+
static int privcmd_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
printk(KERN_DEBUG "privcmd_fault: vma=%p %lx-%lx, pgoff=%lx, uv=%p\n",
@@ -452,6 +517,7 @@ static int privcmd_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
}
static struct vm_operations_struct privcmd_vm_ops = {
+ .close = privcmd_close,
.fault = privcmd_fault
};
@@ -469,7 +535,7 @@ static int privcmd_mmap(struct file *file, struct vm_area_struct *vma)
static int privcmd_enforce_singleshot_mapping(struct vm_area_struct *vma)
{
- return (xchg(&vma->vm_private_data, (void *)1) == NULL);
+ return !cmpxchg(&vma->vm_private_data, NULL, PRIV_VMA_LOCKED);
}
const struct file_operations xen_privcmd_fops = {
diff --git a/drivers/xen/xen-acpi-pad.c b/drivers/xen/xen-acpi-pad.c
new file mode 100644
index 00000000000..da39191e727
--- /dev/null
+++ b/drivers/xen/xen-acpi-pad.c
@@ -0,0 +1,182 @@
+/*
+ * xen-acpi-pad.c - Xen pad interface
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ * Author: Liu, Jinsong <jinsong.liu@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <asm/xen/hypercall.h>
+#include <xen/interface/version.h>
+#include <xen/xen-ops.h>
+
+#define ACPI_PROCESSOR_AGGREGATOR_CLASS "acpi_pad"
+#define ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME "Processor Aggregator"
+#define ACPI_PROCESSOR_AGGREGATOR_NOTIFY 0x80
+static DEFINE_MUTEX(xen_cpu_lock);
+
+static int xen_acpi_pad_idle_cpus(unsigned int idle_nums)
+{
+ struct xen_platform_op op;
+
+ op.cmd = XENPF_core_parking;
+ op.u.core_parking.type = XEN_CORE_PARKING_SET;
+ op.u.core_parking.idle_nums = idle_nums;
+
+ return HYPERVISOR_dom0_op(&op);
+}
+
+static int xen_acpi_pad_idle_cpus_num(void)
+{
+ struct xen_platform_op op;
+
+ op.cmd = XENPF_core_parking;
+ op.u.core_parking.type = XEN_CORE_PARKING_GET;
+
+ return HYPERVISOR_dom0_op(&op)
+ ?: op.u.core_parking.idle_nums;
+}
+
+/*
+ * Query firmware how many CPUs should be idle
+ * return -1 on failure
+ */
+static int acpi_pad_pur(acpi_handle handle)
+{
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object *package;
+ int num = -1;
+
+ if (ACPI_FAILURE(acpi_evaluate_object(handle, "_PUR", NULL, &buffer)))
+ return num;
+
+ if (!buffer.length || !buffer.pointer)
+ return num;
+
+ package = buffer.pointer;
+
+ if (package->type == ACPI_TYPE_PACKAGE &&
+ package->package.count == 2 &&
+ package->package.elements[0].integer.value == 1) /* rev 1 */
+ num = package->package.elements[1].integer.value;
+
+ kfree(buffer.pointer);
+ return num;
+}
+
+/* Notify firmware how many CPUs are idle */
+static void acpi_pad_ost(acpi_handle handle, int stat,
+ uint32_t idle_nums)
+{
+ union acpi_object params[3] = {
+ {.type = ACPI_TYPE_INTEGER,},
+ {.type = ACPI_TYPE_INTEGER,},
+ {.type = ACPI_TYPE_BUFFER,},
+ };
+ struct acpi_object_list arg_list = {3, params};
+
+ params[0].integer.value = ACPI_PROCESSOR_AGGREGATOR_NOTIFY;
+ params[1].integer.value = stat;
+ params[2].buffer.length = 4;
+ params[2].buffer.pointer = (void *)&idle_nums;
+ acpi_evaluate_object(handle, "_OST", &arg_list, NULL);
+}
+
+static void acpi_pad_handle_notify(acpi_handle handle)
+{
+ int idle_nums;
+
+ mutex_lock(&xen_cpu_lock);
+ idle_nums = acpi_pad_pur(handle);
+ if (idle_nums < 0) {
+ mutex_unlock(&xen_cpu_lock);
+ return;
+ }
+
+ idle_nums = xen_acpi_pad_idle_cpus(idle_nums)
+ ?: xen_acpi_pad_idle_cpus_num();
+ if (idle_nums >= 0)
+ acpi_pad_ost(handle, 0, idle_nums);
+ mutex_unlock(&xen_cpu_lock);
+}
+
+static void acpi_pad_notify(acpi_handle handle, u32 event,
+ void *data)
+{
+ switch (event) {
+ case ACPI_PROCESSOR_AGGREGATOR_NOTIFY:
+ acpi_pad_handle_notify(handle);
+ break;
+ default:
+ pr_warn("Unsupported event [0x%x]\n", event);
+ break;
+ }
+}
+
+static int acpi_pad_add(struct acpi_device *device)
+{
+ acpi_status status;
+
+ strcpy(acpi_device_name(device), ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME);
+ strcpy(acpi_device_class(device), ACPI_PROCESSOR_AGGREGATOR_CLASS);
+
+ status = acpi_install_notify_handler(device->handle,
+ ACPI_DEVICE_NOTIFY, acpi_pad_notify, device);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ return 0;
+}
+
+static int acpi_pad_remove(struct acpi_device *device,
+ int type)
+{
+ mutex_lock(&xen_cpu_lock);
+ xen_acpi_pad_idle_cpus(0);
+ mutex_unlock(&xen_cpu_lock);
+
+ acpi_remove_notify_handler(device->handle,
+ ACPI_DEVICE_NOTIFY, acpi_pad_notify);
+ return 0;
+}
+
+static const struct acpi_device_id pad_device_ids[] = {
+ {"ACPI000C", 0},
+ {"", 0},
+};
+
+static struct acpi_driver acpi_pad_driver = {
+ .name = "processor_aggregator",
+ .class = ACPI_PROCESSOR_AGGREGATOR_CLASS,
+ .ids = pad_device_ids,
+ .ops = {
+ .add = acpi_pad_add,
+ .remove = acpi_pad_remove,
+ },
+};
+
+static int __init xen_acpi_pad_init(void)
+{
+ /* Only DOM0 is responsible for Xen acpi pad */
+ if (!xen_initial_domain())
+ return -ENODEV;
+
+ /* Only Xen4.2 or later support Xen acpi pad */
+ if (!xen_running_on_version_or_later(4, 2))
+ return -ENODEV;
+
+ return acpi_bus_register_driver(&acpi_pad_driver);
+}
+subsys_initcall(xen_acpi_pad_init);
diff --git a/drivers/xen/xen-pciback/pci_stub.c b/drivers/xen/xen-pciback/pci_stub.c
index 961d664e2d2..cd50d251998 100644
--- a/drivers/xen/xen-pciback/pci_stub.c
+++ b/drivers/xen/xen-pciback/pci_stub.c
@@ -142,7 +142,8 @@ static struct pcistub_device *pcistub_device_find(int domain, int bus,
if (psdev->dev != NULL
&& domain == pci_domain_nr(psdev->dev->bus)
&& bus == psdev->dev->bus->number
- && PCI_DEVFN(slot, func) == psdev->dev->devfn) {
+ && slot == PCI_SLOT(psdev->dev->devfn)
+ && func == PCI_FUNC(psdev->dev->devfn)) {
pcistub_device_get(psdev);
goto out;
}
@@ -191,7 +192,8 @@ struct pci_dev *pcistub_get_pci_dev_by_slot(struct xen_pcibk_device *pdev,
if (psdev->dev != NULL
&& domain == pci_domain_nr(psdev->dev->bus)
&& bus == psdev->dev->bus->number
- && PCI_DEVFN(slot, func) == psdev->dev->devfn) {
+ && slot == PCI_SLOT(psdev->dev->devfn)
+ && func == PCI_FUNC(psdev->dev->devfn)) {
found_dev = pcistub_device_get_pci_dev(pdev, psdev);
break;
}
@@ -360,7 +362,7 @@ static int __devinit pcistub_init_device(struct pci_dev *dev)
if (!dev_data->pci_saved_state)
dev_err(&dev->dev, "Could not store PCI conf saved state!\n");
else {
- dev_dbg(&dev->dev, "reseting (FLR, D3, etc) the device\n");
+ dev_dbg(&dev->dev, "resetting (FLR, D3, etc) the device\n");
__pci_reset_function_locked(dev);
pci_restore_state(dev);
}
@@ -897,42 +899,35 @@ static struct pci_driver xen_pcibk_pci_driver = {
static inline int str_to_slot(const char *buf, int *domain, int *bus,
int *slot, int *func)
{
- int err;
- char wc = '*';
+ int parsed = 0;
- err = sscanf(buf, " %x:%x:%x.%x", domain, bus, slot, func);
- switch (err) {
+ switch (sscanf(buf, " %x:%x:%x.%x %n", domain, bus, slot, func,
+ &parsed)) {
case 3:
*func = -1;
- err = sscanf(buf, " %x:%x:%x.%c", domain, bus, slot, &wc);
+ sscanf(buf, " %x:%x:%x.* %n", domain, bus, slot, &parsed);
break;
case 2:
*slot = *func = -1;
- err = sscanf(buf, " %x:%x:*.%c", domain, bus, &wc);
- if (err >= 2)
- ++err;
+ sscanf(buf, " %x:%x:*.* %n", domain, bus, &parsed);
break;
}
- if (err == 4 && wc == '*')
+ if (parsed && !buf[parsed])
return 0;
- else if (err < 0)
- return -EINVAL;
/* try again without domain */
*domain = 0;
- wc = '*';
- err = sscanf(buf, " %x:%x.%x", bus, slot, func);
- switch (err) {
+ switch (sscanf(buf, " %x:%x.%x %n", bus, slot, func, &parsed)) {
case 2:
*func = -1;
- err = sscanf(buf, " %x:%x.%c", bus, slot, &wc);
+ sscanf(buf, " %x:%x.* %n", bus, slot, &parsed);
break;
case 1:
*slot = *func = -1;
- err = sscanf(buf, " %x:*.%c", bus, &wc) + 1;
+ sscanf(buf, " %x:*.* %n", bus, &parsed);
break;
}
- if (err == 3 && wc == '*')
+ if (parsed && !buf[parsed])
return 0;
return -EINVAL;
@@ -941,13 +936,20 @@ static inline int str_to_slot(const char *buf, int *domain, int *bus,
static inline int str_to_quirk(const char *buf, int *domain, int *bus, int
*slot, int *func, int *reg, int *size, int *mask)
{
- int err;
+ int parsed = 0;
- err =
- sscanf(buf, " %04x:%02x:%02x.%d-%08x:%1x:%08x", domain, bus, slot,
- func, reg, size, mask);
- if (err == 7)
+ sscanf(buf, " %x:%x:%x.%x-%x:%x:%x %n", domain, bus, slot, func,
+ reg, size, mask, &parsed);
+ if (parsed && !buf[parsed])
return 0;
+
+ /* try again without domain */
+ *domain = 0;
+ sscanf(buf, " %x:%x.%x-%x:%x:%x %n", bus, slot, func, reg, size,
+ mask, &parsed);
+ if (parsed && !buf[parsed])
+ return 0;
+
return -EINVAL;
}
@@ -955,7 +957,7 @@ static int pcistub_device_id_add(int domain, int bus, int slot, int func)
{
struct pcistub_device_id *pci_dev_id;
unsigned long flags;
- int rc = 0;
+ int rc = 0, devfn = PCI_DEVFN(slot, func);
if (slot < 0) {
for (slot = 0; !rc && slot < 32; ++slot)
@@ -969,13 +971,24 @@ static int pcistub_device_id_add(int domain, int bus, int slot, int func)
return rc;
}
+ if ((
+#if !defined(MODULE) /* pci_domains_supported is not being exported */ \
+ || !defined(CONFIG_PCI_DOMAINS)
+ !pci_domains_supported ? domain :
+#endif
+ domain < 0 || domain > 0xffff)
+ || bus < 0 || bus > 0xff
+ || PCI_SLOT(devfn) != slot
+ || PCI_FUNC(devfn) != func)
+ return -EINVAL;
+
pci_dev_id = kmalloc(sizeof(*pci_dev_id), GFP_KERNEL);
if (!pci_dev_id)
return -ENOMEM;
pci_dev_id->domain = domain;
pci_dev_id->bus = bus;
- pci_dev_id->devfn = PCI_DEVFN(slot, func);
+ pci_dev_id->devfn = devfn;
pr_debug(DRV_NAME ": wants to seize %04x:%02x:%02x.%d\n",
domain, bus, slot, func);
@@ -1016,14 +1029,18 @@ static int pcistub_device_id_remove(int domain, int bus, int slot, int func)
return err;
}
-static int pcistub_reg_add(int domain, int bus, int slot, int func, int reg,
- int size, int mask)
+static int pcistub_reg_add(int domain, int bus, int slot, int func,
+ unsigned int reg, unsigned int size,
+ unsigned int mask)
{
int err = 0;
struct pcistub_device *psdev;
struct pci_dev *dev;
struct config_field *field;
+ if (reg > 0xfff || (size < 4 && (mask >> (size * 8))))
+ return -EINVAL;
+
psdev = pcistub_device_find(domain, bus, slot, func);
if (!psdev) {
err = -ENODEV;
@@ -1254,13 +1271,11 @@ static ssize_t permissive_add(struct device_driver *drv, const char *buf,
int err;
struct pcistub_device *psdev;
struct xen_pcibk_dev_data *dev_data;
+
err = str_to_slot(buf, &domain, &bus, &slot, &func);
if (err)
goto out;
- if (slot < 0 || func < 0) {
- err = -EINVAL;
- goto out;
- }
+
psdev = pcistub_device_find(domain, bus, slot, func);
if (!psdev) {
err = -ENODEV;
@@ -1339,8 +1354,6 @@ static int __init pcistub_init(void)
if (pci_devs_to_hide && *pci_devs_to_hide) {
do {
- char wc = '*';
-
parsed = 0;
err = sscanf(pci_devs_to_hide + pos,
@@ -1349,51 +1362,48 @@ static int __init pcistub_init(void)
switch (err) {
case 3:
func = -1;
- err = sscanf(pci_devs_to_hide + pos,
- " (%x:%x:%x.%c) %n",
- &domain, &bus, &slot, &wc,
- &parsed);
+ sscanf(pci_devs_to_hide + pos,
+ " (%x:%x:%x.*) %n",
+ &domain, &bus, &slot, &parsed);
break;
case 2:
slot = func = -1;
- err = sscanf(pci_devs_to_hide + pos,
- " (%x:%x:*.%c) %n",
- &domain, &bus, &wc, &parsed) + 1;
+ sscanf(pci_devs_to_hide + pos,
+ " (%x:%x:*.*) %n",
+ &domain, &bus, &parsed);
break;
}
- if (err != 4 || wc != '*') {
+ if (!parsed) {
domain = 0;
- wc = '*';
err = sscanf(pci_devs_to_hide + pos,
" (%x:%x.%x) %n",
&bus, &slot, &func, &parsed);
switch (err) {
case 2:
func = -1;
- err = sscanf(pci_devs_to_hide + pos,
- " (%x:%x.%c) %n",
- &bus, &slot, &wc,
- &parsed);
+ sscanf(pci_devs_to_hide + pos,
+ " (%x:%x.*) %n",
+ &bus, &slot, &parsed);
break;
case 1:
slot = func = -1;
- err = sscanf(pci_devs_to_hide + pos,
- " (%x:*.%c) %n",
- &bus, &wc, &parsed) + 1;
+ sscanf(pci_devs_to_hide + pos,
+ " (%x:*.*) %n",
+ &bus, &parsed);
break;
}
- if (err != 3 || wc != '*')
- goto parse_error;
}
+ if (parsed <= 0)
+ goto parse_error;
+
err = pcistub_device_id_add(domain, bus, slot, func);
if (err)
goto out;
- /* if parsed<=0, we've reached the end of the string */
pos += parsed;
- } while (parsed > 0 && pci_devs_to_hide[pos]);
+ } while (pci_devs_to_hide[pos]);
}
/* If we're the first PCI Device Driver to register, we're the
diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c
index acedeabe589..88e677b0de7 100644
--- a/drivers/xen/xenbus/xenbus_xs.c
+++ b/drivers/xen/xenbus/xenbus_xs.c
@@ -48,7 +48,6 @@
#include <xen/xenbus.h>
#include <xen/xen.h>
#include "xenbus_comms.h"
-#include <asm/xen/hypervisor.h>
struct xs_stored_msg {
struct list_head list;